Compare commits

..

94 Commits

Author SHA1 Message Date
bol-van
d566f48f94 blockcheck: display ip version every test 2025-05-24 13:35:11 +03:00
bol-van
3513cbb65e winws: fix trash flood warning message 2025-05-24 10:51:01 +03:00
bol-van
20f91cb7ab doc works 2025-05-24 09:58:41 +03:00
bol-van
4becc07572 update changes 2025-05-22 11:01:34 +03:00
bol-van
a39c18737b nfqws: - disables autottl 2025-05-22 11:00:11 +03:00
bol-van
ed7b743fe2 update changes.txt 2025-05-22 10:44:26 +03:00
bol-van
d3b0b3e0b1 nfqws,tpws: display android in version string 2025-05-22 10:19:30 +03:00
bol-van
ba040769a7 doc works 2025-05-21 17:40:46 +03:00
bol-van
0ced50e393 doc works 2025-05-21 17:39:50 +03:00
bol-van
f3abd6815a doc works 2025-05-20 19:07:42 +03:00
bol-van
4572799750 doc works 2025-05-20 19:06:48 +03:00
bol-van
696167509a doc works 2025-05-20 19:03:47 +03:00
bol-van
2374df6d74 doc works 2025-05-20 19:02:22 +03:00
bol-van
ab06d6b640 doc works 2025-05-20 19:01:14 +03:00
bol-van
60efab1cc6 doc works 2025-05-20 18:57:46 +03:00
bol-van
71aebbb4d3 nfqws: allow zero autottl 2025-05-20 18:06:56 +03:00
bol-van
c993f117a2 blockcheck: test http3 google fake 2025-05-20 12:05:43 +03:00
bol-van
b2f0c46388 update changes.txt 2025-05-20 11:32:58 +03:00
bol-van
2b095f863f blockcheck: report test function and domain every test 2025-05-20 11:32:08 +03:00
bol-van
a141dff374 nfqws: warn dup trash flood 2025-05-20 10:30:37 +03:00
bol-van
b34bfda8b5 blockcheck: tls multi fake test 2025-05-10 18:12:51 +03:00
bol-van
c1046a20db nfqws,tpws: sec_harden after daemonize 2025-05-10 16:57:04 +03:00
bol-van
24b93cca7e blockcheck: some dup and orig-autottl mods 2025-05-10 15:58:36 +03:00
bol-van
4f0fdb24f2 nfqws,tpws: support multiple gids in --uid 2025-05-10 11:11:56 +03:00
bol-van
6d52b49b98 nfqws: do not reconstruct synack-split in syn mode 2025-05-10 09:41:26 +03:00
bol-van
4b632313e2 nfqws: improve --synack-split 2025-05-09 20:57:14 +03:00
bol-van
22f3ecaec1 doc works 2025-05-09 20:41:02 +03:00
bol-van
2a23bc99f6 nfqws: --synack-split select syn or ack first 2025-05-09 20:20:55 +03:00
bol-van
8a1d7c7abd doc works 2025-05-09 17:21:08 +03:00
bol-van
ba712f308d doc works 2025-05-09 17:19:46 +03:00
bol-van
9ace0328ad doc works 2025-05-09 17:18:44 +03:00
bol-van
5c6f79799a doc works 2025-05-09 16:45:00 +03:00
bol-van
a84d015b1e doc works 2025-05-09 16:44:08 +03:00
bol-van
2d90a28dbc Revert "doc works"
This reverts commit 3c77bab002cd8709138bbfbccfea866686ad4b23.
2025-05-09 16:42:17 +03:00
bol-van
3c77bab002 doc works 2025-05-09 16:22:21 +03:00
bol-van
8f27725d6a nfqws: --synack-split 2025-05-09 16:20:28 +03:00
bol-van
729ded0c61 nfqws: conntrack workaround TTL=1 2025-05-09 11:17:21 +03:00
bol-van
691a501b0d nfqws,tpws: do most checks before daemonize 2025-05-09 10:32:19 +03:00
bol-van
e62fb2f0f4 nfqws: simplify conntrack workaround 2025-05-08 19:46:35 +03:00
bol-van
603265dac2 nfqws: do not realloc hostname in ipcache if it's the same 2025-05-08 13:45:28 +03:00
bol-van
ed0bb4c106 tpws: ipcache socks hostname 2025-05-08 13:42:24 +03:00
bol-van
6eae2b0e71 doc works 2025-05-08 12:23:51 +03:00
bol-van
c59771f744 doc works 2025-05-08 12:20:55 +03:00
bol-van
dd23d6f3f4 doc works 2025-05-08 12:08:38 +03:00
bol-van
92dc012f08 doc works 2025-05-08 12:01:04 +03:00
bol-van
9bcefde37a doc works 2025-05-08 11:45:50 +03:00
bol-van
d2f7a53927 nfqws: --ctrack-disable 2025-05-08 09:00:20 +03:00
bol-van
f1dd351854 nfqws: --ctrack-disable 2025-05-08 08:54:05 +03:00
bol-van
5c63cb43e7 readme: update vps section 2025-05-06 09:58:03 +03:00
bol-van
7f24f82002 readme: update vps section 2025-05-06 09:51:53 +03:00
bol-van
b0c7af789a init: remove autohostlist touch, not needed anymore 2025-05-04 22:02:36 +03:00
bol-van
a426ea6dad nfqws,tpws: check list files accessibility after all params are parsed 2025-05-04 22:01:00 +03:00
bol-van
bda4226162 init: create autohostlist file if not exists 2025-05-04 21:47:26 +03:00
bol-van
dc1dc5c876 drop time exceeded icmp for nfqws-related connections 2025-05-04 18:21:43 +03:00
bol-van
3ca682e25a drop time exceeded icmp for nfqws-related connections 2025-05-04 18:15:33 +03:00
bol-van
9629ce5cb7 tpws: ipcache 2025-05-04 10:57:23 +03:00
bol-van
c626d88f54 nfqws: minor changes 2025-05-04 10:42:27 +03:00
bol-van
c91ddf4a54 nfqws: do not use interface name for ip->hostname 2025-05-03 22:06:14 +03:00
bol-van
6f1286b5b9 nfqws: fix mem leak 2025-05-03 21:59:43 +03:00
bol-van
c96bc62d3b nfqws: ip->hostname cache 2025-05-03 20:25:53 +03:00
bol-van
8432388b37 nfqws: debug autottl cache lifetime 2025-05-03 15:15:26 +03:00
bol-van
7efa83e61e init.d: remove --ipset prohibition 2025-05-03 13:03:33 +03:00
bol-van
abe91a4bfa nfqws: ipcache destroy in cleanup_params 2025-05-03 12:50:53 +03:00
bol-van
43173e6396 nfqws: return autottl path len check 2025-05-03 12:28:49 +03:00
bol-van
5cc888cd2c nfqws: autottl cache, --dup-autottl, --orig-autottl 2025-05-03 12:11:16 +03:00
bol-van
5b625fa709 update nftables.txt,iptables.txt 2025-05-03 10:54:59 +03:00
bol-van
0a8135b2de update config defaults 2025-05-03 10:52:20 +03:00
bol-van
d21175b4a3 nfqws: prepare for +- autottl 2025-04-29 17:45:34 +03:00
bol-van
68a538daed nfqws: conntrack: do not reset entry on dup SA 2025-04-29 16:31:37 +03:00
bol-van
d2c9ff50cd nfqws: copy DF ip flag 2025-04-29 13:36:05 +03:00
bol-van
50539d6cbf nfqws: windows fixes for recent changes 2025-04-29 12:25:23 +03:00
bol-van
8b5dfcfae1 nfqws: dup,orig_mod 2025-04-26 19:48:35 +03:00
bol-van
ccc60b5f07 init.d: fix missing - sign in 50-nfqws-ipset 2025-04-22 19:28:14 +03:00
bol-van
7f94f42b1d init.d: fix local var name 2025-04-22 19:07:26 +03:00
bol-van
1c1f259b39 init.d: improve nfqws ipset example 2025-04-22 19:03:18 +03:00
bol-van
6ef6c8ee5a nfqws: do not use overlapping memcmp 2025-04-21 15:54:06 +03:00
bol-van
581badfb73 nfqws: --dpi-desync-fake-tls=! 2025-04-21 14:52:51 +03:00
bol-van
8fce75daa4 hardware offload: be closer to fw4 in interface names 2025-04-20 11:26:07 +03:00
bol-van
c1e2e56576 hardware offload: be closer to fw4 in interface names 2025-04-20 11:24:55 +03:00
bol-van
e16ec69922 nfqws: fix unitialized use of host buffer (udp) 2025-04-20 08:49:50 +03:00
bol-van
63256a142f nfqws: fix unitialized use of host buffer 2025-04-19 19:59:42 +03:00
bol-van
4a9a8bd48e typo 2025-04-19 19:41:40 +03:00
bol-van
b996abd5ce nfqws,tpws: use tls record length in TLSDebug 2025-04-14 12:18:07 +03:00
bol-van
12461de3b0 nfqws,tpws: optimize tls debug, show quic 2025-04-14 11:21:16 +03:00
bol-van
7dab497b57 nfqws,tpws: optimize tls debug, show quic 2025-04-14 11:20:20 +03:00
bol-van
41dbba1c4c nfqws,tpws: debug alpn and ech 2025-04-13 18:07:46 +03:00
bol-van
d19f6c19a4 nfqws,tpws: debug tls version 2025-04-13 15:27:50 +03:00
bol-van
b12b1a5a17 winws build fix 2025-04-08 17:33:14 +03:00
bol-van
8022e2576d nfqws: BSD/clang build fix 2025-04-08 17:23:15 +03:00
bol-van
f4ea264ba9 minor var spelling fix 2025-04-08 17:02:33 +03:00
bol-van
061acb27e4
Merge pull request #1334 from tie/master
use enum for option indices
2025-04-08 17:00:53 +03:00
Ivan Trubach
8eb830d304 use enum for option indices 2025-04-08 16:56:17 +03:00
bol-van
2fb93c6add blockcheck: test tpws exists 2025-04-08 16:17:40 +03:00
bol-van
ad5c246629 blockcheck: test whether tpws supports fix-seg 2025-04-08 16:15:06 +03:00
47 changed files with 3710 additions and 1119 deletions

View File

@ -341,6 +341,12 @@ netcat_test()
}
}
tpws_can_fix_seg()
{
# fix-seg requires kernel 4.6+
"$TPWS" --port 1 --dry-run --fix-seg >/dev/null 2>/dev/null
}
check_system()
{
echo \* checking system
@ -355,7 +361,14 @@ check_system()
Linux)
PKTWS="$NFQWS"
PKTWSD=nfqws
FIX_SEG='--fix-seg'
if [ -x "$TPWS" ] ; then
if tpws_can_fix_seg ; then
echo tpws supports --fix-seg on this system
FIX_SEG='--fix-seg'
else
echo tpws does not support --fix-seg on this system
fi
fi
linux_fwtype
[ "$FWTYPE" = iptables -o "$FWTYPE" = nftables ] || {
echo firewall type $FWTYPE not supported in $UNAME
@ -722,6 +735,11 @@ ipt_aux_scheme()
# to avoid possible INVALID state drop
[ "$2" = tcp ] && IPT_ADD_DEL $1 INPUT -p $2 --sport $3 ! --syn -j ACCEPT
local icmp_filter="-p icmp -m icmp --icmp-type"
[ "$IPV" = 6 ] && icmp_filter="-p icmpv6 -m icmp6 --icmpv6-type"
IPT_ADD_DEL $1 INPUT $icmp_filter time-exceeded -m connmark --mark $DESYNC_MARK/$DESYNC_MARK -j DROP
# for strategies with incoming packets involved (autottl)
IPT_ADD_DEL $1 OUTPUT -p $2 --dport $3 -m conntrack --ctstate INVALID -j ACCEPT
if [ "$IPV" = 6 -a -n "$IP6_DEFRAG_DISABLE" ]; then
@ -753,6 +771,7 @@ ipt_scheme()
$IPTABLES -t mangle -A blockcheck_output -p $1 ! --dport $2 -j RETURN
for ip in $3; do
$IPTABLES -t mangle -A blockcheck_output -d $ip -j CONNMARK --or-mark $DESYNC_MARK
$IPTABLES -t mangle -A blockcheck_output -d $ip -j NFQUEUE --queue-num $QNUM
done
@ -770,12 +789,20 @@ nft_scheme()
nft add table inet $NFT_TABLE
nft "add chain inet $NFT_TABLE postnat { type filter hook output priority 102; }"
nft "add rule inet $NFT_TABLE postnat meta nfproto ipv${IPV} $1 dport $2 mark and $DESYNC_MARK != $DESYNC_MARK ip${ipver} daddr {$iplist} queue num $QNUM"
nft "add rule inet $NFT_TABLE postnat meta nfproto ipv${IPV} $1 dport $2 mark and $DESYNC_MARK == 0 ip${ipver} daddr {$iplist} ct mark set ct mark or $DESYNC_MARK queue num $QNUM"
# for strategies with incoming packets involved (autottl)
nft "add chain inet $NFT_TABLE prenat { type filter hook prerouting priority -102; }"
# enable everything generated by nfqws (works only in OUTPUT, not in FORWARD)
nft "add chain inet $NFT_TABLE predefrag { type filter hook output priority -402; }"
nft "add rule inet $NFT_TABLE predefrag meta nfproto ipv${IPV} mark and $DESYNC_MARK !=0 notrack"
[ "$IPV" = 4 ] && {
nft "add rule inet $NFT_TABLE prenat icmp type time-exceeded ct mark and $DESYNC_MARK != 0 drop"
nft "add rule inet $NFT_TABLE prenat icmp type time-exceeded ct state invalid drop"
}
[ "$IPV" = 6 ] && {
nft "add rule inet $NFT_TABLE prenat icmpv6 type time-exceeded ct mark and $DESYNC_MARK != 0 drop"
nft "add rule inet $NFT_TABLE prenat icmpv6 type time-exceeded ct state invalid drop"
}
}
pktws_ipt_prepare()
@ -1015,6 +1042,7 @@ curl_test()
}
ws_curl_test()
{
# $1 - ws start function
# $2 - test function
# $3 - domain
@ -1034,7 +1062,7 @@ tpws_curl_test()
# $1 - test function
# $2 - domain
# $3,$4,$5, ... - tpws params
echo - checking tpws $3 $4 $5 $6 $7 $8 $9${TPWS_EXTRA:+ $TPWS_EXTRA}${TPWS_EXTRA_1:+ "$TPWS_EXTRA_1"}${TPWS_EXTRA_2:+ "$TPWS_EXTRA_2"}${TPWS_EXTRA_3:+ "$TPWS_EXTRA_3"}${TPWS_EXTRA_4:+ "$TPWS_EXTRA_4"}${TPWS_EXTRA_5:+ "$TPWS_EXTRA_5"}${TPWS_EXTRA_6:+ "$TPWS_EXTRA_6"}${TPWS_EXTRA_7:+ "$TPWS_EXTRA_7"}${TPWS_EXTRA_8:+ "$TPWS_EXTRA_8"}${TPWS_EXTRA_9:+ "$TPWS_EXTRA_9"}
echo - $1 ipv$IPV $2 : tpws $3 $4 $5 $6 $7 $8 $9${TPWS_EXTRA:+ $TPWS_EXTRA}${TPWS_EXTRA_1:+ "$TPWS_EXTRA_1"}${TPWS_EXTRA_2:+ "$TPWS_EXTRA_2"}${TPWS_EXTRA_3:+ "$TPWS_EXTRA_3"}${TPWS_EXTRA_4:+ "$TPWS_EXTRA_4"}${TPWS_EXTRA_5:+ "$TPWS_EXTRA_5"}${TPWS_EXTRA_6:+ "$TPWS_EXTRA_6"}${TPWS_EXTRA_7:+ "$TPWS_EXTRA_7"}${TPWS_EXTRA_8:+ "$TPWS_EXTRA_8"}${TPWS_EXTRA_9:+ "$TPWS_EXTRA_9"}
local ALL_PROXY="socks5://127.0.0.1:$SOCKS_PORT"
ws_curl_test tpws_start "$@"${TPWS_EXTRA:+ $TPWS_EXTRA}${TPWS_EXTRA_1:+ "$TPWS_EXTRA_1"}${TPWS_EXTRA_2:+ "$TPWS_EXTRA_2"}${TPWS_EXTRA_3:+ "$TPWS_EXTRA_3"}${TPWS_EXTRA_4:+ "$TPWS_EXTRA_4"}${TPWS_EXTRA_5:+ "$TPWS_EXTRA_5"}${TPWS_EXTRA_6:+ "$TPWS_EXTRA_6"}${TPWS_EXTRA_7:+ "$TPWS_EXTRA_7"}${TPWS_EXTRA_8:+ "$TPWS_EXTRA_8"}${TPWS_EXTRA_9:+ "$TPWS_EXTRA_9"}
local testf=$1 dom=$2 strategy code=$?
@ -1051,11 +1079,14 @@ pktws_curl_test()
# $1 - test function
# $2 - domain
# $3,$4,$5, ... - nfqws/dvtws params
echo - checking $PKTWSD ${WF:+$WF }$3 $4 $5 $6 $7 $8 $9${PKTWS_EXTRA:+ $PKTWS_EXTRA}${PKTWS_EXTRA_1:+ "$PKTWS_EXTRA_1"}${PKTWS_EXTRA_2:+ "$PKTWS_EXTRA_2"}${PKTWS_EXTRA_3:+ "$PKTWS_EXTRA_3"}${PKTWS_EXTRA_4:+ "$PKTWS_EXTRA_4"}${PKTWS_EXTRA_5:+ "$PKTWS_EXTRA_5"}${PKTWS_EXTRA_6:+ "$PKTWS_EXTRA_6"}${PKTWS_EXTRA_7:+ "$PKTWS_EXTRA_7"}${PKTWS_EXTRA_8:+ "$PKTWS_EXTRA_8"}${PKTWS_EXTRA_9:+ "$PKTWS_EXTRA_9"}
ws_curl_test pktws_start "$@"${PKTWS_EXTRA:+ $PKTWS_EXTRA}${PKTWS_EXTRA_1:+ "$PKTWS_EXTRA_1"}${PKTWS_EXTRA_2:+ "$PKTWS_EXTRA_2"}${PKTWS_EXTRA_3:+ "$PKTWS_EXTRA_3"}${PKTWS_EXTRA_4:+ "$PKTWS_EXTRA_4"}${PKTWS_EXTRA_5:+ "$PKTWS_EXTRA_5"}${PKTWS_EXTRA_6:+ "$PKTWS_EXTRA_6"}${PKTWS_EXTRA_7:+ "$PKTWS_EXTRA_7"}${PKTWS_EXTRA_8:+ "$PKTWS_EXTRA_8"}${PKTWS_EXTRA_9:+ "$PKTWS_EXTRA_9"}
local testf=$1 dom=$2 strategy code=$?
local testf=$1 dom=$2 strategy code
shift; shift;
echo - $testf ipv$IPV $dom : $PKTWSD ${WF:+$WF }${PKTWS_EXTRA_PRE:+$PKTWS_EXTRA_PRE }${PKTWS_EXTRA_PRE_1:+"$PKTWS_EXTRA_PRE_1" }${PKTWS_EXTRA_PRE_2:+"$PKTWS_EXTRA_PRE_2" }${PKTWS_EXTRA_PRE_3:+"$PKTWS_EXTRA_PRE_3" }${PKTWS_EXTRA_PRE_4:+"$PKTWS_EXTRA_PRE_4" }${PKTWS_EXTRA_PRE_5:+"$PKTWS_EXTRA_PRE_5" }${PKTWS_EXTRA_PRE_6:+"$PKTWS_EXTRA_PRE_6" }${PKTWS_EXTRA_PRE_7:+"$PKTWS_EXTRA_PRE_7" }${PKTWS_EXTRA_PRE_8:+"$PKTWS_EXTRA_PRE_8" }${PKTWS_EXTRA_PRE_9:+"$PKTWS_EXTRA_PRE_9" }$@${PKTWS_EXTRA:+ $PKTWS_EXTRA}${PKTWS_EXTRA_1:+ "$PKTWS_EXTRA_1"}${PKTWS_EXTRA_2:+ "$PKTWS_EXTRA_2"}${PKTWS_EXTRA_3:+ "$PKTWS_EXTRA_3"}${PKTWS_EXTRA_4:+ "$PKTWS_EXTRA_4"}${PKTWS_EXTRA_5:+ "$PKTWS_EXTRA_5"}${PKTWS_EXTRA_6:+ "$PKTWS_EXTRA_6"}${PKTWS_EXTRA_7:+ "$PKTWS_EXTRA_7"}${PKTWS_EXTRA_8:+ "$PKTWS_EXTRA_8"}${PKTWS_EXTRA_9:+ "$PKTWS_EXTRA_9"}
ws_curl_test pktws_start $testf $dom ${PKTWS_EXTRA_PRE:+$PKTWS_EXTRA_PRE }${PKTWS_EXTRA_PRE_1:+"$PKTWS_EXTRA_PRE_1" }${PKTWS_EXTRA_PRE_2:+"$PKTWS_EXTRA_PRE_2" }${PKTWS_EXTRA_PRE_3:+"$PKTWS_EXTRA_PRE_3" }${PKTWS_EXTRA_PRE_4:+"$PKTWS_EXTRA_PRE_4" }${PKTWS_EXTRA_PRE_5:+"$PKTWS_EXTRA_PRE_5" }${PKTWS_EXTRA_PRE_6:+"$PKTWS_EXTRA_PRE_6" }${PKTWS_EXTRA_PRE_7:+"$PKTWS_EXTRA_PRE_7" }${PKTWS_EXTRA_PRE_8:+"$PKTWS_EXTRA_PRE_8" }${PKTWS_EXTRA_PRE_9:+"$PKTWS_EXTRA_PRE_9" }"$@"${PKTWS_EXTRA:+ $PKTWS_EXTRA}${PKTWS_EXTRA_1:+ "$PKTWS_EXTRA_1"}${PKTWS_EXTRA_2:+ "$PKTWS_EXTRA_2"}${PKTWS_EXTRA_3:+ "$PKTWS_EXTRA_3"}${PKTWS_EXTRA_4:+ "$PKTWS_EXTRA_4"}${PKTWS_EXTRA_5:+ "$PKTWS_EXTRA_5"}${PKTWS_EXTRA_6:+ "$PKTWS_EXTRA_6"}${PKTWS_EXTRA_7:+ "$PKTWS_EXTRA_7"}${PKTWS_EXTRA_8:+ "$PKTWS_EXTRA_8"}${PKTWS_EXTRA_9:+ "$PKTWS_EXTRA_9"}
code=$?
[ "$code" = 0 ] && {
shift; shift;
strategy="$@"
strategy_append_extra_pktws
report_append "ipv${IPV} $dom $testf : $PKTWSD ${WF:+$WF }$strategy"
@ -1065,11 +1096,11 @@ pktws_curl_test()
strategy_append_extra_pktws()
{
strategy="${strategy:+$strategy${PKTWS_EXTRA:+ $PKTWS_EXTRA}${PKTWS_EXTRA_1:+ "$PKTWS_EXTRA_1"}${PKTWS_EXTRA_2:+ "$PKTWS_EXTRA_2"}${PKTWS_EXTRA_3:+ "$PKTWS_EXTRA_3"}${PKTWS_EXTRA_4:+ "$PKTWS_EXTRA_4"}${PKTWS_EXTRA_5:+ "$PKTWS_EXTRA_5"}${PKTWS_EXTRA_6:+ "$PKTWS_EXTRA_6"}${PKTWS_EXTRA_7:+ "$PKTWS_EXTRA_7"}${PKTWS_EXTRA_8:+ "$PKTWS_EXTRA_8"}${PKTWS_EXTRA_9:+ "$PKTWS_EXTRA_9"}}"
strategy="${strategy:+${PKTWS_EXTRA_PRE:+$PKTWS_EXTRA_PRE }${PKTWS_EXTRA_PRE_1:+"$PKTWS_EXTRA_PRE_1" }${PKTWS_EXTRA_PRE_2:+"$PKTWS_EXTRA_PRE_2" }${PKTWS_EXTRA_PRE_3:+"$PKTWS_EXTRA_PRE_3" }${PKTWS_EXTRA_PRE_4:+"$PKTWS_EXTRA_PRE_4" }${PKTWS_EXTRA_PRE_5:+"$PKTWS_EXTRA_PRE_5" }${PKTWS_EXTRA_PRE_6:+"$PKTWS_EXTRA_PRE_6" }${PKTWS_EXTRA_PRE_7:+"$PKTWS_EXTRA_PRE_7" }${PKTWS_EXTRA_PRE_8:+"$PKTWS_EXTRA_PRE_8" }${PKTWS_EXTRA_PRE_9:+"$PKTWS_EXTRA_PRE_9" }$strategy${PKTWS_EXTRA:+ $PKTWS_EXTRA}${PKTWS_EXTRA_1:+ "$PKTWS_EXTRA_1"}${PKTWS_EXTRA_2:+ "$PKTWS_EXTRA_2"}${PKTWS_EXTRA_3:+ "$PKTWS_EXTRA_3"}${PKTWS_EXTRA_4:+ "$PKTWS_EXTRA_4"}${PKTWS_EXTRA_5:+ "$PKTWS_EXTRA_5"}${PKTWS_EXTRA_6:+ "$PKTWS_EXTRA_6"}${PKTWS_EXTRA_7:+ "$PKTWS_EXTRA_7"}${PKTWS_EXTRA_8:+ "$PKTWS_EXTRA_8"}${PKTWS_EXTRA_9:+ "$PKTWS_EXTRA_9"}}"
}
strategy_append_extra_tpws()
{
strategy="${strategy:+$strategy${TPWS_EXTRA:+ $TPWS_EXTRA}${TPWS_EXTRA_1:+ "$TPWS_EXTRA_1"}${TPWS_EXTRA_2:+ "$TPWS_EXTRA_2"}${TPWS_EXTRA_3:+ "$TPWS_EXTRA_3"}${TPWS_EXTRA_4:+ "$TPWS_EXTRA_4"}${TPWS_EXTRA_5:+ "$TPWS_EXTRA_5"}${TPWS_EXTRA_6:+ "$TPWS_EXTRA_6"}${TPWS_EXTRA_7:+ "$TPWS_EXTRA_7"}${TPWS_EXTRA_8:+ "$TPWS_EXTRA_8"}${TPWS_EXTRA_9:+ "$TPWS_EXTRA_9"}}"
strategy="${strategy:+${PKTWS_EXTRA_PRE:+$PKTWS_EXTRA_PRE }${PKTWS_EXTRA_PRE_1:+"$PKTWS_EXTRA_PRE_1" }${PKTWS_EXTRA_PRE_2:+"$PKTWS_EXTRA_PRE_2" }${PKTWS_EXTRA_PRE_3:+"$PKTWS_EXTRA_PRE_3" }${PKTWS_EXTRA_PRE_4:+"$PKTWS_EXTRA_PRE_4" }${PKTWS_EXTRA_PRE_5:+"$PKTWS_EXTRA_PRE_5" }${PKTWS_EXTRA_PRE_6:+"$PKTWS_EXTRA_PRE_6" }${PKTWS_EXTRA_PRE_7:+"$PKTWS_EXTRA_PRE_7" }${PKTWS_EXTRA_PRE_8:+"$PKTWS_EXTRA_PRE_8" }${PKTWS_EXTRA_PRE_9:+"$PKTWS_EXTRA_PRE_9" }$strategy${TPWS_EXTRA:+ $TPWS_EXTRA}${TPWS_EXTRA_1:+ "$TPWS_EXTRA_1"}${TPWS_EXTRA_2:+ "$TPWS_EXTRA_2"}${TPWS_EXTRA_3:+ "$TPWS_EXTRA_3"}${TPWS_EXTRA_4:+ "$TPWS_EXTRA_4"}${TPWS_EXTRA_5:+ "$TPWS_EXTRA_5"}${TPWS_EXTRA_6:+ "$TPWS_EXTRA_6"}${TPWS_EXTRA_7:+ "$TPWS_EXTRA_7"}${TPWS_EXTRA_8:+ "$TPWS_EXTRA_8"}${TPWS_EXTRA_9:+ "$TPWS_EXTRA_9"}}"
}
xxxws_curl_test_update()
@ -1162,15 +1193,19 @@ pktws_curl_test_update_vary()
# $4 - desync mode
# $5,$6,... - strategy
local testf=$1 sec=$2 domain=$3 desync=$4 proto zerofake= tlsmod= splits= pos fake ret=1
local testf=$1 sec=$2 domain=$3 desync=$4 proto splits= pos fake ret=1
local fake1=- fake2=- fake3=-
shift; shift; shift; shift
proto=http
[ "$sec" = 0 ] || proto=tls
test_has_fake $desync && {
zerofake="--dpi-desync-fake-$proto=0x00000000"
[ "$sec" = 0 ] || tlsmod="--dpi-desync-fake-tls-mod=rnd,dupsid,rndsni,padencap"
fake1="--dpi-desync-fake-$proto=0x00000000"
[ "$sec" = 0 ] || {
fake2="--dpi-desync-fake-tls=0x00000000 --dpi-desync-fake-tls=! --dpi-desync-fake-tls-mod=rnd,rndsni,dupsid"
fake3="--dpi-desync-fake-tls-mod=rnd,dupsid,rndsni,padencap"
}
}
if test_has_fakedsplit $desync ; then
splits="method+2 midsld"
@ -1179,7 +1214,8 @@ pktws_curl_test_update_vary()
splits="method+2 midsld"
[ "$sec" = 0 ] || splits="1 midsld 1,midsld"
fi
for fake in '' $zerofake $tlsmod ; do
for fake in '' "$fake1" "$fake2" "$fake3" ; do
[ "$fake" = "-" ] && continue
if [ -n "$splits" ]; then
for pos in $splits ; do
pktws_curl_test_update $testf $domain --dpi-desync=$desync "$@" --dpi-desync-split-pos=$pos $fake && {
@ -1204,14 +1240,14 @@ pktws_check_domain_http_bypass_()
# $2 - encrypted test : 0 = plain, 1 - encrypted with server reply risk, 2 - encrypted without server reply risk
# $3 - domain
local ok ttls s f f2 e desync pos fooling frag sec="$2" delta splits
local ok ttls s f f2 e desync pos fooling frag sec="$2" delta orig splits
local need_split need_disorder need_fakedsplit need_fakeddisorder need_fake need_wssize
local splits_http='method+2 midsld method+2,midsld'
local splits_tls='2 1 sniext+1 sniext+4 host+1 midsld 1,midsld 1,sniext+1,host+1,midsld-2,midsld,midsld+2,endhost-1'
[ "$sec" = 0 ] && {
for s in '--hostcase' '--hostspell=hoSt' '--hostnospace' '--domcase' '--methodeol'; do
pktws_curl_test_update $1 $3 $s
pktws_curl_test_update $1 $3 $s && [ "$SCANLEVEL" = quick ] && return
done
}
@ -1276,10 +1312,21 @@ pktws_check_domain_http_bypass_()
f="$f badseq datanoack md5sig"
[ "$IPV" = 6 ] && f="$f hopbyhop hopbyhop2"
for fooling in $f; do
ok=0
pktws_curl_test_update_vary $1 $2 $3 $desync --dpi-desync-fooling=$fooling $e && {
warn_fool $fooling $desync
[ "$SCANLEVEL" = quick ] && return
need_wssize=0
ok=1
}
[ "$fooling" = md5sig ] && {
[ "$ok" = 1 -a "$SCANLEVEL" != force ] && continue
pktws_curl_test_update_vary $1 $2 $3 $desync --dpi-desync-fooling=$fooling --dup=1 --dup-cutoff=n2 --dup-fooling=md5sig $e && {
warn_fool $fooling $desync
echo "HINT ! To avoid possible 1 sec server response delay use --dup-ttl or --dup-autottl and block ICMP time exceeded"
[ "$SCANLEVEL" = quick ] && return
need_wssize=0
}
}
done
done
@ -1342,8 +1389,11 @@ pktws_check_domain_http_bypass_()
[ "$need_fakedsplit" = 0 ] && contains "$desync" fakedsplit && continue
[ "$need_fakeddisorder" = 0 ] && contains "$desync" fakeddisorder && continue
ok=0
for delta in 1 2 3 4 5; do
pktws_curl_test_update_vary $1 $2 $3 $desync --dpi-desync-ttl=1 --dpi-desync-autottl=$delta $e && ok=1
for orig in '' 1 2 3; do
for delta in 1 2 3 4 5; do
pktws_curl_test_update_vary $1 $2 $3 $desync ${orig:+--orig-autottl=+$orig} --dpi-desync-ttl=1 --dpi-desync-autottl=-$delta $e && ok=1
done
[ "$ok" = 1 -a "$SCANLEVEL" != force ] && break
done
[ "$ok" = 1 ] &&
{
@ -1388,13 +1438,15 @@ pktws_check_domain_http3_bypass_()
# $1 - test function
# $2 - domain
local f desync frag tests rep
local f desync frag tests rep fake
for rep in '' 2 5 10 20; do
pktws_curl_test_update $1 $2 --dpi-desync=fake ${rep:+--dpi-desync-repeats=$rep} && [ "$SCANLEVEL" != force ] && {
[ "$SCANLEVEL" = quick ] && return
break
}
for fake in '' "--dpi-desync-fake-quic=$ZAPRET_BASE/files/fake/quic_initial_www_google_com.bin"; do
for rep in '' 2 5 10 20; do
pktws_curl_test_update $1 $2 --dpi-desync=fake ${fake:+$fake }${rep:+--dpi-desync-repeats=$rep} && [ "$SCANLEVEL" != force ] && {
[ "$SCANLEVEL" = quick ] && return
break
}
done
done
[ "$IPV" = 6 ] && {

View File

@ -405,14 +405,14 @@ std_ports()
has_bad_ws_options()
{
# $1 - nfqws/tpws opts
# ПРИМЕЧАНИЕ ДЛЯ РАСПРОСТРАНИТЕЛЕЙ КОПИПАСТЫ
# ЭТОТ КОД СДЕЛАН СПЕЦИАЛЬНО ДЛЯ ВАС, ЧТОБЫ ВЫ НЕ ПОСТИЛИ В СЕТЬ ПЛОХИЕ РЕЦЕПТЫ
# ЕСЛИ ВАМ ХОЧЕТСЯ ЕГО УДАЛИТЬ И НАПИСАТЬ ИНСТРУКЦИЮ КАК ЕГО УДАЛЯТЬ, ВЫ ДЕЛАЕТЕ ХРЕНОВУЮ УСЛУГУ. НАПИШИТЕ ЛУЧШЕ custom script.
# custom script - ЭТО ФАЙЛИК, КОТОРЫЙ ДОСТАТОЧНО СКОПИРОВАТЬ В НУЖНУЮ ДИРЕКТОРИЮ, ЧТОБЫ ОН СДЕЛАЛ ТОЖЕ САМОЕ, НО ЭФФЕКТИВНО.
# ФИЛЬТРАЦИЯ ПО IPSET В ЯДРЕ НЕСРАВНИМО ЭФФЕКТИВНЕЕ, ЧЕМ ПЕРЕКИДЫВАТЬ ВСЕ ПАКЕТЫ В nfqws И ТАМ ФИЛЬТРОВАТЬ
# --ipset СУЩЕСТВУЕТ ТОЛЬКО ДЛЯ ВИНДЫ И LINUX СИСТЕМ БЕЗ ipset (НАПРИМЕР, Android).
# И ТОЛЬКО ПО ЭТОЙ ПРИЧИНЕ ОНО НЕ ВЫКИНУТО ПОЛНОСТЬЮ ИЗ LINUX ВЕРСИИ
contains "$1" "--ipset"
# kernel or user mode ipset usage should be wise
# if all traffic is already intercepted it would be OK to use ip-based specialized profiles
# but if all traffic is intercepted only to filter a group of ip its BAD. kernel ipset should be used.
# I cannot insert brain to copy-pasters, I know they will misuse. But it's their problem.
# zapret is not made for newbies
#contains "$1" "--ipset"
return 1
}
check_bad_ws_options()
{

View File

@ -391,6 +391,27 @@ zapret_do_firewall_rules_ipt()
zapret_do_firewall_standard_rules_ipt $1
custom_runner zapret_custom_firewall $1
zapret_do_icmp_filter $1
}
zapret_do_icmp_filter()
{
# $1 - 1 - add, 0 - del
local FW_EXTRA_PRE= FW_EXTRA_POST=
[ "$FILTER_TTL_EXPIRED_ICMP" = 1 ] && {
[ "$DISABLE_IPV4" = 1 ] || {
ipt_add_del $1 POSTROUTING -t mangle -m mark --mark $DESYNC_MARK/$DESYNC_MARK -j CONNMARK --or-mark $DESYNC_MARK
ipt_add_del $1 INPUT -p icmp -m icmp --icmp-type time-exceeded -m connmark --mark $DESYNC_MARK/$DESYNC_MARK -j DROP
ipt_add_del $1 FORWARD -p icmp -m icmp --icmp-type time-exceeded -m connmark --mark $DESYNC_MARK/$DESYNC_MARK -j DROP
}
[ "$DISABLE_IPV6" = 1 ] || {
ipt6_add_del $1 POSTROUTING -t mangle -m mark --mark $DESYNC_MARK/$DESYNC_MARK -j CONNMARK --or-mark $DESYNC_MARK
ipt6_add_del $1 INPUT -p icmpv6 -m icmp6 --icmpv6-type time-exceeded -m connmark --mark $DESYNC_MARK/$DESYNC_MARK -j DROP
ipt6_add_del $1 FORWARD -p icmpv6 -m icmp6 --icmpv6-type time-exceeded -m connmark --mark $DESYNC_MARK/$DESYNC_MARK -j DROP
}
}
}
zapret_do_firewall_ipt()

View File

@ -111,6 +111,14 @@ unprepare_route_localnet()
set_route_localnet 0 "$@"
}
get_uevent_devtype()
{
local DEVTYPE INTERFACE IFINDEX OF_NAME OF_FULLNAME OF_COMPATIBLE_N
[ -f "/sys/class/net/$1/uevent" ] && {
. "/sys/class/net/$1/uevent"
echo -n $DEVTYPE
}
}
resolve_lower_devices()
{
# $1 - bridge interface name

View File

@ -106,7 +106,7 @@ cat << EOF | nft -f -
flush chain inet $ZAPRET_NFT_TABLE predefrag_nfqws
add rule inet $ZAPRET_NFT_TABLE predefrag mark and $DESYNC_MARK !=0 jump predefrag_nfqws comment "nfqws generated : avoid drop by INVALID conntrack state"
add rule inet $ZAPRET_NFT_TABLE predefrag_nfqws mark and $DESYNC_MARK_POSTNAT !=0 notrack comment "postnat traffic"
add rule inet $ZAPRET_NFT_TABLE predefrag_nfqws ip frag-off != 0 notrack comment "ipfrag"
add rule inet $ZAPRET_NFT_TABLE predefrag_nfqws ip frag-off & 0x1fff != 0 notrack comment "ipfrag"
add rule inet $ZAPRET_NFT_TABLE predefrag_nfqws exthdr frag exists notrack comment "ipfrag"
add rule inet $ZAPRET_NFT_TABLE predefrag_nfqws tcp flags ! syn,rst,ack notrack comment "datanoack"
add set inet $ZAPRET_NFT_TABLE lanif { type ifname; }
@ -119,6 +119,20 @@ EOF
nft_flush_chain predefrag_nfqws
nft_add_rule predefrag_nfqws notrack comment \"do not track nfqws generated packets to avoid nat tampering and defragmentation\"
}
[ "$FILTER_TTL_EXPIRED_ICMP" = 1 ] && {
if is_postnat; then
# can be caused by untracked nfqws-generated packets
nft_add_rule prerouting icmp type time-exceeded ct state invalid drop
else
nft_add_rule postrouting_hook mark and $DESYNC_MARK != 0 ct mark set ct mark or $DESYNC_MARK comment \"nfqws related : prevent ttl expired socket errors\"
fi
[ "$DISABLE_IPV4" = "1" ] || {
nft_add_rule prerouting icmp type time-exceeded ct mark and $DESYNC_MARK != 0 drop comment \"nfqws related : prevent ttl expired socket errors\"
}
[ "$DISABLE_IPV6" = "1" ] || {
nft_add_rule prerouting icmpv6 type time-exceeded ct mark and $DESYNC_MARK != 0 drop comment \"nfqws related : prevent ttl expired socket errors\"
}
}
}
nft_del_chains()
{
@ -320,7 +334,7 @@ nft_fill_ifsets()
# $5 - space separated wan physical interface names (optional)
# $6 - space separated wan6 physical interface names (optional)
local script i j ALLDEVS devs
local script i j ALLDEVS devs b
# if large sets exist nft works very ineffectively
# looks like it analyzes the whole table blob to find required data pieces
@ -348,15 +362,18 @@ flush set inet $ZAPRET_NFT_TABLE lanif"
nft_create_or_update_flowtable 'offload' 2>/dev/null
# then add elements. some of them can cause error because unsupported
for i in $ALLDEVS; do
# first try to add interface itself
nft_create_or_update_flowtable 'offload' $i 2>/dev/null
# bridge members must be added instead of the bridge itself
# some members may not support hw offload. example : lan1 lan2 lan3 support, wlan0 wlan1 - not
b=
devs=$(resolve_lower_devices $i)
for j in $devs; do
# do not display error if addition failed
nft_create_or_update_flowtable 'offload' $j 2>/dev/null
nft_create_or_update_flowtable 'offload' $j && b=1 2>/dev/null
done
[ -n "$b" ] || {
# no lower devices added ? try to add interface itself
nft_create_or_update_flowtable 'offload' $i 2>/dev/null
}
done
;;
esac
@ -453,7 +470,7 @@ _nft_fw_nfqws_post4()
nft_print_op "$filter" "nfqws postrouting (qnum $port)" 4
rule="${3:+oifname @wanif }$filter ip daddr != @nozapret"
is_postnat && setmark="meta mark set meta mark or $DESYNC_MARK_POSTNAT"
nft_insert_rule $chain $rule $setmark $FW_EXTRA_POST queue num $port bypass
nft_insert_rule $chain $rule $setmark $CONNMARKER $FW_EXTRA_POST queue num $port bypass
nft_add_nfqws_flow_exempt_rule "$rule"
}
}
@ -468,7 +485,7 @@ _nft_fw_nfqws_post6()
nft_print_op "$filter" "nfqws postrouting (qnum $port)" 6
rule="${3:+oifname @wanif6 }$filter ip6 daddr != @nozapret6"
is_postnat && setmark="meta mark set meta mark or $DESYNC_MARK_POSTNAT"
nft_insert_rule $chain $rule $setmark $FW_EXTRA_POST queue num $port bypass
nft_insert_rule $chain $rule $setmark $CONNMARKER $FW_EXTRA_POST queue num $port bypass
nft_add_nfqws_flow_exempt_rule "$rule"
}
}
@ -492,7 +509,7 @@ _nft_fw_nfqws_pre4()
local filter="$1" port="$2" rule
nft_print_op "$filter" "nfqws prerouting (qnum $port)" 4
rule="${3:+iifname @wanif }$filter ip saddr != @nozapret"
nft_insert_rule $(get_prechain) $rule $FW_EXTRA_POST queue num $port bypass
nft_insert_rule $(get_prechain) $rule $CONNMARKER $FW_EXTRA_POST queue num $port bypass
}
}
_nft_fw_nfqws_pre6()
@ -505,7 +522,7 @@ _nft_fw_nfqws_pre6()
local filter="$1" port="$2" rule
nft_print_op "$filter" "nfqws prerouting (qnum $port)" 6
rule="${3:+iifname @wanif6 }$filter ip6 saddr != @nozapret6"
nft_insert_rule $(get_prechain) $rule $FW_EXTRA_POST queue num $port bypass
nft_insert_rule $(get_prechain) $rule $CONNMARKER $FW_EXTRA_POST queue num $port bypass
}
}
nft_fw_nfqws_pre()
@ -683,3 +700,7 @@ zapret_do_firewall_nft()
return 0
}
# ctmark is not available in POSTNAT mode
CONNMARKER=
[ "$FILTER_TTL_EXPIRED_ICMP" = 1 ] && is_postnat && CONNMARKER="ct mark set ct mark or $DESYNC_MARK"

View File

@ -129,6 +129,11 @@ INIT_APPLY_FW=1
# do not work with ipv6
DISABLE_IPV6=1
# drop icmp time exceeded messages for nfqws tampered connections
# in POSTNAT mode this can interfere with default mtr/traceroute in tcp or udp mode. use source port not redirected to nfqws
# set to 0 if you are not expecting connection breakage due to icmp in response to TCP SYN or UDP
FILTER_TTL_EXPIRED_ICMP=1
# select which init script will be used to get ip or host list
# possible values : get_user.sh get_antizapret.sh get_combined.sh get_reestr.sh get_hostlist.sh
# comment if not required

View File

@ -481,3 +481,24 @@ nfqws: update default TLS ClientHello fake. firefox 136.0.4 finger, no kyber, SN
nfqws: multiple mods for multiple TLS fakes
init.d: remove 50-discord
blockcheck: use tpws --fix-seg on linux for multiple splits
v71
nfqws,tpws: debug tls version, alpn, ech
nfqws: --dpi-desync-fake-tls=! means default tls fake
nfqws: --dup*
nfqws: --orig*
nfqws: ipcache of hop count and host names
nfqws: --ctrack-disable
nfqws: --synack-split
nfqws: --autottl=- or --autottl=0:0-0 disable autottl. previous "0" does not work anymore.
tpws: ipcache of host names
nfqws,tpws: set 1024 repeat limit to fakes and dups
nfqws,tpws: do more before daemonize
nfqws,tpws: accept multiple gids in --gid
nfqws,tpws: display "android" in version string if built for android
init.d: remove --ipset parameter prohibition
init.d, blockcheck: drop time exceeded icmp for nfqws-related connections
blockcheck: some dup and orig-ttl mods
blockcheck: PKTWS_EXTRA_PRE
blockcheck: report test function and domain every test

View File

@ -1,9 +1,10 @@
# zapret v70.6
# zapret v71
# SCAMMER WARNING
This software is free and open source under [MIT license](./LICENSE.txt).
If anyone demands you to download this software only from their webpage, telegram channel, forces you to delete links, videos, makes copyright claims, you are dealing with scammers.
However, [donations](#donations) are welcome.
# Multilanguage/Мультиязычный README
___
@ -23,9 +24,12 @@ ___
- [TCP segmentation](#tcp-segmentation)
- [Sequence numbers overlap](#sequence-numbers-overlap)
- [ipv6 specific modes](#ipv6-specific-modes)
- [Original modding](#original-modding)
- [Duplicates](#duplicates)
- [Server reply reaction](#server-reply-reaction)
- [SYNDATA mode](#syndata-mode)
- [DPI desync combos](#dpi-desync-combos)
- [IP cache](#ip-cache)
- [CONNTRACK](#conntrack)
- [Reassemble](#reassemble)
- [UDP support](#udp-support)
@ -34,6 +38,8 @@ ___
- [Virtual machines](#virtual-machines)
- [IPTABLES for nfqws](#iptables-for-nfqws)
- [NFTABLES for nfqws](#nftables-for-nfqws)
- [Flow offloading](#flow-offloading)
- [Server side fooling](#server-side-fooling)
- [tpws](#tpws)
- [TCP segmentation in tpws](#tcp-segmentation-in-tpws)
- [TLSREC](#tlsrec)
@ -139,24 +145,45 @@ nfqws takes the following parameters:
--daemon ; daemonize
--pidfile=<filename> ; write pid to file
--user=<username> ; drop root privs
--uid=uid[:gid] ; drop root privs
--uid=uid[:gid1,gid2,...] ; drop root privs
--bind-fix4 ; apply outgoing interface selection fix for generated ipv4 packets
--bind-fix6 ; apply outgoing interface selection fix for generated ipv6 packets
--wsize=<window_size>[:<scale_factor>] ; set window size. 0 = do not modify. OBSOLETE !
--wssize=<window_size>[:<scale_factor>] ; set window size for server. 0 = do not modify. default scale_factor = 0.
--wssize-cutoff=[n|d|s]N ; apply server wsize only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N
--ctrack-timeouts=S:E:F[:U] ; internal conntrack timeouts for TCP SYN, ESTABLISHED, FIN stages, UDP timeout. default 60:300:60:60
--ctrack-disable=[0|1] ; 1 or no argument disables conntrack
--ipcache-lifetime=<int> ; time in seconds to keep cached hop count and domain name (default 7200). 0 = no expiration
--ipcache-hostname=[0|1] ; 1 or no argument enables ip->hostname caching
--hostcase ; change Host: => host:
--hostspell ; exact spelling of "Host" header. must be 4 chars. default is "host"
--hostnospace ; remove space after Host: and add it to User-Agent: to preserve packet size
--domcase ; mix domain case : Host: TeSt.cOm
--methodeol ; add '\n' before method and remove space after Host:
--synack-split=[syn|synack|acksyn] ; perform TCP split handshake : send SYN only, SYN+ACK or ACK+SYN
--orig-ttl=<int> ; set TTL for original packets
--orig-ttl6=<int> ; set ipv6 hop limit for original packets. by default ttl value is used
--orig-autottl=[<delta>[:<min>[-<max>]]|-] ; auto ttl mode for both ipv4 and ipv6. default: +5:3-64. "0:0-0" or "-" disables autottl.
--orig-autottl6=[<delta>[:<min>[-<max>]]|-] ; overrides --orig-autottl for ipv6 only
--orig-mod-start=[n|d|s]N ; apply orig TTL mod to packet numbers (n, default), data packet numbers (d), relative sequence (s) greater or equal than N
--orig-mod-cutoff=[n|d|s]N ; apply orig TTL mod to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N
--dup=<int> ; duplicate original packets. send N dups before original.
--dup-replace=[0|1] ; 1 or no argument means do not send original, only dups
--dup-ttl=<int> ; set TTL for dups
--dup-ttl6=<int> ; set ipv6 hop limit for dups. by default ttl value is used
--dup-autottl=[<delta>[:<min>[-<max>]]|-] ; auto ttl mode for both ipv4 and ipv6. default: -1:3-64. "0:0-0" or "-" disables autottl.
--dup-autottl6=[<delta>[:<min>[-<max>]]|-] ; overrides --dup-autottl for ipv6 only
--dup-fooling=<mode>[,<mode>] ; can use multiple comma separated values. modes : none md5sig badseq badsum datanoack hopbyhop hopbyhop2
--dup-badseq-increment=<int|0xHEX> ; badseq fooling seq signed increment for dup. default -10000
--dup-badack-increment=<int|0xHEX> ; badseq fooling ackseq signed increment for dup. default -66000
--dup-start=[n|d|s]N ; apply dup to packet numbers (n, default), data packet numbers (d), relative sequence (s) greater or equal than N
--dup-cutoff=[n|d|s]N ; apply dup to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N
--dpi-desync=[<mode0>,]<mode>[,<mode2>] ; try to desync dpi state. modes : synack fake fakeknown rst rstack hopbyhop destopt ipfrag1 multisplit multidisorder fakedsplit fakeddisorder ipfrag2 udplen tamper
--dpi-desync-fwmark=<int|0xHEX> ; override fwmark for desync packet. default = 0x40000000 (1073741824)
--dpi-desync-ttl=<int> ; set ttl for desync packet
--dpi-desync-ttl6=<int> ; set ipv6 hop limit for desync packet. by default ttl value is used.
--dpi-desync-autottl=[<delta>[:<min>[-<max>]]] ; auto ttl mode for both ipv4 and ipv6. default: 1:3-20
--dpi-desync-autottl6=[<delta>[:<min>[-<max>]]] ; overrides --dpi-desync-autottl for ipv6 only
--dpi-desync-autottl=[<delta>[:<min>[-<max>]]|-] ; auto ttl mode for both ipv4 and ipv6. default: -1:3-20. "0:0-0" or "-" disables autottl.
--dpi-desync-autottl6=[<delta>[:<min>[-<max>]]|-] ; overrides --dpi-desync-autottl for ipv6 only
--dpi-desync-fooling=<mode>[,<mode>] ; can use multiple comma separated values. modes : none md5sig ts badseq badsum datanoack hopbyhop hopbyhop2
--dpi-desync-repeats=<N> ; send every desync packet N times
--dpi-desync-skip-nosni=0|1 ; 1(default)=do not act on ClientHello without SNI (ESNI ?)
@ -173,7 +200,7 @@ nfqws takes the following parameters:
--dpi-desync-badack-increment=<int|0xHEX> ; badseq fooling ackseq signed increment. default -66000
--dpi-desync-any-protocol=0|1 ; 0(default)=desync only http and tls 1=desync any nonempty data packet
--dpi-desync-fake-http=<filename>|0xHEX ; file containing fake http request
--dpi-desync-fake-tls=<filename>|0xHEX ; file containing fake TLS ClientHello (for https)
--dpi-desync-fake-tls=<filename>|0xHEX|! ; file containing fake TLS ClientHello (for https). '!' = standard fake
--dpi-desync-fake-tls-mod=mod[,mod] ; comma separated list of TLS fake mods. available mods : none,rnd,rndsni,sni=<sni>,dupsid,padencap
--dpi-desync-fake-unknown=<filename>|0xHEX ; file containing unknown protocol fake payload
--dpi-desync-fake-syndata=<filename>|0xHEX ; file containing SYN data payload
@ -257,10 +284,13 @@ Fakes are separate generated by nfqws packets carrying false information for DPI
* **datanoack** sends tcp fakes without ACK flag. Servers do not accept this but DPI may accept.
This mode may break NAT and may not work with iptables if masquerade is used, even from the router itself.
Works with nftables properly. Likely requires external IP address (some ISPs pass these packets through their NAT).
* **autottl** tries to automatically guess TTL value that allows DPI to receive fakes and does not allow them to reach the server.
This tech relies on well known TTL values used by OS : 64,128,255. nfqws takes first incoming packet (YES, you need to redirect it too),
guesses path length and decreases by `delta` value (default 1). If resulting value is outside the range (min,max - default 3,20)
then its normalized to min or max. If the path shorter than the value then autottl fails and falls back to the fixed value.
* **autottl** tries to automatically guess hop count to the server and compute TTL by adding some delta value that can be positive or negative.
Positive deltas must be preceeded by unary `+` sign. Deltas without any unary sign are treated negative for old versions compatibility reasons.
This tech relies on well known TTL default values used by OS : 64,128,255.
nfqws needs first incoming packet to see it's TTL. You must redirect it too.
If resulting value TTL is outside the range (min,max) then its normalized to min or max.
If delta is negative and TTL is longer than guessed hop count or delta is positive and TTL is shorter than guessed hop count
then autottl fails and falls back to the fixed value.
This can help if multiple DPIs exists on backbone channels, not just near the ISP.
Can fail if inbound and outbound paths are not symmetric.
@ -356,6 +386,44 @@ For example, `hopbyhop,multisplit` means split original tcp packet into several
With `hopbyhop,ipfrag2` header sequence will be : `ipv6,hop-by-hop,fragment,tcp/udp`.
`ipfrag1` mode may not always work without special preparations. See "IP Fragmentation" notices.
### Original modding
Parameters `--orig-ttl` and `--orig-ttl6` allow to set TTL on original packets.
All further packet manipulations, e.g. segmentation, take modded original as data source and inherit modded TTL.
`--orig-autottl` and `--orig-autottl6` work the same way as `dpi-desync-autottl`, but on original packets.
Delta should have unary `+` sign to produce TTL longer than guessed hop count. Otherwise nothing will reach the server.
Example : `--orig-autottl=+5:3-64`.
`--orig-mod-start` and `--orig-mod-cutoff` specify start and end conditions for original modding. The work the same way as
`--dpi-desync-start` and `--dpi-desync-cutoff`.
This function can be useful when DPI hunts for fakes and blocks suspicious connections.
DPI can compute TTL difference between packets and fire block trigger if it exceedes some threshold.
### Duplicates
Duplicates are copies of original packets which are sent before them. Duplicates are enabled by `--dup=N`, where N is dup count.
`--dup-replace` disables sending of original.
Dups are sent only when original would also be sent without reconstruction.
For example, if TCP segmentation happens, original is actually dropped and is being replaced by artificially constructed new packets.
Dups are not sent in this case.
All dup fooling modes are available : `--dup-ttl`. `--dup-ttl6`, `--dup-fooling`.
You decide whether these packets need to reach the server and in what form, according to the intended strategy.
`--dup-autottl` and `--dup-autottl6` work the same way as `dpi-desync-autottl`.
Delta can be preceeded by unary `+` or `-` sign.
Example : `--dup-autottl=-2:3-64`.
`--dup-start` and `--dup-cutoff` specify start and end conditions for dupping. The work the same way as
`--dpi-desync-start` and `--dpi-desync-cutoff`.
This function can help if DPI compares some characteristics of fake and original packets and block connection if they differ some way.
Fooled duplicates can convince DPI that the whole session has an anomaly.
For example, all connection is protected by MD5 signature, not individual packets.
### Server reply reaction
There are DPIs that analyze responses from the server, particularly the certificate from the ServerHello that contain domain name(s). The ClientHello delivery confirmation is an ACK packet from the server with ACK sequence number corresponding to the length of the ClientHello+1.
@ -377,12 +445,26 @@ Without extra parameter payload is 16 zero bytes.
`--dpi-desync` takes up to 3 comma separated modes.
* 0 phase modes work during the connection establishement : `synack`, `syndata` `--wsize`, `--wssize`. [hostlist](((#multiple-strategies))) filters are not applicable.
* 0 phase modes work during the connection establishement : `synack`, `syndata` `--wsize`, `--wssize`. [hostlist](#multiple-strategies) filters are applicable only if [`--ipcache-hostname`](#ip-cache) is enabled.
* In the 1st phase fakes are sent before original data : `fake`, `rst`, `rstack`.
* In the 2nd phase original data is sent in a modified way (for example `fakedsplit` or `ipfrag2`).
Modes must be specified in phase ascending order.
### IP cache
`ipcache` is the structure in the process memory that stores some information by IP address and interface name key.
This information can be used as missing data. Currently it's used in the following cases :
1. IP,interface => hop count . This is used to apply autottl at 0 phase since the first session packet. If the record is absent autottl will not be applied immediately. Second time it will be applied immediately using cached hop count.
2. IP => hostname . Hostname is cached to be used in 0 phase strategies. Mode is disabled by default and can be enabled by `ipcache-hostname` parameter.
This tech is experimental. There's no one-to-one correspondence between IP and domain name. Multiple domains can resolve to the same IP.
If collision happens hostname is replaced. On CDNs a domain can resolve to different IPs over time. `--ipcache-lifetime` limits how long cached record is valid. It's 2 hours by default.
Be prepared for unexpected results that can be explained only by reading debug logs.
SIGUSR2 forces process to output it's ipcache to stdout.
### CONNTRACK
nfqws is equipped with minimalistic connection tracking system (conntrack)
@ -669,6 +751,31 @@ In `iptables` flow offloading is controlled by openwrt proprietary extension `FL
Flow offloading does not interfere with **tpws** and `OUTPUT` traffic. It only breaks nfqws that fools `FORWARD` traffic.
### Server side fooling
It's also possible.
nfqws is intended for client side attacks. That's why it recognizes direct and reply traffic based on role in connection establishement.
If it sees SYN then source IP is client IP. If it sees SYN,ACK then source ip is server IP.
For UDP client address is considered as source IP of the first seen packet of src_ip,src_port,dst_ip,dst_port tuple.
This does not work correctly on the server side. Client traffic is reply traffic, server traffic is direct traffic.
`--wsize` works in any case. It can be used on both client and server.
Other techs work only if nfqws treats traffic as direct traffic.
To apply them to server originated traffic disable conntrack by `--ctrack-disable` parameter.
If a packet is not found in conntrack it's treated as direct and techs like `multidisorder` will be applied.
Most of the protocols will not be recognized because protocol recognition system only reacts to client packets.
To make things working use `--dpi-desync-any-protocol` with connbytes or packet payload limiter.
start/cutoff are unavailable because they are conntrack based.
`--synack-split` removes standard SYN,ACK packet and replaces it with one SYN packet, SYN then ACK separate packets or ACK then SYN separate packets.
Client sends SYN,ACK in reply which usually only server does.
This makes some DPI's to treat connection establishement roles wrong. They stop to block.
See [split handshake](https://nmap.org/misc/split-handshake.pdf).
On server side traffic should be redirected to nfqws using source port numbers and original connbytes direction.
## tpws
@ -705,6 +812,8 @@ tpws is transparent proxy.
--local-tcp-user-timeout=<seconds> ; set tcp user timeout for local leg (default : 10, 0 = system default)
--remote-tcp-user-timeout=<seconds> ; set tcp user timeout for remote leg (default : 20, 0 = system default)
--fix-seg=<int> ; recover failed TCP segmentation at the cost of slowdown. wait up to N msec.
--ipcache-lifetime=<int> ; time in seconds to keep cached domain name (default 7200). 0 = no expiration
--ipcache-hostname=[0|1] ; 1 or no argument enables ip->hostname caching
--no-resolve ; disable socks5 remote dns
--resolver-threads=<int> ; number of resolver worker threads
--maxconn=<max_connections> ; max number of local legs
@ -755,7 +864,7 @@ tpws is transparent proxy.
--daemon ; daemonize
--pidfile=<filename> ; write pid to file
--user=<username> ; drop root privs
--uid=uid[:gid] ; drop root privs
--uid=uid[:gid1,gid2,...] ; drop root privs
```
### TCP segmentation in tpws
@ -789,7 +898,7 @@ If you're attempting to split massive transmission with `--split-any-protocol` o
`--mss` sets TCP_MAXSEG socket option. Client sets this value in MSS TCP option in the SYN packet.
Server replies with it's own MSS in SYN,ACK packet. Usually servers lower their packet sizes but they still don't fit to supplied MSS. The greater MSS client sets the bigger server's packets will be.
If it's enough to split TLS 1.2 ServerHello, it may fool DPI that checks certificate domain name.
This scheme may significantly lower speed. Hostlist filter is possible only in socks mode if client uses remote resolving (firefox `network.proxy.socks_remote_dns`).
This scheme may significantly lower speed. Hostlist filter is possible only in socks mode if client uses remote resolving (firefox `network.proxy.socks_remote_dns`) or if `ipcache-hostname` is enabled.
`--mss` is not required for TLS1.3. If TLS1.3 is negotiable then MSS make things only worse. Use only if nothing better is available. Works only in Linux, not BSD or MacOS.
### Other tamper options
@ -1270,6 +1379,10 @@ With other values or if the parameter is commented out, the rules will not be ap
This is useful if you have a firewall management system, in the settings of which you should tie the rules.
Not applicable to `OpenWRT` if used with `firewall3+iptables`.
`FILTER_TTL_EXPIRED_ICMP=1` blocks icmp time exceeded messages in response to connections handled by nfqws.
Linux closes socket if it receives this icmp in response to SYN packet. Similar mechanism exists for datagram sockets.
It's better to disable this if you do not expect problems caused by icmp.
The following settings are not relevant for openwrt :
If your system works as a router, then you need to enter the names of the internal and external interfaces:

View File

@ -1,9 +1,10 @@
# zapret v70.6
# zapret v71
# ВНИМАНИЕ, остерегайтесь мошенников
zapret является свободным и open source.
Всякий, кто понуждает вас скачивать zapret только с его ресурса, требует удалить ссылки, видео, файлы, обосновывая эти требования авторскими правами, сам нарушает [лицензию](./LICENSE.txt).
Однако, это не исключает [добровольные пожертвования](#поддержать-разработчика).
# Multilanguage README
@ -25,7 +26,10 @@ zapret является свободным и open source.
- [TCP СЕГМЕНТАЦИЯ](#tcp-сегментация)
- [ПЕРЕКРЫТИЕ SEQUENCE NUMBERS](#перекрытие-sequence-numbers)
- [СПЕЦИФИЧЕСКИЕ РЕЖИМЫ IPV6](#специфические-режимы-ipv6)
- [МОДИФИКАЦИЯ ОРИГИНАЛА](#модификация-оригинала)
- [ДУБЛИКАТЫ](#дубликаты)
- [КОМБИНИРОВАНИЕ МЕТОДОВ ДЕСИНХРОНИЗАЦИИ](#комбинирование-методов-десинхронизации)
- [КЭШ IP](#кэш-ip)
- [РЕАКЦИЯ DPI НА ОТВЕТ СЕРВЕРА](#реакция-dpi-на-ответ-сервера)
- [РЕЖИМ SYNACK](#режим-synack)
- [РЕЖИМ SYNDATA](#режим-syndata)
@ -38,6 +42,7 @@ zapret является свободным и open source.
- [IPTABLES ДЛЯ NFQWS](#iptables-для-nfqws)
- [NFTABLES ДЛЯ NFQWS](#nftables-для-nfqws)
- [FLOW OFFLOADING](#flow-offloading)
- [ДУРЕНИЕ СО СТОРОНЫ СЕРВЕРА](#дурение-со-стороны-сервера)
- [tpws](#tpws)
- [TCP СЕГМЕНТАЦИЯ В TPWS](#tcp-сегментация-в-tpws)
- [TLSREC](#tlsrec)
@ -169,10 +174,31 @@ dvtws, собираемый из тех же исходников (см. [док
--qnum=N ; номер очереди N
--bind-fix4 ; пытаться решить проблему неверного выбора исходящего интерфейса для сгенерированных ipv4 пакетов
--bind-fix6 ; пытаться решить проблему неверного выбора исходящего интерфейса для сгенерированных ipv6 пакетов
--ctrack-timeouts=S:E:F[:U] ; таймауты внутреннего conntrack в состояниях SYN, ESTABLISHED, FIN, таймаут udp. по умолчанию 60:300:60:60
--ctrack-disable=[0|1] ; 1 или остутствие аргумента отключает conntrack
--ipcache-lifetime=<int> ; время жизни записей кэша IP в секундах. 0 - без ограничений.
--ipcache-hostname=[0|1] ; 1 или отсутствие аргумента включают кэширование имен хостов для применения в стратегиях нулевой фазы
--wsize=<winsize>[:<scale_factor>] ; менять tcp window size на указанный размер в SYN,ACK. если не задан scale_factor, то он не меняется (устарело !)
--wssize=<winsize>[:<scale_factor>] ; менять tcp window size на указанный размер в исходящих пакетах. scale_factor по умолчанию 0. (см. conntrack !)
--wssize-cutoff=[n|d|s]N ; изменять server window size в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру меньше N
--ctrack-timeouts=S:E:F[:U] ; таймауты внутреннего conntrack в состояниях SYN, ESTABLISHED, FIN, таймаут udp. по умолчанию 60:300:60:60
--synack-split=[syn|synack|acksyn] ; выполнить tcp split handshake. вместо SYN,ACK отсылать только SYN, SYN+ACK или ACK+SYN
--orig-ttl=<int> ; модифицировать TTL оригинального пакета
--orig-ttl6=<int> ; модифицировать ipv6 hop limit оригинальных пакетов. если не указано, используется значение --orig-ttl
--orig-autottl=[<delta>[:<min>[-<max>]]|-] ; режим auto ttl для ipv4 и ipv6. по умолчанию: +5:3-64. "0:0-0" или "-" отключает функцию
--orig-autottl6=[<delta>[:<min>[-<max>]]|-] ; переопределение предыдущего параметра для ipv6
--orig-mod-start=[n|d|s]N ; применять orig-mod только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру больше или равно N
--orig-mod-cutoff=[n|d|s]N ; применять orig-mod только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру меньше N
--dup=<int> ; высылать N дубликатов до оригинала
--dup-replace=[0|1] ; 1 или отсутствие аргумента блокирует отправку оригинала. отправляются только дубликаты.
--dup-ttl=<int> ; модифицировать TTL дубликатов
--dup-ttl6=<int> ; модифицировать ipv6 hop limit дубликатов. если не указано, используется значение --dup-ttl
--dup-autottl=[<delta>[:<min>[-<max>]]|-] ; режим auto ttl для ipv4 и ipv6. по умолчанию: +1:3-64. "0:0-0" или "-" отключает функцию
--dup-autottl6=[<delta>[:<min>[-<max>]]|-] ; переопределение предыдущего параметра для ipv6
--dup-fooling=<fooling> ; дополнительные методики как сделать, чтобы дубликат не дошел до сервера. none md5sig badseq badsum datanoack hopbyhop hopbyhop2
--dup-badseq-increment=<int|0xHEX> ; инкремент sequence number для badseq. по умолчанию -10000
--dup-badack-increment=<int|0xHEX> ; инкремент ack sequence number для badseq. по умолчанию -66000
--dup-start=[n|d|s]N ; применять dup только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру больше или равно N
--dup-cutoff=[n|d|s]N ; применять dup только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру меньше N
--hostcase ; менять регистр заголовка "Host:" по умолчанию на "host:".
--hostnospace ; убрать пробел после "Host:" и переместить его в конец значения "User-Agent:" для сохранения длины пакета
--methodeol ; добавить перевод строки в unix стиле ('\n') перед методом и убрать пробел из Host: : "GET / ... Host: domain.com" => "\nGET / ... Host:domain.com"
@ -181,9 +207,9 @@ dvtws, собираемый из тех же исходников (см. [док
--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-ttl=<int> ; установить ttl для десинхронизирующих пакетов
--dpi-desync-ttl6=<int> ; установить ipv6 hop limit для десинхронизирующих пакетов. если не указано, используется значение ttl
--dpi-desync-autottl=[<delta>[:<min>[-<max>]]] ; режим auto ttl для ipv4 и ipv6. по умолчанию: 1:3-20. delta=0 отключает функцию.
--dpi-desync-autottl6=[<delta>[:<min>[-<max>]]] ; переопределение предыдущего параметра для ipv6
--dpi-desync-ttl6=<int> ; установить ipv6 hop limit для десинхронизирующих пакетов. если не указано, используется значение --dpi-desync-ttl
--dpi-desync-autottl=[<delta>[:<min>[-<max>]]|-] ; режим auto ttl для ipv4 и ipv6. по умолчанию: 1:3-20. "0:0-0" или "-" отключает функцию
--dpi-desync-autottl6=[<delta>[:<min>[-<max>]]|-] ; переопределение предыдущего параметра для ipv6
--dpi-desync-fooling=<fooling> ; дополнительные методики как сделать, чтобы фейковый пакет не дошел до сервера. none md5sig badseq badsum datanoack hopbyhop hopbyhop2
--dpi-desync-repeats=<N> ; посылать каждый генерируемый в nfqws пакет N раз (не влияет на остальные пакеты)
--dpi-desync-skip-nosni=0|1 ; 1(default)=не применять dpi desync для запросов без hostname в SNI, в частности для ESNI
@ -195,7 +221,7 @@ dvtws, собираемый из тех же исходников (см. [док
--dpi-desync-badack-increment=<int|0xHEX> ; инкремент ack sequence number для badseq. по умолчанию -66000
--dpi-desync-any-protocol=0|1 ; 0(default)=работать только по http request и tls clienthello 1=по всем непустым пакетам данных
--dpi-desync-fake-http=<filename>|0xHEX ; файл, содержащий фейковый http запрос для dpi-desync=fake, на замену стандартному www.iana.org
--dpi-desync-fake-tls=<filename>|0xHEX ; файл, содержащий фейковый tls clienthello для dpi-desync=fake, на замену стандартному
--dpi-desync-fake-tls=<filename>|0xHEX|! ; файл, содержащий фейковый tls clienthello для dpi-desync=fake, на замену стандартному. '!' = стандартный фейк
--dpi-desync-fake-tls-mod=mod[,mod] ; список через запятую режимов runtime модификации фейков : none,rnd,rndsni,sni=<sni>,dupsid,padencap
--dpi-desync-fake-unknown=<filename>|0xHEX ; файл, содержащий фейковый пейлоад неизвестного протокола для dpi-desync=fake, на замену стандартным нулям 256 байт
--dpi-desync-fake-syndata=<filename>|0xHEX ; файл, содержащий фейковый пейлоад пакета SYN для режима десинхронизации syndata
@ -310,14 +336,19 @@ dvtws, собираемый из тех же исходников (см. [док
выяснено, что многие провайдерские NAT не отбрасывают эти пакеты, потому работает даже с внутренним провайдерским IP.
Но linux NAT оно не пройдет, так что за домашним роутером эта техника скорее всего не сработает, но может сработать с него.
Может сработать и через роутер, если подключение по проводу, и на роутере включено аппаратное ускорение.
* `autottl`. Суть режима в автоматическом определении TTL, чтобы он почти наверняка прошел DPI и немного не дошел до
сервера. Берутся базовые значения TTL 64,128,255, смотрится входящий пакет
(да, требуется направить первый входящий пакет на nfqws !). Вычисляется длина пути, отнимается `delta` (1 по
умолчанию). Если TTL вне диапазона (min,max - 3,20 по умолчанию), то берутся значения min,max, чтобы вписаться в
диапазон. Если при этом полученный TTL больше длины пути, то автоматизм не сработал и берутся фиксированные значения
TTL для атаки. Техника позволяет решить вопрос, когда вся сеть перегорожена шлагбаумами (DPI, ТСПУ) везде где только
* `autottl`. Суть режима в автоматическом определении TTL, чтобы пакет почти наверняка прошел DPI и немного не дошел до
сервера (`--dpi-desync-autottl`). Или наоборот - TTL едва хватило, чтобы он все-таки дошел до сервера (см `--dup-autottl`, `--orig-autottl`).
Берутся базовые значения TTL 64,128,255, смотрится входящий пакет (да, требуется направить первый входящий пакет на nfqws !).
Вычисляется длина пути, прибавляется `delta`. delta может быть положительной или отрицательной.
Чтобы задать положительную дельту, нужно указать унарный знак **+** перед числом.
В случае его отсутствия или при наличии унарного знака **-** дельта считается отрицательной.
Если TTL вне диапазона min,max, то берутся значения min,max, чтобы вписаться в
диапазон. Если при этом дельта отрицательная и полученный TTL больше длины пути или дельта положительная и полученный TTL меньше длины пути,
то автоматизм не сработал и берутся фиксированные значения : `--dpi-desync-ttl`, `--orig-ttl`, `--dup-ttl`.
Техника позволяет решить вопрос, когда вся сеть перегорожена шлагбаумами (DPI, ТСПУ) везде где только
можно, включая магистралов. Но потенциально может давать сбои. Например, при асимметрии входящего и исходящего канала
до конкретного сервера. На каких-то провайдерах эта техника будет работать неплохо, на других доставит больше проблем,
до конкретного сервера. Некоторые сервера выдают нестандартный TTL (google), потому на них получается полная ерунда.
Если не учитывать подобные исключения, то на каких-то провайдерах эта техника будет работать неплохо, на других доставит больше проблем,
чем пользы. Где-то может потребоваться тюнинг параметров. Лучше использовать с дополнительным ограничителем.
Режимы дурения могут сочетаться в любых комбинациях. `--dpi-desync-fooling` берет множество значений через запятую.
@ -450,16 +481,69 @@ extension хедерам в поисках транспортного хедер
При `hopbyhop,ipfrag2` последовательность хедеров будет : `ipv6,hop-by-hop`,`fragment`,`tcp/udp`.
Режим `ipfrag1` может срабатывать не всегда без специальной подготовки. См. раздел `IP фрагментация`.
### МОДИФИКАЦИЯ ОРИГИНАЛА
Параметры `--orig-ttl` и `--orig-ttl6` позволяют изменить TTL оригинальных пакетов.
Если дальнейшие манипуляции связаны с оригиналом, например, идет TCP сегментация, то исходными
данными являются измененные оригинальные пакеты. То есть в данном примере TCP сегменты пойдут с измененным TTL.
Вариант `--orig-autottl` и `--orig-autottl6` работает аналогично `dpi-desync-autottl`, но по оригинальным пакетам.
Дельту стоит указывать положительную с унарным знаком `+`, иначе оригинал не дойдет до сервера, и вы вообще ничего не получите.
Пример : `--orig-autottl=+5:3-64`.
`--orig-mod-start` и `--orig-mod-cutoff` задают ограничитель по началу и концу модификации оригинала.
Схема аналогична `--dpi-desync-start` и `--dpi-desync-cutoff`.
Функция может быть полезна, когда DPI охотится за фейками и блокирует соединение при наличии подозрительных признаков,
в частности, измененный TTL у фейка относительно оригинала.
### ДУБЛИКАТЫ
Дубликаты - это копии оригинальных пакетов, высылаемые перед ними. Включаются параметром `--dup=N`, где N - количество дублей,
не включающее оригинал. `--dup-replace` отключает отсылку оригинала.
Отсылка дублей имеет место только в тех случаях, когда высылается и оригинал без реконструкции.
Например, если случилась TCP сегментация, то оригинал фактически дропается и заменяется искусственно сконструированными сегментами.
Дубли высланы не будут. Это же касается изменения состава хедеров ipv6, режима tamper для DHT и других.
Возможно применение всех вариантов дурения, как и для desync : `--dup-ttl`. `--dup-ttl6`, `--dup-fooling`. Нужно ли, чтобы эти пакеты доходили до сервера и в каком виде, решаете вы согласно задуманной стратегии.
Вариант `--dup-autottl` и `--dup-autottl6` работает аналогично `dpi-desync-autottl`, но по дублям.
Дельту можно указывать положительную с унарным знаком `+`, а можно и отрицательную. Зависит от вашей задумки.
Пример : `--dup-autottl=-2:3-64`.
`--dup-start` и `--dup-cutoff` задают ограничитель по началу и концу применения стратегии дубликатов.
Схема аналогична `--dpi-desync-start` и `--dpi-desync-cutoff`.
Функция может помочь, когда DPI сечет разницу в характеристиках фейков и оригинала.
Дубликатами можно попытаться заставить DPI принять , что весь сеанс идет аномальным.
Например, у нас имеется TCP сеанс с MD5 сразу с первого SYN пакета. Значит последующие MD5 будут восприниматься нормально.
### КОМБИНИРОВАНИЕ МЕТОДОВ ДЕСИНХРОНИЗАЦИИ
В параметре dpi-desync можно указать до 3 режимов через запятую.
* 0 фаза - предполагает работу на этапе установления соединения : `synack`, `syndata`, `--wsize`, `--wssize`. На эту фазу не действуют фильтры по [hostlist](#множественные-стратегии).
* 0 фаза - предполагает работу на этапе установления соединения : `synack`, `syndata`, `--wsize`, `--wssize`. На эту фазу не действуют фильтры по [hostlist](#множественные-стратегии), кроме случая, описанного [далее](#кэш-ip).
* 1 фаза - отсылка чего-либо до оригинального пакета данных : `fake`, `rst`, `rstack`.
* 2 фаза - отсылка в модифицированном виде оригинального пакета данных (например, `fakedsplit` или `ipfrag2`).
Режимы требуют указания в порядке возрастания номеров фаз.
### КЭШ IP
ipcache представляет собой структуру в памяти процесса, позволяющую по ключу IP адреса и имени интерфейса запоминать некоторую информацию,
которую впоследствии можно извлечь и использовать как недостающие данные. На текущий момент это применяются в следующих ситуациях :
1. IP,interface => hop count . Кэшируется количество хопов до сервера для последующего применения в autottl прямо с первого пакета, когда еще ответа не было. Пока записи в кэше нет, autottl не будет применен сразу. При повторном запросе до истечения времени жизни записи autottl будет применение сразу.
2. IP => hostname . Кэшируется имя хоста, вне привязки к интерфейсу, для последующего применения в стратегиях нулевой фазы. Режим отключен по умолчанию и включается через параметры `ipcache-hostname`.
Данная техника является экспериментальной. Ее проблема в том, что как такового нет однозначного соответствия между доменом и IP. Множество доменов могут ссылаться на тот же IP адрес.
При коллизии происходит замещение имени хоста на последний вариант.
Домен может скакать по разным IP на CDN. Сейчас один адрес, через час - другой. Эта проблема решается через время жизни записей кэша : `--ipcache-lifetime`. По умолчанию 2 часа.
Однако, может случиться и так, что в вашем случае применение техники несет больше пользы, чем проблем. Будьте готовы к непонятному на первый взгляд поведению, которое может быть исследовано только через `--debug` лог.
При подаче сигнала SIGUSR2 процесс выводит содержимое ipcache на консоль.
### РЕАКЦИЯ DPI НА ОТВЕТ СЕРВЕРА
Есть DPI, которые анализируют ответы от сервера, в частности сертификат из ServerHello, где прописаны домены.
@ -803,6 +887,32 @@ iptables target `FLOWOFFLOAD` - это проприетарное изобрет
Управление offload в nftables реализовано в базовом ядре linux без патчей.
nftables - единственный способ включения offload на классическом Linux.
### ДУРЕНИЕ СО СТОРОНЫ СЕРВЕРА
Это тоже возможно.
nfqws рассчитан на атаку со стороны клиента, поэтому он распознает прямой и обратный трафик на основании роли в установлении tcp соединения.
Если проходит SYN, то source IP - это клиент. Если проходит SYN,ACK , то source IP - это сервер.
Для UDP клиентом считается source IP первого прошедшего пакета по двум связкам ip-port.
На сервере трафиком клиента будет считаться принятый трафик, а трафиком сервера - исходящий.
`--wsize` работает в любом случае, он может использоваться как на клиенте, так и на сервере.
Остальные техники работают только если nfqws считает трафик трафиком клиента.
Поэтому для их применения по исходящему с сервера трафику conntrack нужно выключить параметром `--ctrack-disable`.
Если пакет не найден в conntrack, по нему идет работа как по пакету клиента.
Большинство протоколов опознаваться не будет, потому что система их опознавания рассчитана на содержание пакетов от клиента.
Чтобы задействовать техники типа `fake` или `multisplit` нужно использовать `--dpi-desync-any-protocol` с ограничителем connbytes или
с ограничителем на основании содержания пакета или его заголовков.
start/cutoff недоступны, поскольку завязаны на conntrack.
Техника `synack-split` позволяет разбить tcp сегмент SYN,ACK на отдельные части с SYN и с ACK.
В ответ на это клиент шлет SYN,ACK , что обычно характеризует сервер.
У некоторых DPI от этого может ломаться алгоритм, и они перестают блокировать запрещенный контент.
Здесь [подробное описание](https://nmap.org/misc/split-handshake.pdf) что есть split handshake.
Перенаправление трафика обычно идет по номеру source портов и направлению original.
original - это исходящий с системы трафик, reply - входящий.
## tpws
@ -861,6 +971,8 @@ tpws - это transparent proxy.
--local-tcp-user-timeout=<seconds> ; таймаут соединений client-proxy (по умолчанию : 10 сек, 0 = оставить системное значение)
--remote-tcp-user-timeout=<seconds> ; таймаут соединений proxy-target (по умолчанию : 20 сек, 0 = оставить системное значение)
--fix-seg=<int> ; исправлять неудачи tcp сегментации ценой задержек для всех клиентов и замедления. ждать до N мс. по умолчанию 30 мс.
--ipcache-lifetime=<int> ; время жизни записей кэша IP в секундах. 0 - без ограничений.
--ipcache-hostname=[0|1] ; 1 или отсутствие аргумента включают кэширование имен хостов для применения в стратегиях нулевой фазы
--split-pos=N|-N|marker+N|marker-N ; список через запятую маркеров для tcp сегментации
--split-any-protocol ; применять сегментацию к любым пакетам. по умолчанию - только к известным протоколам (http, TLS)
@ -982,9 +1094,9 @@ tpws работает на уровне сокетов, поэтому длин
шлет сервер. На TLS 1.2 если сервер разбил заброс так, чтобы домен из сертификата не попал в первый пакет,
это может обмануть DPI, секущий ответ сервера.
Схема может значительно снизить скорость и сработать не на всех сайтах.
С фильтром по hostlist совместимо только в режиме socks при включенном удаленном ресолвинге хостов.
(firefox network.proxy.socks_remote_dns). Это единственный вариант, когда tpws может узнать имя хоста
еще на этапе установления соединения.
С фильтром по hostlist совместимо только в [некоторых случаях](#множественные-стратегии-1), когда возможно узнать имя хоста на момент применения дурения.
Применяя данную опцию к сайтам TLS1.3, если броузер тоже поддерживает TLS1.3, то вы делаете только хуже.
Но нет способа автоматически узнать когда надо применять, когда нет, поскольку MSS идет только в
3-way handshake еще до обмена данными, а версию TLS можно узнать только по ответу сервера, который
@ -1008,14 +1120,17 @@ tpws работает на уровне сокетов, поэтому длин
Работают аналогично **nfqws**, кроме некоторых моментов.
Нет параметра `--filter-udp`, поскольку **tpws** udp не поддерживает.
Методы нулевой фазы (`--mss`) могут работать по хостлисту в одном единственном случае:
если используется режим socks и удаленный ресолвинг хостов через прокси.
То есть работоспособность вашей настройки в одном и том же режиме может зависеть от того,
применяет ли клиент удаленный ресолвинг. Это может быть неочевидно.
В одной программе работает, в другой - нет.
Если вы используете профиль с хостлистом , и вам нужен mss, укажите mss в профиле с хостлистом,
Методы нулевой фазы (`--mss`) могут работать по хостлисту только в двух случаях:
если используется режим socks и удаленный ресолвинг хостов через прокси, либо используется система [кэша IP](#кэш-ip) для запоминания соответствия IP->hostname.
Работоспособность вашей настройки в одном и том же режиме может зависеть от того,
применяет ли клиент удаленный ресолвинг. Это может быть неочевидно. В одной программе работает, в другой - нет.
Если вы используете профиль с хостлистом , и вам нужен mss всегда, укажите mss в профиле с хостлистом,
создайте еще один профиль без хостлиста, если его еще нет, и в нем еще раз укажите mss.
Тогда при любом раскладе будет выполняться mss.
Если вам нужен mss по хостлисту, указывайте `--mss` только в профиле с хостлистом и убедитесь в наличии любого из необходимых условий работы в таком режиме.
Используйте `curl --socks5` и `curl --socks5-hostname` для проверки вашей стратегии.
Смотрите вывод `--debug`, чтобы убедиться в правильности настроек.
@ -1506,8 +1621,10 @@ SKIP_DNSCHECK=1 - отказ от проверки DNS
SKIP_IPBLOCK=1 - отказ от тестов блокировки по порту или IP
SKIP_TPWS=1 - отказ от тестов tpws
SKIP_PKTWS=1 - отказ от тестов nfqws/dvtws/winws
PKTWS_EXTRA, TPWS_EXTRA - дополнительные параметры nfqws/dvtws/winws и tpws
PKTWS_EXTRA, TPWS_EXTRA - дополнительные параметры nfqws/dvtws/winws и tpws, указываемые после основной стратегии
PKTWS_EXTRA_1 .. PKTWS_EXTRA_9, TPWS_EXTRA_1 .. TPWS_EXTRA_9 - отдельно дополнительные параметры, содержащие пробелы
PKTWS_EXTRA_PRE - дополнительные параметры для nfqws/dvtws/winws, указываемые перед основной стратегией
PKTWS_EXTRA_PRE_1 .. PKTWS_EXTRA_PRE_9 - отдельно дополнительные параметры, содержащие пробелы
SECURE_DNS=0|1 - принудительно выключить или включить DoH
DOH_SERVERS - список URL DoH через пробел для автоматического выбора работающего сервера
DOH_SERVER - конкретный DoH URL, отказ от поиска
@ -1660,13 +1777,16 @@ nfqws начнет получать адреса пакетов из локал
Каждая опция предполагает запуск одного инстанса соответствующего демона. Все различия методов дурения
для `http`, `https`, `quic` и т.д. должны быть отражены через схему мультистратегий.
В этом смысле настройка похожа на вариант `winws` на Windows, а перенос конфигов не должен представлять больших сложностей.
Основное правило настройки перехвата - перехватывайте только необходимый минимум.
Любой перехват лишнего - это бессмысленная нагрузка на вашу систему.
Опции демонов `--ipset` использовать запрещено. Это сделано намеренно и искусственно, чтобы не поощрять простой и
работающий, но неэффективный метод на *nix системах. Используйте `ipset`-ы режима ядра.
При необходимости пишите и задействуйте `custom scripts`.
Опции демонов `--ipset` использовать нужно с умом. Не стоит перехватывать весь трафик, чтобы потом по параметру --ipset
выделить лишь горстку IP. Это будет работать, но очень неэффективно с точки зрения нагрузки на систему.
Используйте `ipset`-ы режима ядра. При необходимости пишите и задействуйте `custom scripts`.
Но если у вас и так идет работа по всем IP, и нужно написать небольшую специализацию по IP, то --ipset вполне уместен.
Настройки демонов можно для удобства писать на нескольких строках, используя двойные или одинарные кавычки.
Чтобы задействовать стандартные обновляемые хост-листы из `ipset`, используйте маркер <HOSTLIST>.
Чтобы задействовать стандартные обновляемые хост-листы из каталога `ipset`, используйте маркер <HOSTLIST>.
Он будет заменен на параметры, соответствующие режиму MODE_FILTER, и будут подставлены реально существующие файлы.
Если MODE_FILTER не предполагает стандартного хостлиста, <HOSTLIST> будет заменен на пустую строку.
Стандартные хостлисты следует вставлять в финальных стратегиях (стратегиях по умолчанию), закрывающих цепочки по
@ -1760,7 +1880,7 @@ hardware: выборочное управление включено в режи
```
`FLOWOFFLOAD=donttouch`
Параметр GETLIST указывает инсталлятору `install_easy.sh` какой скрипт дергать
Параметр `GETLIST` указывает инсталлятору `install_easy.sh` какой скрипт дергать
для обновления списка заблокированных ip или хостов.
Он же вызывается через `get_config.sh` из запланированных заданий (crontab или systemd timer).
Поместите сюда название скрипта, который будете использовать для обновления листов.
@ -1847,11 +1967,18 @@ OPENWRT_WAN4="wan4 vpn"
OPENWRT_WAN6="wan6 vpn6"
```
Параметр INIT_APPLY_FW=1 разрешает init скрипту самостоятельно применять правила iptables.\
Параметр `INIT_APPLY_FW=1` разрешает init скрипту самостоятельно применять правила iptables.\
При иных значениях или если параметр закомментирован, правила применены не будут.\
Это полезно, если у вас есть система управления фаерволом, в настройки которой и следует прикрутить правила.\
На OpenWrt неприменимо при использовании firewall3+iptables.
`FILTER_TTL_EXPIRED_ICMP=1` включает механизмы блокировки пакетов icmp time exceeded, высылаемые роутерами по пути следования пакета в ответ на исчерпание TTL/HL.
В linux соединение обрывается системой, если в ответ на первый пакет (для tcp - SYN) пришел такой icmp. Аналогичная схема имеется и в datagram сокетах.
Блокировка icmp идет исключительно за счет средств iptables/nftables.
Чтобы не трогать весь трафик, в режиме PRENAT используется connmark для пометки сеансов, над которыми поработал nfqws. В режиме POSTNAT так сделать нельзя,
поэтому помечаются все сеансы, заворачиваемые на nfqws.
Настройку лучше отключить, если вы не ожидаете проблем от icmp, тк в этом случае будет меньше ненужных вмешательств в трафик.
***Следующие настройки не актуальны для openwrt:***
Если ваша система работает как роутер, то нужно вписать названия внутренних и внешних интерфейсов:

View File

@ -1,19 +1,29 @@
# this custom script demonstrates how to launch extra nfqws instance limited by ipset. ipv4 only.
# this custom script demonstrates how to launch extra nfqws instance limited by ipset
# can override in config :
NFQWS_OPT_DESYNC_NFQWS_MY1="${NFQWS_OPT_DESYNC_NFQWS_MY1:---dpi-desync=fake --dpi-desync-repeats=6 --dpi-desync-any-protocol}"
NFQWS_MY1_PORTS=${NFQWS_MY1_PORTS:-6000-6009}
NFQWS_MY1_SUBNETS="${NFQWS_MY1_SUBNETS:-34.0.48.0/21 34.0.56.0/23 34.0.59.0/24 34.0.60.0/24 34.0.62.0/23}"
NFQWS_MY1_OPT="${NFQWS_MY1_OPT:---filter-udp=* --dpi-desync=fake --dpi-desync-repeats=6 --dpi-desync-any-protocol --new --filter-tcp=* --dpi-desync=multisplit}"
NFQWS_MY1_SUBNETS4="${NFQWS_MY1_SUBNETS4:-173.194.0.0/16 108.177.0.0/17 74.125.0.0/16 64.233.160.0/19 172.217.0.0/16}"
NFQWS_MY1_SUBNETS6="${NFQWS_MY1_SUBNETS6:-2a00:1450::/29}"
NFQWS_MY1_PORTS_TCP=${NFQWS_MY1_PORTS_TCP:-$NFQWS_PORTS_TCP}
NFQWS_MY1_PORTS_UDP=${NFQWS_MY1_PORTS_UDP:-$NFQWS_PORTS_UDP}
NFQWS_MY1_TCP_PKT_OUT=${NFQWS_MY1_TCP_PKT_OUT:-$NFQWS_TCP_PKT_OUT}
NFQWS_MY1_UDP_PKT_OUT=${NFQWS_MY1_UDP_PKT_OUT:-$NFQWS_UDP_PKT_OUT}
NFQWS_MY1_TCP_PKT_IN=${NFQWS_MY1_TCP_PKT_IN:-$NFQWS_TCP_PKT_IN}
NFQWS_MY1_UDP_PKT_IN=${NFQWS_MY1_UDP_PKT_IN:-$NFQWS_UDP_PKT_IN}
NFQWS_MY1_IPSET_SIZE=${NFQWS_MY1_IPSET_SIZE:-4096}
NFQWS_MY1_IPSET_OPT="${NFQWS_MY1_IPSET_OPT:-hash:net hashsize 8192 maxelem $NFQWS_MY1_IPSET_SIZE}"
alloc_dnum DNUM_NFQWS_MY1
alloc_qnum QNUM_NFQWS_MY1
NFQWS_MY1_SET_NAME=my1nfqws4
NFQWS_MY1_NAME4=my1nfqws4
NFQWS_MY1_NAME6=my1nfqws6
zapret_custom_daemons()
{
# $1 - 1 - run, 0 - stop
local opt="--qnum=$QNUM_NFQWS_MY1 $NFQWS_OPT_DESYNC_NFQWS_MY1"
local opt="--qnum=$QNUM_NFQWS_MY1 $NFQWS_MY1_OPT"
do_nfqws $1 $DNUM_NFQWS_MY1 "$opt"
}
@ -21,54 +31,114 @@ zapret_custom_firewall()
{
# $1 - 1 - run, 0 - stop
local f
local first_packets_only="$ipt_connbytes 1:3"
local NFQWS_MY1_PORTS_IPT=$(replace_char - : $NFQWS_MY1_PORTS)
local dest_set="-m set --match-set $NFQWS_MY1_SET_NAME dst"
local subnet
local f4 f6 subnet
local NFQWS_MY1_PORTS_TCP=$(replace_char - : $NFQWS_MY1_PORTS_TCP)
local NFQWS_MY1_PORTS_UDP=$(replace_char - : $NFQWS_MY1_PORTS_UDP)
local DISABLE_IPV6=1
[ "$1" = 1 ] && {
ipset create $NFQWS_MY1_SET_NAME hash:net hashsize 8192 maxelem 4096 2>/dev/null
ipset flush $NFQWS_MY1_SET_NAME
for subnet in $NFQWS_MY1_SUBNETS; do
echo add $NFQWS_MY1_SET_NAME $subnet
[ "$1" = 1 -a "$DISABLE_IPV4" != 1 ] && {
ipset create $NFQWS_MY1_NAME4 $NFQWS_MY1_IPSET_OPT family inet 2>/dev/null
ipset flush $NFQWS_MY1_NAME4
for subnet in $NFQWS_MY1_SUBNETS4; do
echo add $NFQWS_MY1_NAME4 $subnet
done | ipset -! restore
}
[ "$1" = 1 -a "$DISABLE_IPV6" != 1 ] && {
ipset create $NFQWS_MY1_NAME6 $NFQWS_MY1_IPSET_OPT family inet6 2>/dev/null
ipset flush $NFQWS_MY1_NAME6
for subnet in $NFQWS_MY1_SUBNETS6; do
echo add $NFQWS_MY1_NAME6 $subnet
done | ipset -! restore
}
f="-p udp -m multiport --dports $NFQWS_MY1_PORTS_IPT"
fw_nfqws_post $1 "$f $first_packets_only $dest_set" "" $QNUM_NFQWS_MY1
[ -n "$NFQWS_MY1_PORTS_TCP" ] && {
[ -n "$NFQWS_MY1_TCP_PKT_OUT" -a "$NFQWS_MY1_TCP_PKT_OUT" != 0 ] && {
f4="-p tcp -m multiport --dports $NFQWS_MY1_PORTS_TCP $ipt_connbytes 1:$NFQWS_MY1_TCP_PKT_OUT -m set --match-set"
f6="$f4 $NFQWS_MY1_NAME6 dst"
f4="$f4 $NFQWS_MY1_NAME4 dst"
fw_nfqws_post $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
[ -n "$NFQWS_MY1_TCP_PKT_IN" -a "$NFQWS_MY1_TCP_PKT_IN" != 0 ] && {
f4="-p tcp -m multiport --sports $NFQWS_MY1_PORTS_TCP $ipt_connbytes 1:$NFQWS_MY1_TCP_PKT_IN -m set --match-set"
f6="$f4 $NFQWS_MY1_NAME6 src"
f4="$f4 $NFQWS_MY1_NAME4 src"
fw_nfqws_pre $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
}
[ -n "$NFQWS_MY1_PORTS_UDP" ] && {
[ -n "$NFQWS_MY1_UDP_PKT_OUT" -a "$NFQWS_MY1_UDP_PKT_OUT" != 0 ] && {
f4="-p udp -m multiport --dports $NFQWS_MY1_PORTS_UDP $ipt_connbytes 1:$NFQWS_MY1_UDP_PKT_OUT -m set --match-set"
f6="$f4 $NFQWS_MY1_NAME6 dst"
f4="$f4 $NFQWS_MY1_NAME4 dst"
fw_nfqws_post $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
[ -n "$NFQWS_MY1_UDP_PKT_IN" -a "$NFQWS_MY1_UDP_PKT_IN" != 0 ] && {
f4="-p udp -m multiport --sports $NFQWS_MY1_PORTS_UDP $ipt_connbytes 1:$NFQWS_MY1_UDP_PKT_IN -m set --match-set"
f6="$f4 $NFQWS_MY1_NAME6 src"
f4="$f4 $NFQWS_MY1_NAME4 src"
fw_nfqws_pre $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
}
[ "$1" = 1 ] || {
ipset destroy $NFQWS_MY1_SET_NAME 2>/dev/null
ipset destroy $NFQWS_MY1_NAME4 2>/dev/null
ipset destroy $NFQWS_MY1_NAME6 2>/dev/null
}
}
zapret_custom_firewall_nft()
{
# stop logic is not required
local f4 f6 subnets
local first_packets_only="$nft_connbytes 1-$NFQWS_MY1_PKT_OUT"
local f
local first_packets_only="$nft_connbytes 1-3"
local dest_set="ip daddr @$NFQWS_MY1_SET_NAME"
local subnets
[ "$DISABLE_IPV4" != 1 ] && {
make_comma_list subnets $NFQWS_MY1_SUBNETS4
nft_create_set $NFQWS_MY1_NAME4 "type ipv4_addr; size $NFQWS_MY1_IPSET_SIZE; auto-merge; flags interval;"
nft_flush_set $NFQWS_MY1_NAME4
nft_add_set_element $NFQWS_MY1_NAME4 "$subnets"
}
[ "$DISABLE_IPV6" != 1 ] && {
make_comma_list subnets $NFQWS_MY1_SUBNETS6
nft_create_set $NFQWS_MY1_NAME6 "type ipv6_addr; size $NFQWS_MY1_IPSET_SIZE; auto-merge; flags interval;"
nft_flush_set $NFQWS_MY1_NAME6
nft_add_set_element $NFQWS_MY1_NAME6 "$subnets"
}
local DISABLE_IPV6=1
make_comma_list subnets $NFQWS_MY1_SUBNETS
nft_create_set $NFQWS_MY1_SET_NAME "type ipv4_addr; size 4096; auto-merge; flags interval;"
nft_flush_set $NFQWS_MY1_SET_NAME
nft_add_set_element $NFQWS_MY1_SET_NAME "$subnets"
f="udp dport {$NFQWS_MY1_PORTS}"
nft_fw_nfqws_post "$f $first_packets_only $dest_set" "" $QNUM_NFQWS_MY1
[ -n "$NFQWS_MY1_PORTS_TCP" ] && {
[ -n "$NFQWS_MY1_TCP_PKT_OUT" -a "$NFQWS_MY1_TCP_PKT_OUT" != 0 ] && {
f4="tcp dport {$NFQWS_MY1_PORTS_TCP} $(nft_first_packets $NFQWS_MY1_TCP_PKT_OUT)"
f6="$f4 ip6 daddr @$NFQWS_MY1_NAME6"
f4="$f4 ip daddr @$NFQWS_MY1_NAME4"
nft_fw_nfqws_post $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
[ -n "$NFQWS_MY1_TCP_PKT_IN" -a "$NFQWS_MY1_TCP_PKT_IN" != 0 ] && {
f4="tcp sport {$NFQWS_MY1_PORTS_TCP} $(nft_first_packets $NFQWS_MY1_TCP_PKT_IN)"
f6="$f4 ip6 saddr @$NFQWS_MY1_NAME6"
f4="$f4 ip saddr @$NFQWS_MY1_NAME4"
nft_fw_nfqws_pre $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
}
[ -n "$NFQWS_MY1_PORTS_UDP" ] && {
[ -n "$NFQWS_MY1_UDP_PKT_OUT" -a "$NFQWS_MY1_UDP_PKT_OUT" != 0 ] && {
f4="udp dport {$NFQWS_MY1_PORTS_UDP} $(nft_first_packets $NFQWS_MY1_UDP_PKT_OUT)"
f6="$f4 ip6 daddr @$NFQWS_MY1_NAME6"
f4="$f4 ip daddr @$NFQWS_MY1_NAME4"
nft_fw_nfqws_post $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
[ -n "$NFQWS_MY1_UDP_PKT_IN" -a "$NFQWS_MY1_UDP_PKT_IN" != 0 ] && {
f4="udp sport {$NFQWS_MY1_PORTS_UDP} $(nft_first_packets $NFQWS_MY1_UDP_PKT_IN)"
f6="$f4 ip6 saddr @$NFQWS_MY1_NAME6"
f4="$f4 ip saddr @$NFQWS_MY1_NAME4"
nft_fw_nfqws_pre $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
}
}
zapret_custom_firewall_nft_flush()
{
# this function is called after all nft fw rules are deleted
# however sets are not deleted. it's desired to clear sets here.
nft_del_set $NFQWS_MY1_SET_NAME 2>/dev/null
nft_del_set $NFQWS_MY1_NAME4 2>/dev/null
nft_del_set $NFQWS_MY1_NAME6 2>/dev/null
}

View File

@ -28,7 +28,6 @@ zapret_custom_firewall()
local f4 f6 subnet
local PORTS_IPT=$(replace_char - : $TPWS_MY1_PORTS)
local dest_set="-m set --match-set $TPWS_MY1_NAME4 dst"
[ "$1" = 1 -a "$DISABLE_IPV4" != 1 ] && {
ipset create $TPWS_MY1_NAME4 $TPWS_MY1_IPSET_OPT family inet 2>/dev/null
@ -58,7 +57,7 @@ zapret_custom_firewall()
zapret_custom_firewall_nft()
{
local f4 f6 subnet
local f4 f6 subnets
[ "$DISABLE_IPV4" != 1 ] && {
make_comma_list subnets $TPWS_MY1_SUBNETS4

View File

@ -1,7 +1,7 @@
# Example systemd service unit for nfqws. Adjust for your installation.
# WARNING ! This unit requires to compile nfqws using `make systemd`
# WARNING ! This makefile target enabled special systemd notify support.
# WARNING ! This makefile target enables special systemd notify support.
# PREPARE
# install build depends

View File

@ -1,7 +1,7 @@
# Example systemd service unit for tpws. Adjust for your installation.
# WARNING ! This unit requires to compile tpws using `make systemd`
# WARNING ! This makefile target enabled special systemd notify support.
# WARNING ! This makefile target enables special systemd notify support.
# PREPARE
# install build depends

View File

@ -225,6 +225,28 @@ static void exithelp(void)
#define PRINT_VER printf("self-built version %s %s\n\n", __DATE__, __TIME__)
#endif
enum opt_indices {
IDX_HELP,
IDX_H,
IDX_4,
IDX_6,
IDX_PREFIX_LENGTH,
IDX_V4_THRESHOLD,
IDX_V6_THRESHOLD,
IDX_LAST,
};
static const struct option long_options[] = {
[IDX_HELP] = {"help", no_argument, 0, 0},
[IDX_H] = {"h", no_argument, 0, 0},
[IDX_4] = {"4", no_argument, 0, 0},
[IDX_6] = {"6", no_argument, 0, 0},
[IDX_PREFIX_LENGTH] = {"prefix-length", required_argument, 0, 0},
[IDX_V4_THRESHOLD] = {"v4-threshold", required_argument, 0, 0},
[IDX_V6_THRESHOLD] = {"v6-threshold", required_argument, 0, 0},
[IDX_LAST] = {NULL, 0, NULL, 0},
};
static void parse_params(int argc, char *argv[])
{
int option_index = 0;
@ -236,33 +258,23 @@ static void parse_params(int argc, char *argv[])
params.pctdiv = DEFAULT_PCTDIV;
params.v6_threshold = DEFAULT_V6_THRESHOLD;
const struct option long_options[] = {
{ "help",no_argument,0,0 },// optidx=0
{ "h",no_argument,0,0 },// optidx=1
{ "4",no_argument,0,0 },// optidx=2
{ "6",no_argument,0,0 },// optidx=3
{ "prefix-length",required_argument,0,0 },// optidx=4
{ "v4-threshold",required_argument,0,0 },// optidx=5
{ "v6-threshold",required_argument,0,0 },// optidx=6
{ NULL,0,NULL,0 }
};
while ((v = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1)
{
if (v) exithelp();
switch (option_index)
{
case 0:
case 1:
case IDX_HELP:
case IDX_H:
PRINT_VER;
exithelp();
break;
case 2:
case IDX_4:
params.ipv6 = false;
break;
case 3:
case IDX_6:
params.ipv6 = true;
break;
case 4:
case IDX_PREFIX_LENGTH:
i = sscanf(optarg,"%u-%u",&plen1,&plen2);
if (i == 1) plen2 = plen1;
if (i<=0 || plen2<plen1 || !plen1 || !plen2)
@ -271,7 +283,7 @@ static void parse_params(int argc, char *argv[])
exit(1);
}
break;
case 5:
case IDX_V4_THRESHOLD:
i = sscanf(optarg, "%u/%u", &params.pctmult, &params.pctdiv);
if (i!=2 || params.pctdiv<2 || params.pctmult<1 || params.pctmult>=params.pctdiv)
{
@ -279,7 +291,7 @@ static void parse_params(int argc, char *argv[])
exit(1);
}
break;
case 6:
case IDX_V6_THRESHOLD:
i = sscanf(optarg, "%u", &params.v6_threshold);
if (i != 1 || params.v6_threshold<1)
{

View File

@ -467,25 +467,38 @@ static void exithelp(void)
#define PRINT_VER printf("self-built version %s %s\n\n", __DATE__, __TIME__)
#endif
enum opt_indices {
IDX_HELP,
IDX_THREADS,
IDX_FAMILY,
IDX_VERBOSE,
IDX_STATS,
IDX_LOG_RESOLVED,
IDX_LOG_FAILED,
IDX_DNS_MAKE_QUERY,
IDX_DNS_PARSE_QUERY,
IDX_LAST,
};
static const struct option long_options[] = {
[IDX_HELP] = {"help", no_argument, 0, 0},
[IDX_THREADS] = {"threads", required_argument, 0, 0},
[IDX_FAMILY] = {"family", required_argument, 0, 0},
[IDX_VERBOSE] = {"verbose", no_argument, 0, 0},
[IDX_STATS] = {"stats", required_argument, 0, 0},
[IDX_LOG_RESOLVED] = {"log-resolved", required_argument, 0, 0},
[IDX_LOG_FAILED] = {"log-failed", required_argument, 0, 0},
[IDX_DNS_MAKE_QUERY] = {"dns-make-query", required_argument, 0, 0},
[IDX_DNS_PARSE_QUERY] = {"dns-parse-query", no_argument, 0, 0},
[IDX_LAST] = {NULL, 0, NULL, 0},
};
int main(int argc, char **argv)
{
int r, v, option_index = 0;
char fn1[256],fn2[256];
char dom[256];
static const struct option long_options[] = {
{"help",no_argument,0,0}, // optidx=0
{"threads",required_argument,0,0}, // optidx=1
{"family",required_argument,0,0}, // optidx=2
{"verbose",no_argument,0,0}, // optidx=3
{"stats",required_argument,0,0}, // optidx=4
{"log-resolved",required_argument,0,0}, // optidx=5
{"log-failed",required_argument,0,0}, // optidx=6
{"dns-make-query",required_argument,0,0}, // optidx=7
{"dns-parse-query",no_argument,0,0}, // optidx=8
{NULL,0,NULL,0}
};
memset(&glob, 0, sizeof(glob));
*fn1 = *fn2 = *dom = 0;
glob.family = FAMILY4;
@ -495,11 +508,11 @@ int main(int argc, char **argv)
if (v) exithelp();
switch (option_index)
{
case 0: /* help */
case IDX_HELP:
PRINT_VER;
exithelp();
break;
case 1: /* threads */
case IDX_THREADS:
glob.threads = optarg ? atoi(optarg) : 0;
if (glob.threads <= 0 || glob.threads > 100)
{
@ -507,7 +520,7 @@ int main(int argc, char **argv)
return 1;
}
break;
case 2: /* family */
case IDX_FAMILY:
if (!strcmp(optarg, "4"))
glob.family = FAMILY4;
else if (!strcmp(optarg, "6"))
@ -520,25 +533,25 @@ int main(int argc, char **argv)
return 1;
}
break;
case 3: /* verbose */
case IDX_VERBOSE:
glob.verbose = '\1';
break;
case 4: /* stats */
case IDX_STATS:
glob.stats_every = optarg ? atoi(optarg) : 0;
break;
case 5: /* log-resolved */
case IDX_LOG_RESOLVED:
strncpy(fn1,optarg,sizeof(fn1));
fn1[sizeof(fn1)-1] = 0;
break;
case 6: /* log-failed */
case IDX_LOG_FAILED:
strncpy(fn2,optarg,sizeof(fn2));
fn2[sizeof(fn2)-1] = 0;
break;
case 7: /* dns-make-query */
case IDX_DNS_MAKE_QUERY:
strncpy(dom,optarg,sizeof(dom));
dom[sizeof(dom)-1] = 0;
break;
case 8: /* dns-parse-query */
case IDX_DNS_PARSE_QUERY:
return dns_parse_query();
}
}

View File

@ -143,8 +143,11 @@ static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr
}
else if (tcp_synack_segment(tcphdr))
{
if (t->state!=SYN) ConntrackReInitTrack(t); // erase current entry
if (!t->seq0) t->seq0 = ntohl(tcphdr->th_ack)-1;
// ignore SA dups
uint32_t seq0 = ntohl(tcphdr->th_ack)-1;
if (t->state!=SYN && t->seq0!=seq0)
ConntrackReInitTrack(t); // erase current entry
if (!t->seq0) t->seq0 = seq0;
t->ack0 = ntohl(tcphdr->th_seq);
}
else if (tcphdr->th_flags & (TH_FIN|TH_RST))
@ -338,8 +341,8 @@ void ConntrackPoolDump(const t_conntrack *p)
printf("rseq=%u pos_orig=%u rack=%u pos_reply=%u",
t->track.seq_last, t->track.pos_orig,
t->track.ack_last, t->track.pos_reply);
printf(" req_retrans=%u cutoff=%u wss_cutoff=%u d_cutoff=%u hostname=%s l7proto=%s\n",
t->track.req_retrans_counter, t->track.b_cutoff, t->track.b_wssize_cutoff, t->track.b_desync_cutoff, t->track.hostname, l7proto_str(t->track.l7proto));
printf(" req_retrans=%u cutoff=%u wss_cutoff=%u desync_cutoff=%u dup_cutoff=%u orig_cutoff=%u hostname=%s l7proto=%s\n",
t->track.req_retrans_counter, t->track.b_cutoff, t->track.b_wssize_cutoff, t->track.b_desync_cutoff, t->track.b_dup_cutoff, t->track.b_orig_mod_cutoff, t->track.hostname, l7proto_str(t->track.l7proto));
};
}

View File

@ -77,14 +77,16 @@ typedef struct
bool req_seq_present,req_seq_finalized,req_seq_abandoned;
uint32_t req_seq_start,req_seq_end; // sequence interval of the request (to track retransmissions)
uint8_t incoming_ttl, autottl;
uint8_t incoming_ttl, desync_autottl, orig_autottl, dup_autottl;
bool b_autottl_discovered;
bool b_cutoff; // mark for deletion
bool b_wssize_cutoff, b_desync_cutoff;
bool b_wssize_cutoff, b_desync_cutoff, b_dup_cutoff, b_orig_mod_cutoff;
t_l7proto l7proto;
bool l7proto_discovered;
char *hostname;
bool hostname_discovered;
bool hostname_ah_check; // should perform autohostlist checks
t_reassemble reasm_orig;

View File

@ -38,6 +38,11 @@ uint32_t net16_add(uint16_t netorder_value, uint16_t cpuorder_increment)
return htons(ntohs(netorder_value)+cpuorder_increment);
}
bool ip_has_df(const struct ip *ip)
{
return ip && !!(ntohs(ip->ip_off) & IP_DF);
}
uint8_t *tcp_find_option(struct tcphdr *tcp, uint8_t kind)
{
uint8_t *t = (uint8_t*)(tcp+1);
@ -83,10 +88,22 @@ bool tcp_has_fastopen(const struct tcphdr *tcp)
opt = tcp_find_option((struct tcphdr*)tcp, 254);
return opt && opt[1]>=4 && opt[2]==0xF9 && opt[3]==0x89;
}
uint16_t tcp_find_mss(struct tcphdr *tcp)
{
uint8_t *t = tcp_find_option(tcp,2);
return (t && t[1]==4) ? *(uint16_t*)(t+2) : 0;
}
bool tcp_has_sack(struct tcphdr *tcp)
{
uint8_t *t = tcp_find_option(tcp,4);
return !!t;
}
// n prefix (nsport, nwsize) means network byte order
static void fill_tcphdr(
struct tcphdr *tcp, uint32_t fooling, uint8_t tcp_flags,
bool sack,
uint16_t nmss,
uint32_t nseq, uint32_t nack_seq,
uint16_t nsport, uint16_t ndport,
uint16_t nwsize, uint8_t scale_factor,
@ -111,20 +128,32 @@ static void fill_tcphdr(
tcp->th_seq = nseq;
tcp->th_ack = nack_seq;
}
tcp->th_off = 5;
tcp->th_off = 5;
if ((fooling & FOOL_DATANOACK) && !(tcp_flags & (TH_SYN|TH_RST)) && data_len)
tcp_flags &= ~TH_ACK;
*((uint8_t*)tcp+13)= tcp_flags;
tcp->th_win = nwsize;
if (nmss)
{
tcpopt[t++] = 2; // kind
tcpopt[t++] = 4; // len
*(uint16_t*)(tcpopt+t) = nmss;
t+=2;
}
if (sack)
{
tcpopt[t++] = 4; // kind
tcpopt[t++] = 2; // len
}
if (fooling & FOOL_MD5SIG)
{
tcpopt[0] = 19; // kind
tcpopt[1] = 18; // len
*(uint32_t*)(tcpopt+2)=random();
*(uint32_t*)(tcpopt+6)=random();
*(uint32_t*)(tcpopt+10)=random();
*(uint32_t*)(tcpopt+14)=random();
t=18;
tcpopt[t] = 19; // kind
tcpopt[t+1] = 18; // len
*(uint32_t*)(tcpopt+t+2)=random();
*(uint32_t*)(tcpopt+t+6)=random();
*(uint32_t*)(tcpopt+t+10)=random();
*(uint32_t*)(tcpopt+t+14)=random();
t+=18;
}
if (timestamps || (fooling & FOOL_TS))
{
@ -145,10 +174,12 @@ static void fill_tcphdr(
tcp->th_off += t>>2;
tcp->th_sum = 0;
}
static uint16_t tcpopt_len(uint32_t fooling, const uint32_t *timestamps, uint8_t scale_factor)
static uint16_t tcpopt_len(bool sack, bool mss, uint32_t fooling, const uint32_t *timestamps, uint8_t scale_factor)
{
uint16_t t=0;
if (fooling & FOOL_MD5SIG) t=18;
if (sack) t+=2;
if (mss) t+=4;
if (fooling & FOOL_MD5SIG) t+=18;
if ((fooling & FOOL_TS) || timestamps) t+=10;
if (scale_factor!=SCALE_NONE) t+=3;
return (t+3)&~3;
@ -163,11 +194,11 @@ static void fill_udphdr(struct udphdr *udp, uint16_t nsport, uint16_t ndport, ui
udp->uh_sum = 0;
}
static void fill_iphdr(struct ip *ip, const struct in_addr *src, const struct in_addr *dst, uint16_t pktlen, uint8_t proto, uint8_t ttl, uint8_t tos, uint16_t ip_id)
static void fill_iphdr(struct ip *ip, const struct in_addr *src, const struct in_addr *dst, uint16_t pktlen, uint8_t proto, bool DF, uint8_t ttl, uint8_t tos, uint16_t ip_id)
{
ip->ip_tos = tos;
ip->ip_sum = 0;
ip->ip_off = 0;
ip->ip_off = DF ? htons(IP_DF) : 0;
ip->ip_v = 4;
ip->ip_hl = 5;
ip->ip_len = htons(pktlen);
@ -190,10 +221,13 @@ static void fill_ip6hdr(struct ip6_hdr *ip6, const struct in6_addr *src, const s
bool prepare_tcp_segment4(
const struct sockaddr_in *src, const struct sockaddr_in *dst,
uint8_t tcp_flags,
bool sack,
uint16_t nmss,
uint32_t nseq, uint32_t nack_seq,
uint16_t nwsize,
uint8_t scale_factor,
uint32_t *timestamps,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@ -203,7 +237,7 @@ bool prepare_tcp_segment4(
const void *data, uint16_t len,
uint8_t *buf, size_t *buflen)
{
uint16_t tcpoptlen = tcpopt_len(fooling,timestamps,scale_factor);
uint16_t tcpoptlen = tcpopt_len(sack,!!nmss,fooling,timestamps,scale_factor);
uint16_t ip_payload_len = sizeof(struct tcphdr) + tcpoptlen + len;
uint16_t pktlen = sizeof(struct ip) + ip_payload_len;
if (pktlen>*buflen) return false;
@ -212,12 +246,12 @@ bool prepare_tcp_segment4(
struct tcphdr *tcp = (struct tcphdr*)(ip+1);
uint8_t *payload = (uint8_t*)(tcp+1)+tcpoptlen;
fill_iphdr(ip, &src->sin_addr, &dst->sin_addr, pktlen, IPPROTO_TCP, ttl, tos, ip_id);
fill_tcphdr(tcp,fooling,tcp_flags,nseq,nack_seq,src->sin_port,dst->sin_port,nwsize,scale_factor,timestamps,badseq_increment,badseq_ack_increment,len);
fill_iphdr(ip, &src->sin_addr, &dst->sin_addr, pktlen, IPPROTO_TCP, DF, ttl, tos, ip_id);
fill_tcphdr(tcp,fooling,tcp_flags,sack,nmss,nseq,nack_seq,src->sin_port,dst->sin_port,nwsize,scale_factor,timestamps,badseq_increment,badseq_ack_increment,len);
memcpy(payload,data,len);
tcp4_fix_checksum(tcp,ip_payload_len,&ip->ip_src,&ip->ip_dst);
if (fooling & FOOL_BADSUM) tcp->th_sum^=htons(0xBEAF);
if (fooling & FOOL_BADSUM) tcp->th_sum^=(uint16_t)(1+random()%0xFFFF);
*buflen = pktlen;
return true;
@ -226,6 +260,8 @@ bool prepare_tcp_segment4(
bool prepare_tcp_segment6(
const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst,
uint8_t tcp_flags,
bool sack,
uint16_t nmss,
uint32_t nseq, uint32_t nack_seq,
uint16_t nwsize,
uint8_t scale_factor,
@ -238,7 +274,7 @@ bool prepare_tcp_segment6(
const void *data, uint16_t len,
uint8_t *buf, size_t *buflen)
{
uint16_t tcpoptlen = tcpopt_len(fooling,timestamps,scale_factor);
uint16_t tcpoptlen = tcpopt_len(sack,!!nmss,fooling,timestamps,scale_factor);
uint16_t transport_payload_len = sizeof(struct tcphdr) + tcpoptlen + len;
uint16_t ip_payload_len = transport_payload_len +
8*!!((fooling & (FOOL_HOPBYHOP|FOOL_HOPBYHOP2))==FOOL_HOPBYHOP) +
@ -297,11 +333,11 @@ bool prepare_tcp_segment6(
uint8_t *payload = (uint8_t*)(tcp+1)+tcpoptlen;
fill_ip6hdr(ip6, &src->sin6_addr, &dst->sin6_addr, ip_payload_len, proto, ttl, flow_label);
fill_tcphdr(tcp,fooling,tcp_flags,nseq,nack_seq,src->sin6_port,dst->sin6_port,nwsize,scale_factor,timestamps,badseq_increment,badseq_ack_increment,len);
fill_tcphdr(tcp,fooling,tcp_flags,sack,nmss,nseq,nack_seq,src->sin6_port,dst->sin6_port,nwsize,scale_factor,timestamps,badseq_increment,badseq_ack_increment,len);
memcpy(payload,data,len);
tcp6_fix_checksum(tcp,transport_payload_len,&ip6->ip6_src,&ip6->ip6_dst);
if (fooling & FOOL_BADSUM) tcp->th_sum^=htons(0xBEAF);
if (fooling & FOOL_BADSUM) tcp->th_sum^=(1+random()%0xFFFF);
*buflen = pktlen;
return true;
@ -310,10 +346,13 @@ bool prepare_tcp_segment6(
bool prepare_tcp_segment(
const struct sockaddr *src, const struct sockaddr *dst,
uint8_t tcp_flags,
bool sack,
uint16_t nmss,
uint32_t nseq, uint32_t nack_seq,
uint16_t nwsize,
uint8_t scale_factor,
uint32_t *timestamps,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@ -325,9 +364,9 @@ bool prepare_tcp_segment(
uint8_t *buf, size_t *buflen)
{
return (src->sa_family==AF_INET && dst->sa_family==AF_INET) ?
prepare_tcp_segment4((struct sockaddr_in *)src,(struct sockaddr_in *)dst,tcp_flags,nseq,nack_seq,nwsize,scale_factor,timestamps,ttl,tos,ip_id,fooling,badseq_increment,badseq_ack_increment,data,len,buf,buflen) :
prepare_tcp_segment4((struct sockaddr_in *)src,(struct sockaddr_in *)dst,tcp_flags,sack,nmss,nseq,nack_seq,nwsize,scale_factor,timestamps,DF,ttl,tos,ip_id,fooling,badseq_increment,badseq_ack_increment,data,len,buf,buflen) :
(src->sa_family==AF_INET6 && dst->sa_family==AF_INET6) ?
prepare_tcp_segment6((struct sockaddr_in6 *)src,(struct sockaddr_in6 *)dst,tcp_flags,nseq,nack_seq,nwsize,scale_factor,timestamps,ttl,flow_label,fooling,badseq_increment,badseq_ack_increment,data,len,buf,buflen) :
prepare_tcp_segment6((struct sockaddr_in6 *)src,(struct sockaddr_in6 *)dst,tcp_flags,sack,nmss,nseq,nack_seq,nwsize,scale_factor,timestamps,ttl,flow_label,fooling,badseq_increment,badseq_ack_increment,data,len,buf,buflen) :
false;
}
@ -335,6 +374,7 @@ bool prepare_tcp_segment(
// padlen<0 means payload shrinking
bool prepare_udp_segment4(
const struct sockaddr_in *src, const struct sockaddr_in *dst,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@ -361,7 +401,7 @@ bool prepare_udp_segment4(
uint8_t *payload = (uint8_t*)(udp+1);
fill_iphdr(ip, &src->sin_addr, &dst->sin_addr, pktlen, IPPROTO_UDP, ttl, tos, ip_id);
fill_iphdr(ip, &src->sin_addr, &dst->sin_addr, pktlen, IPPROTO_UDP, DF, ttl, tos, ip_id);
fill_udphdr(udp, src->sin_port, dst->sin_port, datalen);
memcpy(payload,data,len);
@ -370,7 +410,7 @@ bool prepare_udp_segment4(
else
memset(payload+len,0,padlen);
udp4_fix_checksum(udp,ip_payload_len,&ip->ip_src,&ip->ip_dst);
if (fooling & FOOL_BADSUM) udp->uh_sum^=htons(0xBEAF);
if (fooling & FOOL_BADSUM) udp->uh_sum^=(1+random()%0xFFFF);
*buflen = pktlen;
return true;
@ -459,13 +499,14 @@ bool prepare_udp_segment6(
else
memset(payload+len,0,padlen);
udp6_fix_checksum(udp,transport_payload_len,&ip6->ip6_src,&ip6->ip6_dst);
if (fooling & FOOL_BADSUM) udp->uh_sum^=htons(0xBEAF);
if (fooling & FOOL_BADSUM) udp->uh_sum^=(1+random()%0xFFFF);
*buflen = pktlen;
return true;
}
bool prepare_udp_segment(
const struct sockaddr *src, const struct sockaddr *dst,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@ -477,7 +518,7 @@ bool prepare_udp_segment(
uint8_t *buf, size_t *buflen)
{
return (src->sa_family==AF_INET && dst->sa_family==AF_INET) ?
prepare_udp_segment4((struct sockaddr_in *)src,(struct sockaddr_in *)dst,ttl,tos,ip_id,fooling,padding,padding_size,padlen,data,len,buf,buflen) :
prepare_udp_segment4((struct sockaddr_in *)src,(struct sockaddr_in *)dst,DF,ttl,tos,ip_id,fooling,padding,padding_size,padlen,data,len,buf,buflen) :
(src->sa_family==AF_INET6 && dst->sa_family==AF_INET6) ?
prepare_udp_segment6((struct sockaddr_in6 *)src,(struct sockaddr_in6 *)dst,ttl,flow_label,fooling,padding,padding_size,padlen,data,len,buf,buflen) :
false;
@ -601,10 +642,29 @@ bool ip_frag(
return false;
}
void rewrite_ttl(struct ip *ip, struct ip6_hdr *ip6, uint8_t ttl)
bool rewrite_ttl(struct ip *ip, struct ip6_hdr *ip6, uint8_t ttl)
{
if (ip) ip->ip_ttl = ttl;
if (ip6) ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = ttl;
if (ttl)
{
if (ip)
{
if (ip->ip_ttl!=ttl)
{
ip->ip_ttl = ttl;
ip4_fix_checksum(ip);
return true;
}
}
else if (ip6)
{
if (ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim!=ttl)
{
ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = ttl;
return true;
}
}
}
return false;
}
@ -1772,16 +1832,15 @@ bool rawsend_queue(struct rawpacket_tailhead *q)
}
// return guessed fake ttl value. 0 means unsuccessfull, should not perform autottl fooling
// ttl = TTL of incoming packet
uint8_t autottl_guess(uint8_t ttl, const autottl *attl)
uint8_t hop_count_guess(uint8_t ttl)
{
uint8_t orig, path, fake;
// 18.65.168.125 ( cloudfront ) 255
// 157.254.246.178 128
// 1.1.1.1 64
// guess original ttl. consider path lengths less than 32 hops
uint8_t orig;
if (ttl>223)
orig=255;
else if (ttl<128 && ttl>96)
@ -1791,13 +1850,21 @@ uint8_t autottl_guess(uint8_t ttl, const autottl *attl)
else
return 0;
path = orig - ttl;
return orig - ttl;
}
// return guessed fake ttl value. 0 means unsuccessfull, should not perform autottl fooling
uint8_t autottl_eval(uint8_t hop_count, const autottl *attl)
{
uint8_t fake;
int d;
fake = path > attl->delta ? path - attl->delta : attl->min;
if (fake<attl->min) fake=attl->min;
else if (fake>attl->max) fake=attl->max;
d = (int)hop_count + attl->delta;
if (d<attl->min) fake=attl->min;
else if (d>attl->max) fake=attl->max;
else fake=(uint8_t)d;
if (fake>=path) return 0;
if (attl->delta<0 && fake>=hop_count || attl->delta>=0 && fake<hop_count)
return 0;
return fake;
}
@ -1849,15 +1916,15 @@ void verdict_tcp_csum_fix(uint8_t verdict, struct tcphdr *tcphdr, size_t transpo
{
if (!(verdict & VERDICT_NOCSUM))
{
#ifdef __CYGWIN__
// always fix csum for windivert. original can be partial or bad
#ifndef __CYGWIN__
#ifdef __FreeBSD__
if ((verdict & VERDICT_MASK)!=VERDICT_DROP)
#elif defined(__FreeBSD__)
// FreeBSD tend to pass ipv6 frames with wrong checksum
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY || ip6hdr)
#else
// if original packet was tampered earlier it needs checksum fixed
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY)
#endif
#endif
tcp_fix_checksum(tcphdr,transport_len,ip,ip6hdr);
}
@ -1866,15 +1933,15 @@ void verdict_udp_csum_fix(uint8_t verdict, struct udphdr *udphdr, size_t transpo
{
if (!(verdict & VERDICT_NOCSUM))
{
#ifdef __CYGWIN__
// always fix csum for windivert. original can be partial or bad
#ifndef __CYGWIN__
#ifdef __FreeBSD__
if ((verdict & VERDICT_MASK)!=VERDICT_DROP)
#elif defined(__FreeBSD__)
// FreeBSD tend to pass ipv6 frames with wrong checksum
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY || ip6hdr)
#else
// if original packet was tampered earlier it needs checksum fixed
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY)
#endif
#endif
udp_fix_checksum(udphdr,transport_len,ip,ip6hdr);
}

View File

@ -59,6 +59,7 @@ uint32_t net16_add(uint16_t netorder_value, uint16_t cpuorder_increment);
#define VERDICT_DROP 2
#define VERDICT_MASK 3
#define VERDICT_NOCSUM 4
#define VERDICT_GARBAGE 8
#define IP4_TOS(ip_header) (ip_header ? ip_header->ip_tos : 0)
#define IP4_IP_ID(ip_header) (ip_header ? ip_header->ip_id : 0)
@ -68,10 +69,13 @@ uint32_t net16_add(uint16_t netorder_value, uint16_t cpuorder_increment);
bool prepare_tcp_segment4(
const struct sockaddr_in *src, const struct sockaddr_in *dst,
uint8_t tcp_flags,
bool sack,
uint16_t nmss,
uint32_t nseq, uint32_t nack_seq,
uint16_t nwsize,
uint8_t scale_factor,
uint32_t *timestamps,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@ -83,6 +87,8 @@ bool prepare_tcp_segment4(
bool prepare_tcp_segment6(
const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst,
uint8_t tcp_flags,
bool sack,
uint16_t nmss,
uint32_t nseq, uint32_t nack_seq,
uint16_t nwsize,
uint8_t scale_factor,
@ -97,10 +103,13 @@ bool prepare_tcp_segment6(
bool prepare_tcp_segment(
const struct sockaddr *src, const struct sockaddr *dst,
uint8_t tcp_flags,
bool sack,
uint16_t nmss,
uint32_t nseq, uint32_t nack_seq,
uint16_t nwsize,
uint8_t scale_factor,
uint32_t *timestamps,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@ -114,6 +123,7 @@ bool prepare_tcp_segment(
bool prepare_udp_segment4(
const struct sockaddr_in *src, const struct sockaddr_in *dst,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@ -133,6 +143,7 @@ bool prepare_udp_segment6(
uint8_t *buf, size_t *buflen);
bool prepare_udp_segment(
const struct sockaddr *src, const struct sockaddr *dst,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@ -162,15 +173,20 @@ bool ip_frag(
uint8_t *pkt1, size_t *pkt1_size,
uint8_t *pkt2, size_t *pkt2_size);
void rewrite_ttl(struct ip *ip, struct ip6_hdr *ip6, uint8_t ttl);
bool rewrite_ttl(struct ip *ip, struct ip6_hdr *ip6, uint8_t ttl);
void extract_ports(const struct tcphdr *tcphdr, const struct udphdr *udphdr, uint8_t *proto, uint16_t *sport, uint16_t *dport);
void extract_endpoints(const struct ip *ip,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr,const struct udphdr *udphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst);
uint8_t *tcp_find_option(struct tcphdr *tcp, uint8_t kind);
uint32_t *tcp_find_timestamps(struct tcphdr *tcp);
uint8_t tcp_find_scale_factor(const struct tcphdr *tcp);
uint16_t tcp_find_mss(struct tcphdr *tcp);
bool tcp_has_sack(struct tcphdr *tcp);
bool tcp_has_fastopen(const struct tcphdr *tcp);
bool ip_has_df(const struct ip *ip);
#ifdef __CYGWIN__
extern uint32_t w_win32_error;
@ -242,15 +258,13 @@ void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_fac
typedef struct
{
uint8_t delta, min, max;
int8_t delta;
uint8_t min, max;
} autottl;
#define AUTOTTL_DEFAULT_DELTA 1
#define AUTOTTL_DEFAULT_MIN 3
#define AUTOTTL_DEFAULT_MAX 20
#define AUTOTTL_ENABLED(a) (!!(a).delta)
#define AUTOTTL_SET_DEFAULT(a) {(a).delta=AUTOTTL_DEFAULT_DELTA; (a).min=AUTOTTL_DEFAULT_MIN; (a).max=AUTOTTL_DEFAULT_MAX;}
#define AUTOTTL_ENABLED(a) ((a).delta || (a).min || (a).max)
uint8_t autottl_guess(uint8_t ttl, const autottl *attl);
uint8_t hop_count_guess(uint8_t ttl);
uint8_t autottl_eval(uint8_t hop_count, const autottl *attl);
void do_nat(bool bOutbound, struct ip *ip, struct ip6_hdr *ip6, struct tcphdr *tcphdr, struct udphdr *udphdr, const struct sockaddr_in *target4, const struct sockaddr_in6 *target6);
void verdict_tcp_csum_fix(uint8_t verdict, struct tcphdr *tcphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr);

File diff suppressed because it is too large Load Diff

View File

@ -52,4 +52,4 @@ bool desync_valid_second_stage(enum dpi_desync_mode mode);
bool desync_valid_second_stage_tcp(enum dpi_desync_mode mode);
bool desync_valid_second_stage_udp(enum dpi_desync_mode mode);
uint8_t dpi_desync_packet(uint32_t fwmark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt);
uint8_t dpi_desync_packet(uint32_t fwmark, const char *ifin, const char *ifout, uint8_t *data_pkt, size_t *len_pkt);

View File

@ -9,6 +9,8 @@
#include <stdio.h>
#include <time.h>
#define UNARY_PLUS(v) (v>0 ? "+" : "")
// this saves memory. sockaddr_storage is larger than required. it can be 128 bytes. sockaddr_in6 is 28 bytes.
typedef union
{

View File

@ -287,11 +287,13 @@ static struct hostlist_file *RegisterHostlist_(struct hostlist_files_head *hostl
}
struct hostlist_file *RegisterHostlist(struct desync_profile *dp, bool bExclude, const char *filename)
{
/*
if (filename && !file_mod_time(filename))
{
DLOG_ERR("cannot access hostlist file '%s'\n",filename);
return NULL;
}
*/
return RegisterHostlist_(
&params.hostlists,
bExclude ? &dp->hl_collection_exclude : &dp->hl_collection,

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "packet_queue.h"
@ -25,7 +26,7 @@ void rawpacket_queue_destroy(struct rawpacket_tailhead *q)
while((rp = rawpacket_dequeue(q))) rawpacket_free(rp);
}
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len,size_t len_payload)
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload)
{
struct rawpacket *rp = malloc(sizeof(struct rawpacket));
if (!rp) return NULL;
@ -39,13 +40,14 @@ struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sock
rp->dst = *dst;
rp->fwmark = fwmark;
if (ifout)
{
strncpy(rp->ifout,ifout,sizeof(rp->ifout));
rp->ifout[sizeof(rp->ifout)-1]=0;
}
if (ifin)
snprintf(rp->ifin,sizeof(rp->ifin),"%s",ifin);
else
rp->ifout[0]=0;
*rp->ifin = 0;
if (ifout)
snprintf(rp->ifout,sizeof(rp->ifout),"%s",ifout);
else
*rp->ifout = 0;
memcpy(rp->packet,data,len);
rp->len=len;
rp->len_payload=len_payload;

View File

@ -9,7 +9,7 @@
struct rawpacket
{
struct sockaddr_storage dst;
char ifout[IFNAMSIZ+1];
char ifin[IFNAMSIZ], ifout[IFNAMSIZ];
uint32_t fwmark;
size_t len, len_payload;
uint8_t *packet;
@ -21,6 +21,6 @@ void rawpacket_queue_init(struct rawpacket_tailhead *q);
void rawpacket_queue_destroy(struct rawpacket_tailhead *q);
bool rawpacket_queue_empty(const struct rawpacket_tailhead *q);
unsigned int rawpacket_queue_count(const struct rawpacket_tailhead *q);
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len,size_t len_payload);
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload);
struct rawpacket *rawpacket_dequeue(struct rawpacket_tailhead *q);
void rawpacket_free(struct rawpacket *rp);

View File

@ -187,10 +187,10 @@ void dp_init(struct desync_profile *dp)
dp->desync_repeats = 1;
dp->fake_syndata_size = 16;
dp->wscale=-1; // default - dont change scale factor (client)
dp->desync_ttl6 = 0xFF; // unused
dp->desync_badseq_increment = BADSEQ_INCREMENT_DEFAULT;
dp->desync_badseq_ack_increment = BADSEQ_ACK_INCREMENT_DEFAULT;
dp->wssize_cutoff_mode = dp->desync_start_mode = dp->desync_cutoff_mode = 'n'; // packet number by default
dp->desync_ttl6 = dp->dup_ttl6 = dp->orig_mod_ttl6 = 0xFF; // unused
dp->desync_badseq_increment = dp->dup_badseq_increment = BADSEQ_INCREMENT_DEFAULT;
dp->desync_badseq_ack_increment = dp->dup_badseq_ack_increment = BADSEQ_ACK_INCREMENT_DEFAULT;
dp->wssize_cutoff_mode = dp->desync_start_mode = dp->desync_cutoff_mode = dp->dup_start_mode = dp->dup_cutoff_mode = dp->orig_mod_start_mode = dp->orig_mod_cutoff_mode = 'n'; // packet number by default
dp->udplen_increment = UDPLEN_INCREMENT_DEFAULT;
dp->hostlist_auto_fail_threshold = HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT;
dp->hostlist_auto_fail_time = HOSTLIST_AUTO_FAIL_TIME_DEFAULT;
@ -294,3 +294,12 @@ bool dp_list_have_autohostlist(struct desync_profile_list_head *head)
return true;
return false;
}
// check if we need empty outgoing ACK
bool dp_list_need_all_out(struct desync_profile_list_head *head)
{
struct desync_profile_list *dpl;
LIST_FOREACH(dpl, head, next)
if (dpl->dp.dup_repeats || PROFILE_HAS_ORIG_MOD(&dpl->dp))
return true;
return false;
}

View File

@ -36,6 +36,19 @@
#define HOSTLIST_AUTO_FAIL_TIME_DEFAULT 60
#define HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT 3
#define IPCACHE_LIFETIME 7200
#define AUTOTTL_DEFAULT_DESYNC_DELTA -1
#define AUTOTTL_DEFAULT_DESYNC_MIN 3
#define AUTOTTL_DEFAULT_DESYNC_MAX 20
#define AUTOTTL_DEFAULT_ORIG_DELTA +5
#define AUTOTTL_DEFAULT_ORIG_MIN 3
#define AUTOTTL_DEFAULT_ORIG_MAX 64
#define AUTOTTL_DEFAULT_DUP_DELTA -1
#define AUTOTTL_DEFAULT_DUP_MIN 3
#define AUTOTTL_DEFAULT_DUP_MAX 64
#define MAX_SPLITS 64
#define FAKE_TLS_MOD_SAVE_MASK 0x0F
@ -50,6 +63,8 @@
#define FAKE_MAX_TCP 1460
#define FAKE_MAX_UDP 1472
#define MAX_GIDS 64
enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG };
struct fake_tls_mod_cache
@ -62,6 +77,8 @@ struct fake_tls_mod
uint32_t mod;
};
typedef enum {SS_NONE=0,SS_SYN,SS_SYNACK,SS_ACKSYN} t_synack_split;
struct desync_profile
{
int n; // number of the profile
@ -71,6 +88,8 @@ struct desync_profile
char wssize_cutoff_mode; // n - packets, d - data packets, s - relative sequence
unsigned int wssize_cutoff;
t_synack_split synack_split;
bool hostcase, hostnospace, domcase, methodeol;
char hostspell[4];
enum dpi_desync_mode desync_mode0,desync_mode,desync_mode2;
@ -82,6 +101,20 @@ struct desync_profile
int split_count;
struct proto_pos seqovl;
char dup_start_mode, dup_cutoff_mode; // n - packets, d - data packets, s - relative sequence
bool dup_replace;
unsigned int dup_start, dup_cutoff;
unsigned int dup_repeats;
uint8_t dup_ttl, dup_ttl6;
uint32_t dup_fooling_mode;
uint32_t dup_badseq_increment, dup_badseq_ack_increment;
autottl dup_autottl, dup_autottl6;
char orig_mod_start_mode, orig_mod_cutoff_mode; // n - packets, d - data packets, s - relative sequence
unsigned int orig_mod_start, orig_mod_cutoff;
uint8_t orig_mod_ttl, orig_mod_ttl6;
autottl orig_autottl, orig_autottl6;
char desync_start_mode, desync_cutoff_mode; // n - packets, d - data packets, s - relative sequence
unsigned int desync_start, desync_cutoff;
uint8_t desync_ttl, desync_ttl6;
@ -114,9 +147,10 @@ struct desync_profile
hostfail_pool *hostlist_auto_fail_counters;
};
#define PROFILE_IPSETS_ABSENT(dp) (!LIST_FIRST(&dp->ips_collection) && !LIST_FIRST(&dp->ips_collection_exclude))
#define PROFILE_IPSETS_EMPTY(dp) (ipset_collection_is_empty(&dp->ips_collection) && ipset_collection_is_empty(&dp->ips_collection_exclude))
#define PROFILE_HOSTLISTS_EMPTY(dp) (hostlist_collection_is_empty(&dp->hl_collection) && hostlist_collection_is_empty(&dp->hl_collection_exclude))
#define PROFILE_IPSETS_ABSENT(dp) (!LIST_FIRST(&(dp)->ips_collection) && !LIST_FIRST(&(dp)->ips_collection_exclude))
#define PROFILE_IPSETS_EMPTY(dp) (ipset_collection_is_empty(&(dp)->ips_collection) && ipset_collection_is_empty(&(dp)->ips_collection_exclude))
#define PROFILE_HOSTLISTS_EMPTY(dp) (hostlist_collection_is_empty(&(dp)->hl_collection) && hostlist_collection_is_empty(&(dp)->hl_collection_exclude))
#define PROFILE_HAS_ORIG_MOD(dp) ((dp)->orig_mod_ttl || (dp)->orig_mod_ttl6)
struct desync_profile_list {
struct desync_profile dp;
@ -127,6 +161,7 @@ struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head);
void dp_entry_destroy(struct desync_profile_list *entry);
void dp_list_destroy(struct desync_profile_list_head *head);
bool dp_list_have_autohostlist(struct desync_profile_list_head *head);
bool dp_list_need_all_out(struct desync_profile_list_head *head);
void dp_init(struct desync_profile *dp);
bool dp_fake_defaults(struct desync_profile *dp);
void dp_clear(struct desync_profile *dp);
@ -141,6 +176,8 @@ struct params_s
char debug_logfile[PATH_MAX];
bool debug;
bool daemon;
#ifdef __linux__
int qnum;
#elif defined(BSD)
@ -156,8 +193,10 @@ struct params_s
#else
bool droproot;
uid_t uid;
gid_t gid;
gid_t gid[MAX_GIDS];
int gid_count;
#endif
char pidfile[PATH_MAX];
char hostlist_auto_debuglog[PATH_MAX];
@ -168,6 +207,11 @@ struct params_s
unsigned int ctrack_t_syn, ctrack_t_est, ctrack_t_fin, ctrack_t_udp;
t_conntrack conntrack;
bool ctrack_disable;
bool autottl_present,cache_hostname;
unsigned int ipcache_lifetime;
ip_cache ipcache;
};
extern struct params_s params;

View File

@ -3,6 +3,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#define DESTROY_STR_POOL(etype, ppool) \
etype *elem, *tmp; \
@ -549,7 +550,7 @@ struct blob_item *blob_collection_add_blob(struct blob_collection_head *head, co
if (data) memcpy(entry->data,data,size);
entry->size = size;
entry->size_buf = size+size_reserve;
// insert to the end
struct blob_item *itemc,*iteml=LIST_FIRST(head);
if (iteml)
@ -579,3 +580,219 @@ bool blob_collection_empty(const struct blob_collection_head *head)
{
return !LIST_FIRST(head);
}
static void ipcache_item_touch(ip_cache_item *item)
{
time(&item->last);
}
static void ipcache_item_init(ip_cache_item *item)
{
ipcache_item_touch(item);
item->hostname = NULL;
item->hops = 0;
}
static void ipcache_item_destroy(ip_cache_item *item)
{
free(item->hostname);
}
static void ipcache4Destroy(ip_cache4 **ipcache)
{
ip_cache4 *elem, *tmp;
HASH_ITER(hh, *ipcache, elem, tmp)
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
static void ipcache4Key(ip4if *key, const struct in_addr *a, const char *iface)
{
memset(key,0,sizeof(*key)); // make sure everything is zero
key->addr = *a;
if (iface) snprintf(key->iface,sizeof(key->iface),"%s",iface);
}
static ip_cache4 *ipcache4Find(ip_cache4 *ipcache, const struct in_addr *a, const char *iface)
{
ip_cache4 *entry;
struct ip4if key;
ipcache4Key(&key,a,iface);
HASH_FIND(hh, ipcache, &key, sizeof(key), entry);
return entry;
}
static ip_cache4 *ipcache4Add(ip_cache4 **ipcache, const struct in_addr *a, const char *iface)
{
// avoid dups
ip_cache4 *entry = ipcache4Find(*ipcache,a,iface);
if (entry) return entry; // already included
entry = malloc(sizeof(ip_cache4));
if (!entry) return NULL;
ipcache4Key(&entry->key,a,iface);
oom = false;
HASH_ADD(hh, *ipcache, key, sizeof(entry->key), entry);
if (oom) { free(entry); return NULL; }
ipcache_item_init(&entry->data);
return entry;
}
static void ipcache4Print(ip_cache4 *ipcache)
{
char s_ip[16];
time_t now;
ip_cache4 *ipc, *tmp;
time(&now);
HASH_ITER(hh, ipcache , ipc, tmp)
{
*s_ip=0;
inet_ntop(AF_INET, &ipc->key.addr, s_ip, sizeof(s_ip));
printf("%s iface=%s : hops %u hostname=%s now=last+%llu\n", s_ip, ipc->key.iface, ipc->data.hops, ipc->data.hostname ? ipc->data.hostname : "", (unsigned long long)(now-ipc->data.last));
}
}
static void ipcache6Destroy(ip_cache6 **ipcache)
{
ip_cache6 *elem, *tmp;
HASH_ITER(hh, *ipcache, elem, tmp)
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
static void ipcache6Key(ip6if *key, const struct in6_addr *a, const char *iface)
{
memset(key,0,sizeof(*key)); // make sure everything is zero
key->addr = *a;
if (iface) snprintf(key->iface,sizeof(key->iface),"%s",iface);
}
static ip_cache6 *ipcache6Find(ip_cache6 *ipcache, const struct in6_addr *a, const char *iface)
{
ip_cache6 *entry;
ip6if key;
ipcache6Key(&key,a,iface);
HASH_FIND(hh, ipcache, &key, sizeof(key), entry);
return entry;
}
static ip_cache6 *ipcache6Add(ip_cache6 **ipcache, const struct in6_addr *a, const char *iface)
{
// avoid dups
ip_cache6 *entry = ipcache6Find(*ipcache,a,iface);
if (entry) return entry; // already included
entry = malloc(sizeof(ip_cache6));
if (!entry) return NULL;
ipcache6Key(&entry->key,a,iface);
oom = false;
HASH_ADD(hh, *ipcache, key, sizeof(entry->key), entry);
if (oom) { free(entry); return NULL; }
ipcache_item_init(&entry->data);
return entry;
}
static void ipcache6Print(ip_cache6 *ipcache)
{
char s_ip[40];
time_t now;
ip_cache6 *ipc, *tmp;
time(&now);
HASH_ITER(hh, ipcache , ipc, tmp)
{
*s_ip=0;
inet_ntop(AF_INET6, &ipc->key.addr, s_ip, sizeof(s_ip));
printf("%s iface=%s : hops %u hostname=%s now=last+%llu\n", s_ip, ipc->key.iface, ipc->data.hops, ipc->data.hostname ? ipc->data.hostname : "", (unsigned long long)(now-ipc->data.last));
}
}
void ipcacheDestroy(ip_cache *ipcache)
{
ipcache4Destroy(&ipcache->ipcache4);
ipcache6Destroy(&ipcache->ipcache6);
}
void ipcachePrint(ip_cache *ipcache)
{
ipcache4Print(ipcache->ipcache4);
ipcache6Print(ipcache->ipcache6);
}
ip_cache_item *ipcacheTouch(ip_cache *ipcache, const struct in_addr *a4, const struct in6_addr *a6, const char *iface)
{
ip_cache4 *ipcache4;
ip_cache6 *ipcache6;
if (a4)
{
if ((ipcache4 = ipcache4Add(&ipcache->ipcache4,a4,iface)))
{
ipcache_item_touch(&ipcache4->data);
return &ipcache4->data;
}
}
else if (a6)
{
if ((ipcache6 = ipcache6Add(&ipcache->ipcache6,a6,iface)))
{
ipcache_item_touch(&ipcache6->data);
return &ipcache6->data;
}
}
return NULL;
}
static void ipcache4_purge(ip_cache4 **ipcache, time_t lifetime)
{
ip_cache4 *elem, *tmp;
time_t now = time(NULL);
HASH_ITER(hh, *ipcache, elem, tmp)
{
if (now >= (elem->data.last + lifetime))
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
}
static void ipcache6_purge(ip_cache6 **ipcache, time_t lifetime)
{
ip_cache6 *elem, *tmp;
time_t now = time(NULL);
HASH_ITER(hh, *ipcache, elem, tmp)
{
if (now >= (elem->data.last + lifetime))
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
}
static void ipcache_purge(ip_cache *ipcache, time_t lifetime)
{
if (lifetime) // 0 = no expire
{
ipcache4_purge(&ipcache->ipcache4, lifetime);
ipcache6_purge(&ipcache->ipcache6, lifetime);
}
}
static time_t ipcache_purge_prev=0;
void ipcachePurgeRateLimited(ip_cache *ipcache, time_t lifetime)
{
time_t now = time(NULL);
// do not purge too often to save resources
if (ipcache_purge_prev != now)
{
ipcache_purge(ipcache, lifetime);
ipcache_purge_prev = now;
}
}

View File

@ -3,6 +3,7 @@
#include <stdbool.h>
#include <ctype.h>
#include <sys/queue.h>
#include <net/if.h>
#include <time.h>
#include "helpers.h"
@ -161,3 +162,43 @@ struct blob_item *blob_collection_add(struct blob_collection_head *head);
struct blob_item *blob_collection_add_blob(struct blob_collection_head *head, const void *data, size_t size, size_t size_reserve);
void blob_collection_destroy(struct blob_collection_head *head);
bool blob_collection_empty(const struct blob_collection_head *head);
typedef struct ip4if
{
char iface[IFNAMSIZ];
struct in_addr addr;
} ip4if;
typedef struct ip6if
{
char iface[IFNAMSIZ];
struct in6_addr addr;
} ip6if;
typedef struct ip_cache_item
{
time_t last;
char *hostname;
uint8_t hops;
} ip_cache_item;
typedef struct ip_cache4
{
ip4if key;
ip_cache_item data;
UT_hash_handle hh; /* makes this structure hashable */
} ip_cache4;
typedef struct ip_cache6
{
ip6if key;
ip_cache_item data;
UT_hash_handle hh; /* makes this structure hashable */
} ip_cache6;
typedef struct ip_cache
{
ip_cache4 *ipcache4;
ip_cache6 *ipcache6;
} ip_cache;
ip_cache_item *ipcacheTouch(ip_cache *ipcache, const struct in_addr *a4, const struct in6_addr *a6, const char *iface);
void ipcachePurgeRateLimited(ip_cache *ipcache, time_t lifetime);
void ipcacheDestroy(ip_cache *ipcache);
void ipcachePrint(ip_cache *ipcache);

View File

@ -345,6 +345,19 @@ size_t HttpPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz)
}
const char *TLSVersionStr(uint16_t tlsver)
{
switch(tlsver)
{
case 0x0301: return "TLS 1.0";
case 0x0302: return "TLS 1.1";
case 0x0303: return "TLS 1.2";
case 0x0304: return "TLS 1.3";
default:
// 0x0a0a, 0x1a1a, ..., 0xfafa
return (((tlsver & 0x0F0F) == 0x0A0A) && ((tlsver>>12)==((tlsver>>4)&0xF))) ? "GREASE" : "UNKNOWN";
}
}
uint16_t TLSRecordDataLen(const uint8_t *data)
{
@ -1015,7 +1028,8 @@ bool IsDiscordIpDiscoveryRequest(const uint8_t *data, size_t len)
return len==74 &&
data[0]==0 && data[1]==1 &&
data[2]==0 && data[3]==70 &&
data[8]==0 && memcmp(&data[8],&data[9],63)==0; // address is not set in requests
!memcmp(data+8,"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",64);
// address is not set in request
}
bool IsStunMessage(const uint8_t *data, size_t len)
{

View File

@ -57,6 +57,7 @@ int HttpReplyCode(const uint8_t *data, size_t len);
// must be pre-checked by IsHttpReply
bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host);
const char *TLSVersionStr(uint16_t tlsver);
uint16_t TLSRecordDataLen(const uint8_t *data);
size_t TLSRecordLen(const uint8_t *data);
bool IsTLSRecordFull(const uint8_t *data, size_t len);

View File

@ -192,20 +192,21 @@ static bool set_seccomp(void)
bool sec_harden(void)
{
bool bRes = true;
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
{
DLOG_PERROR("PR_SET_NO_NEW_PRIVS(prctl)");
return false;
bRes = false;
}
#if ARCH_NR!=0
if (!set_seccomp())
{
DLOG_PERROR("seccomp");
if (errno==EINVAL) DLOG_ERR("seccomp: this can be safely ignored if kernel does not support seccomp\n");
return false;
bRes = false;
}
#endif
return true;
return bRes;
}
@ -294,8 +295,13 @@ bool can_drop_root(void)
#endif
}
bool droproot(uid_t uid, gid_t gid)
bool droproot(uid_t uid, gid_t *gid, int gid_count)
{
if (gid_count<1)
{
DLOG_ERR("droproot: no groups specified");
return false;
}
#ifdef __linux__
if (prctl(PR_SET_KEEPCAPS, 1L))
{
@ -304,12 +310,12 @@ bool droproot(uid_t uid, gid_t gid)
}
#endif
// drop all SGIDs
if (setgroups(0,NULL))
if (setgroups(gid_count,gid))
{
DLOG_PERROR("setgroups");
return false;
}
if (setgid(gid))
if (setgid(gid[0]))
{
DLOG_PERROR("setgid");
return false;
@ -342,9 +348,13 @@ void print_id(void)
#endif
void daemonize(void)
{
int pid;
#ifdef __CYGWIN__
char *cwd = get_current_dir_name();
#endif
pid = fork();
if (pid == -1)
@ -355,6 +365,10 @@ void daemonize(void)
else if (pid != 0)
exit(0);
#ifdef __CYGWIN__
chdir(get_current_dir_name());
#endif
if (setsid() == -1)
exit(2);
if (chdir("/") == -1)

View File

@ -84,7 +84,7 @@ bool dropcaps(void);
#ifndef __CYGWIN__
bool sec_harden(void);
bool can_drop_root(void);
bool droproot(uid_t uid, gid_t gid);
bool droproot(uid_t uid, gid_t *gid, int gid_count);
void print_id(void);
#endif

View File

@ -12,6 +12,7 @@
// this saves memory. sockaddr_storage is larger than required. it can be 128 bytes. sockaddr_in6 is 28 bytes.
typedef union
{
sa_family_t sa_family;
struct sockaddr_in sa4; // size 16
struct sockaddr_in6 sa6; // size 28
} sockaddr_in46;

View File

@ -287,11 +287,13 @@ static struct hostlist_file *RegisterHostlist_(struct hostlist_files_head *hostl
}
struct hostlist_file *RegisterHostlist(struct desync_profile *dp, bool bExclude, const char *filename)
{
/*
if (filename && !file_mod_time(filename))
{
DLOG_ERR("cannot access hostlist file '%s'\n",filename);
return NULL;
}
*/
return RegisterHostlist_(
&params.hostlists,
bExclude ? &dp->hl_collection_exclude : &dp->hl_collection,

View File

@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <time.h>
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
@ -18,11 +19,15 @@
#define HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT 3
#define HOSTLIST_AUTO_FAIL_TIME_DEFAULT 60
#define FIX_SEG_DEFAULT_MAX_WAIT 50
#define FIX_SEG_DEFAULT_MAX_WAIT 50
#define IPCACHE_LIFETIME 7200
#define MAX_GIDS 64
enum bindll { unwanted=0, no, prefer, force };
#define MAX_BINDS 32
#define MAX_BINDS 32
struct bind_s
{
char bindaddr[64],bindiface[IF_NAMESIZE];
@ -31,7 +36,7 @@ struct bind_s
int bind_wait_ifup,bind_wait_ip,bind_wait_ip_ll;
};
#define MAX_SPLITS 16
#define MAX_SPLITS 16
enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG };
@ -114,8 +119,9 @@ struct params_s
bool droproot;
bool daemon;
uid_t uid;
gid_t gid;
char pidfile[256];
gid_t gid[MAX_GIDS];
int gid_count;
char pidfile[PATH_MAX];
int maxconn,resolver_threads,maxfiles,max_orphan_time;
int local_rcvbuf,local_sndbuf,remote_rcvbuf,remote_sndbuf;
#if defined(__linux__) || defined(__APPLE__)
@ -140,6 +146,10 @@ struct params_s
bool tamper; // any tamper option is set
bool tamper_lim; // tamper-start or tamper-cutoff set in any profile
struct desync_profile_list_head desync_profiles;
unsigned int ipcache_lifetime;
bool cache_hostname;
ip_cache ipcache;
};
extern struct params_s params;

View File

@ -3,6 +3,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#define DESTROY_STR_POOL(etype, ppool) \
etype *elem, *tmp; \
@ -517,3 +518,277 @@ bool port_filters_deny_if_empty(struct port_filters_head *head)
if (LIST_FIRST(head)) return true;
return pf_parse("0",&pf) && port_filter_add(head,&pf);
}
struct blob_item *blob_collection_add(struct blob_collection_head *head)
{
struct blob_item *entry = calloc(1,sizeof(struct blob_item));
if (entry)
{
// insert to the end
struct blob_item *itemc,*iteml=LIST_FIRST(head);
if (iteml)
{
while ((itemc=LIST_NEXT(iteml,next))) iteml = itemc;
LIST_INSERT_AFTER(iteml, entry, next);
}
else
LIST_INSERT_HEAD(head, entry, next);
}
return entry;
}
struct blob_item *blob_collection_add_blob(struct blob_collection_head *head, const void *data, size_t size, size_t size_reserve)
{
struct blob_item *entry = calloc(1,sizeof(struct blob_item));
if (!entry) return NULL;
if (!(entry->data = malloc(size+size_reserve)))
{
free(entry);
return NULL;
}
if (data) memcpy(entry->data,data,size);
entry->size = size;
entry->size_buf = size+size_reserve;
// insert to the end
struct blob_item *itemc,*iteml=LIST_FIRST(head);
if (iteml)
{
while ((itemc=LIST_NEXT(iteml,next))) iteml = itemc;
LIST_INSERT_AFTER(iteml, entry, next);
}
else
LIST_INSERT_HEAD(head, entry, next);
return entry;
}
void blob_collection_destroy(struct blob_collection_head *head)
{
struct blob_item *entry;
while ((entry = LIST_FIRST(head)))
{
LIST_REMOVE(entry, next);
free(entry->extra);
free(entry->extra2);
free(entry->data);
free(entry);
}
}
bool blob_collection_empty(const struct blob_collection_head *head)
{
return !LIST_FIRST(head);
}
static void ipcache_item_touch(ip_cache_item *item)
{
time(&item->last);
}
static void ipcache_item_init(ip_cache_item *item)
{
ipcache_item_touch(item);
item->hostname = NULL;
}
static void ipcache_item_destroy(ip_cache_item *item)
{
free(item->hostname);
}
static void ipcache4Destroy(ip_cache4 **ipcache)
{
ip_cache4 *elem, *tmp;
HASH_ITER(hh, *ipcache, elem, tmp)
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
static void ipcache4Key(ip4if *key, const struct in_addr *a)
{
memset(key,0,sizeof(*key)); // make sure everything is zero
key->addr = *a;
}
static ip_cache4 *ipcache4Find(ip_cache4 *ipcache, const struct in_addr *a)
{
ip_cache4 *entry;
struct ip4if key;
ipcache4Key(&key,a);
HASH_FIND(hh, ipcache, &key, sizeof(key), entry);
return entry;
}
static ip_cache4 *ipcache4Add(ip_cache4 **ipcache, const struct in_addr *a)
{
// avoid dups
ip_cache4 *entry = ipcache4Find(*ipcache,a);
if (entry) return entry; // already included
entry = malloc(sizeof(ip_cache4));
if (!entry) return NULL;
ipcache4Key(&entry->key,a);
oom = false;
HASH_ADD(hh, *ipcache, key, sizeof(entry->key), entry);
if (oom) { free(entry); return NULL; }
ipcache_item_init(&entry->data);
return entry;
}
static void ipcache4Print(ip_cache4 *ipcache)
{
char s_ip[16];
time_t now;
ip_cache4 *ipc, *tmp;
time(&now);
HASH_ITER(hh, ipcache , ipc, tmp)
{
*s_ip=0;
inet_ntop(AF_INET, &ipc->key.addr, s_ip, sizeof(s_ip));
printf("%s : hostname=%s now=last+%llu\n", s_ip, ipc->data.hostname ? ipc->data.hostname : "", (unsigned long long)(now-ipc->data.last));
}
}
static void ipcache6Destroy(ip_cache6 **ipcache)
{
ip_cache6 *elem, *tmp;
HASH_ITER(hh, *ipcache, elem, tmp)
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
static void ipcache6Key(ip6if *key, const struct in6_addr *a)
{
memset(key,0,sizeof(*key)); // make sure everything is zero
key->addr = *a;
}
static ip_cache6 *ipcache6Find(ip_cache6 *ipcache, const struct in6_addr *a)
{
ip_cache6 *entry;
ip6if key;
ipcache6Key(&key,a);
HASH_FIND(hh, ipcache, &key, sizeof(key), entry);
return entry;
}
static ip_cache6 *ipcache6Add(ip_cache6 **ipcache, const struct in6_addr *a)
{
// avoid dups
ip_cache6 *entry = ipcache6Find(*ipcache,a);
if (entry) return entry; // already included
entry = malloc(sizeof(ip_cache6));
if (!entry) return NULL;
ipcache6Key(&entry->key,a);
oom = false;
HASH_ADD(hh, *ipcache, key, sizeof(entry->key), entry);
if (oom) { free(entry); return NULL; }
ipcache_item_init(&entry->data);
return entry;
}
static void ipcache6Print(ip_cache6 *ipcache)
{
char s_ip[40];
time_t now;
ip_cache6 *ipc, *tmp;
time(&now);
HASH_ITER(hh, ipcache , ipc, tmp)
{
*s_ip=0;
inet_ntop(AF_INET6, &ipc->key.addr, s_ip, sizeof(s_ip));
printf("%s : hostname=%s now=last+%llu\n", s_ip, ipc->data.hostname ? ipc->data.hostname : "", (unsigned long long)(now-ipc->data.last));
}
}
void ipcacheDestroy(ip_cache *ipcache)
{
ipcache4Destroy(&ipcache->ipcache4);
ipcache6Destroy(&ipcache->ipcache6);
}
void ipcachePrint(ip_cache *ipcache)
{
ipcache4Print(ipcache->ipcache4);
ipcache6Print(ipcache->ipcache6);
}
ip_cache_item *ipcacheTouch(ip_cache *ipcache, const struct in_addr *a4, const struct in6_addr *a6)
{
ip_cache4 *ipcache4;
ip_cache6 *ipcache6;
if (a4)
{
if ((ipcache4 = ipcache4Add(&ipcache->ipcache4,a4)))
{
ipcache_item_touch(&ipcache4->data);
return &ipcache4->data;
}
}
else if (a6)
{
if ((ipcache6 = ipcache6Add(&ipcache->ipcache6,a6)))
{
ipcache_item_touch(&ipcache6->data);
return &ipcache6->data;
}
}
return NULL;
}
static void ipcache4_purge(ip_cache4 **ipcache, time_t lifetime)
{
ip_cache4 *elem, *tmp;
time_t now = time(NULL);
HASH_ITER(hh, *ipcache, elem, tmp)
{
if (now >= (elem->data.last + lifetime))
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
}
static void ipcache6_purge(ip_cache6 **ipcache, time_t lifetime)
{
ip_cache6 *elem, *tmp;
time_t now = time(NULL);
HASH_ITER(hh, *ipcache, elem, tmp)
{
if (now >= (elem->data.last + lifetime))
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
}
static void ipcache_purge(ip_cache *ipcache, time_t lifetime)
{
if (lifetime) // 0 = no expire
{
ipcache4_purge(&ipcache->ipcache4, lifetime);
ipcache6_purge(&ipcache->ipcache6, lifetime);
}
}
static time_t ipcache_purge_prev=0;
void ipcachePurgeRateLimited(ip_cache *ipcache, time_t lifetime)
{
time_t now = time(NULL);
// do not purge too often to save resources
if (ipcache_purge_prev != now)
{
ipcache_purge(ipcache, lifetime);
ipcache_purge_prev = now;
}
}

View File

@ -3,6 +3,7 @@
#include <stdbool.h>
#include <ctype.h>
#include <sys/queue.h>
#include <net/if.h>
#include <time.h>
#include "helpers.h"
@ -146,3 +147,55 @@ bool port_filter_add(struct port_filters_head *head, const port_filter *pf);
void port_filters_destroy(struct port_filters_head *head);
bool port_filters_in_range(const struct port_filters_head *head, uint16_t port);
bool port_filters_deny_if_empty(struct port_filters_head *head);
struct blob_item {
uint8_t *data; // main data blob
size_t size; // main data blob size
size_t size_buf;// main data blob allocated size
void *extra; // any data without size
void *extra2; // any data without size
LIST_ENTRY(blob_item) next;
};
LIST_HEAD(blob_collection_head, blob_item);
struct blob_item *blob_collection_add(struct blob_collection_head *head);
struct blob_item *blob_collection_add_blob(struct blob_collection_head *head, const void *data, size_t size, size_t size_reserve);
void blob_collection_destroy(struct blob_collection_head *head);
bool blob_collection_empty(const struct blob_collection_head *head);
typedef struct ip4if
{
struct in_addr addr;
} ip4if;
typedef struct ip6if
{
struct in6_addr addr;
} ip6if;
typedef struct ip_cache_item
{
time_t last;
char *hostname;
} ip_cache_item;
typedef struct ip_cache4
{
ip4if key;
ip_cache_item data;
UT_hash_handle hh; /* makes this structure hashable */
} ip_cache4;
typedef struct ip_cache6
{
ip6if key;
ip_cache_item data;
UT_hash_handle hh; /* makes this structure hashable */
} ip_cache6;
typedef struct ip_cache
{
ip_cache4 *ipcache4;
ip_cache6 *ipcache6;
} ip_cache;
ip_cache_item *ipcacheTouch(ip_cache *ipcache, const struct in_addr *a4, const struct in6_addr *a6);
void ipcachePurgeRateLimited(ip_cache *ipcache, time_t lifetime);
void ipcacheDestroy(ip_cache *ipcache);
void ipcachePrint(ip_cache *ipcache);

View File

@ -339,6 +339,20 @@ size_t HttpPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz)
const char *TLSVersionStr(uint16_t tlsver)
{
switch(tlsver)
{
case 0x0301: return "TLS 1.0";
case 0x0302: return "TLS 1.1";
case 0x0303: return "TLS 1.2";
case 0x0304: return "TLS 1.3";
default:
// 0x0a0a, 0x1a1a, ..., 0xfafa
return (((tlsver & 0x0F0F) == 0x0A0A) && ((tlsver>>12)==((tlsver>>4)&0xF))) ? "GREASE" : "UNKNOWN";
}
}
uint16_t TLSRecordDataLen(const uint8_t *data)
{
return pntoh16(data + 3);

View File

@ -53,6 +53,7 @@ int HttpReplyCode(const uint8_t *data, size_t len);
// must be pre-checked by IsHttpReply
bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host);
const char *TLSVersionStr(uint16_t tlsver);
uint16_t TLSRecordDataLen(const uint8_t *data);
size_t TLSRecordLen(const uint8_t *data);
bool IsTLSRecordFull(const uint8_t *data, size_t len);

View File

@ -169,25 +169,24 @@ static bool set_seccomp(void)
bool sec_harden(void)
{
bool bRes = true;
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
{
DLOG_PERROR("PR_SET_NO_NEW_PRIVS(prctl)");
return false;
bRes = false;
}
#if ARCH_NR!=0
if (!set_seccomp())
{
DLOG_PERROR("seccomp");
if (errno==EINVAL) DLOG_ERR("seccomp: this can be safely ignored if kernel does not support seccomp\n");
return false;
bRes = false;
}
#endif
return true;
return bRes;
}
bool checkpcap(uint64_t caps)
{
if (!caps) return true; // no special caps reqd
@ -270,8 +269,13 @@ bool can_drop_root(void)
#endif
}
bool droproot(uid_t uid, gid_t gid)
bool droproot(uid_t uid, gid_t *gid, int gid_count)
{
if (gid_count<1)
{
DLOG_ERR("droproot: no groups specified");
return false;
}
#ifdef __linux__
if (prctl(PR_SET_KEEPCAPS, 1L))
{
@ -280,12 +284,12 @@ bool droproot(uid_t uid, gid_t gid)
}
#endif
// drop all SGIDs
if (setgroups(0,NULL))
if (setgroups(gid_count,gid))
{
DLOG_PERROR("setgroups");
return false;
}
if (setgid(gid))
if (setgid(gid[0]))
{
DLOG_PERROR("setgid");
return false;

View File

@ -84,7 +84,7 @@ bool dropcaps(void);
bool sec_harden(void);
bool can_drop_root();
bool droproot(uid_t uid, gid_t gid);
bool droproot(uid_t uid, gid_t *gid, int gid_count);
void print_id(void);
void daemonize(void);
bool writepid(const char *filename);

View File

@ -7,6 +7,7 @@
#include "ipset.h"
#include "protocol.h"
#include "helpers.h"
#include "pools.h"
#define PKTDATA_MAXDUMP 32
@ -15,6 +16,126 @@ void packet_debug(const uint8_t *data, size_t sz)
hexdump_limited_dlog(data, sz, PKTDATA_MAXDUMP); VPRINT("\n");
}
static void TLSDebugHandshake(const uint8_t *tls,size_t sz)
{
if (!params.debug) return;
if (sz<6) return;
const uint8_t *ext;
size_t len,len2;
uint16_t v_handshake=pntoh16(tls+4), v, v2;
VPRINT("TLS handshake version : %s\n",TLSVersionStr(v_handshake));
if (TLSFindExtInHandshake(tls,sz,43,&ext,&len,false))
{
if (len)
{
len2 = ext[0];
if (len2<len)
{
for(ext++,len2&=~1 ; len2 ; len2-=2,ext+=2)
{
v = pntoh16(ext);
VPRINT("TLS supported versions ext : %s\n",TLSVersionStr(v));
}
}
}
}
else
VPRINT("TLS supported versions ext : not present\n");
if (TLSFindExtInHandshake(tls,sz,16,&ext,&len,false))
{
if (len>=2)
{
len2 = pntoh16(ext);
if (len2<=(len-2))
{
char s[32];
for(ext+=2; len2 ;)
{
v = *ext; ext++; len2--;
if (v<=len2)
{
v2 = v<sizeof(s) ? v : sizeof(s)-1;
memcpy(s,ext,v2);
s[v2]=0;
VPRINT("TLS ALPN ext : %s\n",s);
len2-=v;
ext+=v;
}
else
break;
}
}
}
}
else
VPRINT("TLS ALPN ext : not present\n");
VPRINT("TLS ECH ext : %s\n",TLSFindExtInHandshake(tls,sz,65037,NULL,NULL,false) ? "present" : "not present");
}
static void TLSDebug(const uint8_t *tls,size_t sz)
{
if (!params.debug) return;
if (sz<11) return;
VPRINT("TLS record layer version : %s\n",TLSVersionStr(pntoh16(tls+1)));
size_t reclen=TLSRecordLen(tls);
if (reclen<sz) sz=reclen; // correct len if it has more data than the first tls record has
TLSDebugHandshake(tls+5,sz-5);
}
bool ipcache_put_hostname(const struct in_addr *a4, const struct in6_addr *a6, const char *hostname)
{
if (!params.cache_hostname) return true;
ip_cache_item *ipc = ipcacheTouch(&params.ipcache,a4,a6);
if (!ipc)
{
DLOG_ERR("ipcache_put_hostname: out of memory\n");
return false;
}
if (!ipc->hostname || strcmp(ipc->hostname,hostname))
{
free(ipc->hostname);
if (!(ipc->hostname = strdup(hostname)))
{
DLOG_ERR("ipcache_put_hostname: out of memory\n");
return false;
}
VPRINT("hostname cached: %s\n", hostname);
}
return true;
}
static bool ipcache_get_hostname(const struct in_addr *a4, const struct in6_addr *a6, char *hostname, size_t hostname_buf_len)
{
if (!params.cache_hostname)
{
*hostname = 0;
return true;
}
ip_cache_item *ipc = ipcacheTouch(&params.ipcache,a4,a6);
if (!ipc)
{
DLOG_ERR("ipcache_get_hostname: out of memory\n");
return false;
}
if (ipc->hostname)
{
VPRINT("got cached hostname: %s\n", ipc->hostname);
snprintf(hostname,hostname_buf_len,"%s",ipc->hostname);
}
else
*hostname = 0;
return true;
}
static bool dp_match(struct desync_profile *dp, const struct sockaddr *dest, const char *hostname, t_l7proto l7proto)
{
bool bHostlistsEmpty;
@ -70,8 +191,17 @@ static struct desync_profile *dp_find(struct desync_profile_list_head *head, con
VPRINT("desync profile not found\n");
return NULL;
}
void apply_desync_profile(t_ctrack *ctrack, const struct sockaddr *dest)
{
ipcachePurgeRateLimited(&params.ipcache, params.ipcache_lifetime);
if (!ctrack->hostname)
{
char host[256];
if (ipcache_get_hostname(dest->sa_family==AF_INET ? &((struct sockaddr_in*)dest)->sin_addr : NULL, dest->sa_family==AF_INET6 ? &((struct sockaddr_in6*)dest)->sin6_addr : NULL , host, sizeof(host)) && *host)
if (!(ctrack->hostname=strdup(host)))
DLOG_ERR("hostname dup : out of memory");
}
ctrack->dp = dp_find(&params.desync_profiles, dest, ctrack->hostname, ctrack->l7proto);
}
@ -130,6 +260,7 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,
{
VPRINT("Data block contains TLS ClientHello\n");
l7proto=TLS;
TLSDebug(segment,*size);
bHaveHost=TLSHelloExtractHost((uint8_t*)segment,*size,Host,sizeof(Host),false);
}
else
@ -139,7 +270,11 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,
}
if (bHaveHost)
{
VPRINT("request hostname: %s\n", Host);
if (!ipcache_put_hostname(dest->sa_family==AF_INET ? &((struct sockaddr_in*)dest)->sin_addr : NULL, dest->sa_family==AF_INET6 ? &((struct sockaddr_in6*)dest)->sin6_addr : NULL , Host))
DLOG_ERR("ipcache_put_hostname: out of memory");
}
bool bDiscoveredL7 = ctrack->l7proto==UNKNOWN && l7proto!=UNKNOWN;
if (bDiscoveredL7)
@ -148,15 +283,17 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,
ctrack->l7proto=l7proto;
}
bool bDiscoveredHostname = bHaveHost && !ctrack->hostname;
bool bDiscoveredHostname = bHaveHost && !ctrack->hostname_discovered;
if (bDiscoveredHostname)
{
VPRINT("discovered hostname\n");
free(ctrack->hostname);
if (!(ctrack->hostname=strdup(Host)))
{
DLOG_ERR("strdup hostname : out of memory\n");
return;
}
ctrack->hostname_discovered = true;
}
if (bDiscoveredL7 || bDiscoveredHostname)

View File

@ -15,11 +15,13 @@ typedef struct
t_l7proto l7proto;
bool bTamperInCutoff;
bool b_host_checked,b_host_matches,b_ah_check;
bool hostname_discovered;
char *hostname;
struct desync_profile *dp; // desync profile cache
} t_ctrack;
void apply_desync_profile(t_ctrack *ctrack, const struct sockaddr *dest);
bool ipcache_put_hostname(const struct in_addr *a4, const struct in6_addr *a6, const char *hostname);
void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *multisplit_pos, int *multisplit_count, uint8_t *split_flags);
void tamper_in(t_ctrack *ctrack, const struct sockaddr *client, uint8_t *segment,size_t segment_buffer_size,size_t *size);

File diff suppressed because it is too large Load Diff

View File

@ -494,6 +494,9 @@ static bool connect_remote_conn(tproxy_conn_t *conn)
{
int mss=0;
if (conn->track.hostname)
if (!ipcache_put_hostname(conn->dest.sa_family==AF_INET ? &((struct sockaddr_in*)&conn->dest)->sin_addr : NULL, conn->dest.sa_family==AF_INET6 ? &((struct sockaddr_in6*)&conn->dest)->sin6_addr : NULL , conn->track.hostname))
DLOG_ERR("ipcache_put_hostname: out of memory");
apply_desync_profile(&conn->track, (struct sockaddr *)&conn->dest);
if (conn->track.dp && conn->track.dp->mss)
@ -1087,7 +1090,8 @@ static bool resolve_complete(struct resolve_item *ri, struct tailhead *conn_list
if (!conn->track.hostname)
{
DBGPRINT("resolve_complete put hostname : %s\n", ri->dom);
conn->track.hostname = strdup(ri->dom);
if (!(conn->track.hostname = strdup(ri->dom)))
DLOG_ERR("dup hostname: out of memory\n");
}
sa46copy(&conn->dest, (struct sockaddr *)&ri->ss);
return proxy_mode_connect_remote(conn,conn_list);