diff --git a/common/ipt.sh b/common/ipt.sh index 5ca11ed..9790e7a 100644 --- a/common/ipt.sh +++ b/common/ipt.sh @@ -129,7 +129,7 @@ _fw_tpws4() # $3 - tpws port # $4 - lan interface names space separated # $5 - wan interface names space separated - [ "$DISABLE_IPV4" = "1" ] || { + [ "$DISABLE_IPV4" = "1" -o -z "$2" ] || { local i rule [ "$1" = 1 ] && prepare_tpws_fw4 @@ -159,7 +159,7 @@ _fw_tpws6() # $4 - lan interface names space separated # $5 - wan interface names space separated - [ "$DISABLE_IPV6" = "1" ] || { + [ "$DISABLE_IPV6" = "1" -o -z "$2" ] || { local i rule DNAT6 ipt_print_op $1 "$2" "tpws (port $3)" 6 @@ -197,7 +197,7 @@ _fw_nfqws_post4() # $2 - iptable filter for ipv4 # $3 - queue number # $4 - wan interface names space separated - [ "$DISABLE_IPV4" = "1" ] || { + [ "$DISABLE_IPV4" = "1" -o -z "$2" ] || { local i ipt_print_op $1 "$2" "nfqws postrouting (qnum $3)" @@ -218,7 +218,7 @@ _fw_nfqws_post6() # $2 - iptable filter for ipv6 # $3 - queue number # $4 - wan interface names space separated - [ "$DISABLE_IPV6" = "1" ] || { + [ "$DISABLE_IPV6" = "1" -o -z "$2" ] || { local i ipt_print_op $1 "$2" "nfqws postrouting (qnum $3)" 6 diff --git a/common/nft.sh b/common/nft.sh index c851c43..ead3495 100644 --- a/common/nft.sh +++ b/common/nft.sh @@ -83,16 +83,20 @@ cat << EOF | nft -f - add rule inet $ZAPRET_NFT_TABLE localnet_protect ip daddr $TPWS_LOCALHOST4 return comment "route_localnet allow access to tpws" add rule inet $ZAPRET_NFT_TABLE localnet_protect ip daddr 127.0.0.0/8 drop comment "route_localnet remote access protection" add rule inet $ZAPRET_NFT_TABLE input iif != lo jump localnet_protect - add chain inet $ZAPRET_NFT_TABLE postrouting { type filter hook postrouting priority 101; } + add chain inet $ZAPRET_NFT_TABLE postrouting { type filter hook postrouting priority 99; } flush chain inet $ZAPRET_NFT_TABLE postrouting - add chain inet $ZAPRET_NFT_TABLE predefrag { type filter hook output priority -401; } - flush chain inet $ZAPRET_NFT_TABLE predefrag - add rule inet $ZAPRET_NFT_TABLE predefrag mark and $DESYNC_MARK !=0 notrack comment "do not track nfqws generated packets to avoid nat tampering and defragmentation" add set inet $ZAPRET_NFT_TABLE lanif { type ifname; } add set inet $ZAPRET_NFT_TABLE wanif { type ifname; } add set inet $ZAPRET_NFT_TABLE wanif6 { type ifname; } add map inet $ZAPRET_NFT_TABLE link_local { type ifname : ipv6_addr; } EOF +# unfortunately this approach breaks udp desync of the connection initiating packet (new, first one) +# however without notrack ipfrag will not work +# postrouting priority : 99 - before srcnat, 101 - after srcnat +# add chain inet $ZAPRET_NFT_TABLE predefrag { type filter hook output priority -401; } +# flush chain inet $ZAPRET_NFT_TABLE predefrag +# add rule inet $ZAPRET_NFT_TABLE predefrag mark and $DESYNC_MARK !=0 notrack comment "do not track nfqws generated packets to avoid nat tampering and defragmentation" + } nft_del_chains() { @@ -206,6 +210,14 @@ nft_add_nfqws_flow_exempt_rule() # do not need this because of oifname @wanif/@wanif6 filter in forward chain #nft_add_rule flow_offload $(nft_reverse_nfqws_rule $1) return comment \"reverse flow offloading exemption\" } +nft_add_flow_offload_exemption() +{ + # "$1" - rule for ipv4 + # "$2" - rule for ipv6 + # "$3" - comment + [ "$DISABLE_IPV4" = "1" -o -z "$1" ] || nft_add_rule flow_offload oifname @wanif $1 ip daddr != @nozapret return comment \"$3\" + [ "$DISABLE_IPV6" = "1" -o -z "$2" ] || nft_add_rule flow_offload oifname @wanif6 $2 ip6 daddr != @nozapret6 return comment \"$3\" +} nft_hw_offload_supported() { @@ -351,7 +363,7 @@ _nft_fw_tpws4() # $2 - tpws port # $3 - not-empty if wan interface filtering required - [ "$DISABLE_IPV4" = "1" ] || { + [ "$DISABLE_IPV4" = "1" -o -z "$1" ] || { local filter="$1" port="$2" nft_print_op "$filter" "tpws (port $2)" 4 nft_add_rule dnat_output skuid != $WS_USER ${3:+oifname @wanif }$filter ip daddr != @nozapret dnat ip to $TPWS_LOCALHOST4:$port @@ -366,7 +378,7 @@ _nft_fw_tpws6() # $3 - lan interface names space separated # $4 - not-empty if wan interface filtering required - [ "$DISABLE_IPV6" = "1" ] || { + [ "$DISABLE_IPV6" = "1" -o -z "$1" ] || { local filter="$1" port="$2" DNAT6 i nft_print_op "$filter" "tpws (port $port)" 6 nft_add_rule dnat_output skuid != $WS_USER ${4:+oifname @wanif6 }$filter ip6 daddr != @nozapret6 dnat ip6 to [::1]:$port @@ -396,7 +408,7 @@ _nft_fw_nfqws_post4() # $2 - queue number # $3 - not-empty if wan interface filtering required - [ "$DISABLE_IPV4" = "1" ] || { + [ "$DISABLE_IPV4" = "1" -o -z "$1" ] || { local filter="$1" port="$2" rule nft_print_op "$filter" "nfqws postrouting (qnum $port)" 4 rule="${3:+oifname @wanif }$filter ip daddr != @nozapret" @@ -410,7 +422,7 @@ _nft_fw_nfqws_post6() # $2 - queue number # $3 - not-empty if wan interface filtering required - [ "$DISABLE_IPV6" = "1" ] || { + [ "$DISABLE_IPV6" = "1" -o -z "$1" ] || { local filter="$1" port="$2" rule nft_print_op "$filter" "nfqws postrouting (qnum $port)" 6 rule="${3:+oifname @wanif6 }$filter ip6 daddr != @nozapret6" diff --git a/files/fake/quic_short_header.bin b/files/fake/quic_short_header.bin new file mode 100644 index 0000000..7562c0a Binary files /dev/null and b/files/fake/quic_short_header.bin differ diff --git a/init.d/openwrt/custom-nfqws-quic4all-tspu b/init.d/openwrt/custom-nfqws-quic4all-tspu new file mode 100644 index 0000000..0aeba80 --- /dev/null +++ b/init.d/openwrt/custom-nfqws-quic4all-tspu @@ -0,0 +1,66 @@ +# this custom script in addition to MODE=nfqws runs desync to all QUIC initial packets, without ipset/hostlist filtering +# and also sends special fake on outgoing packets with short header and udp.length>=600 +# need to add to config : NFQWS_OPT_DESYNC_QUIC="--dpi-desync=fake" +# need to add to config : NFQWS_OPT_DESYNC_QUIC_SHORT_HEADER="--dpi-desync=fake --dpi-desync-any-protocol --dpi-desync-fake-unknown-udp=/opt/zapret/files/fake/quic_short_header.bin --dpi-desync-cutoff=n2" +# NOTE : do not use TTL fooling. chromium QUIC engine breaks sessions if TTL expired in transit received + +# this hack is for russian TSPU QUIC blocking +# without additional short header fake connections randomly hang + +QNUM2=$(($QNUM+10)) +QNUM3=$(($QNUM+11)) + +zapret_custom_daemons() +{ + # stop logic is managed by procd + + local MODE_OVERRIDE=nfqws + local opt + + start_daemons_procd + + opt="--qnum=$QNUM2 $NFQWS_OPT_BASE $NFQWS_OPT_DESYNC_QUIC" + run_daemon 100 $NFQWS "$opt" + + opt="--qnum=$QNUM3 $NFQWS_OPT_BASE $NFQWS_OPT_DESYNC_QUIC_SHORT_HEADER" + run_daemon 101 $NFQWS "$opt" +} +zapret_custom_firewall() +{ + # $1 - 1 - run, 0 - stop + + local MODE_OVERRIDE=nfqws + local f + local first_packets_only="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:3" + local desync="-m mark ! --mark $DESYNC_MARK/$DESYNC_MARK" + local udplen="-m length --length 600:1500" + local short_header4="-m u32 --u32 0>>22&0x3C@8>>24&0xC0=0x40" + + zapret_do_firewall_rules_ipt $1 + + f="-p udp --dport 443" + fw_nfqws_post $1 "$f $desync $first_packets_only" "$f $desync $first_packets_only" $QNUM2 + # this for ipv4 only. do not have access to ipv6 enabled TSPU to test + fw_nfqws_post $1 "$f $desync $udplen $short_header4" "" $QNUM3 + +} +zapret_custom_firewall_nft() +{ + # stop logic is not required + + local MODE_OVERRIDE=nfqws + local f + local first_packets_only="ct original packets 1-3" + local desync="mark and $DESYNC_MARK == 0" + local udplen="udp length 600-1500" + local short_header4="@th,64,2 1" + + zapret_apply_firewall_rules_nft + + f="udp dport 443" + nft_fw_nfqws_post $1 "$f $desync $first_packets_only" "$f $desync $first_packets_only" $QNUM2 + # this for ipv4 only. do not have access to ipv6 enabled TSPU to test + nft_fw_nfqws_post $1 "$f $desync $udplen $short_header4" "" $QNUM3 + # add flow offload exemption for the whole stream because we dont know when short_header rule triggers + nft_add_flow_offload_exemption "$f" "" "QUIC exemption for complex TSPU hack" +} diff --git a/init.d/sysv/custom-nfqws-quic4all-tspu b/init.d/sysv/custom-nfqws-quic4all-tspu new file mode 100644 index 0000000..8a29eb2 --- /dev/null +++ b/init.d/sysv/custom-nfqws-quic4all-tspu @@ -0,0 +1,66 @@ +# this custom script in addition to MODE=nfqws runs desync to all QUIC initial packets, without ipset/hostlist filtering +# and also sends special fake on outgoing packets with short header and udp.length>=600 +# need to add to config : NFQWS_OPT_DESYNC_QUIC="--dpi-desync=fake" +# need to add to config : NFQWS_OPT_DESYNC_QUIC_SHORT_HEADER="--dpi-desync=fake --dpi-desync-any-protocol --dpi-desync-fake-unknown-udp=/opt/zapret/files/fake/quic_short_header.bin --dpi-desync-cutoff=n2" +# NOTE : do not use TTL fooling. chromium QUIC engine breaks sessions if TTL expired in transit received + +# this hack is for russian TSPU QUIC blocking +# without additional short header fake connections randomly hang + +QNUM2=$(($QNUM+10)) +QNUM3=$(($QNUM+11)) + +zapret_custom_daemons() +{ + # stop logic is managed by procd + + local MODE_OVERRIDE=nfqws + local opt + + zapret_do_daemons $1 + + opt="--qnum=$QNUM2 $NFQWS_OPT_BASE $NFQWS_OPT_DESYNC_QUIC" + do_nfqws $1 100 "$opt" + + opt="--qnum=$QNUM3 $NFQWS_OPT_BASE $NFQWS_OPT_DESYNC_QUIC_SHORT_HEADER" + do_nfqws $1 101 "$opt" +} +zapret_custom_firewall() +{ + # $1 - 1 - run, 0 - stop + + local MODE_OVERRIDE=nfqws + local f + local first_packets_only="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:3" + local desync="-m mark ! --mark $DESYNC_MARK/$DESYNC_MARK" + local udplen="-m length --length 600:1500" + local short_header4="-m u32 --u32 0>>22&0x3C@8>>24&0xC0=0x40" + + zapret_do_firewall_rules_ipt $1 + + f="-p udp --dport 443" + fw_nfqws_post $1 "$f $desync $first_packets_only" "$f $desync $first_packets_only" $QNUM2 + # this for ipv4 only. do not have access to ipv6 enabled TSPU to test + fw_nfqws_post $1 "$f $desync $udplen $short_header4" "" $QNUM3 + +} +zapret_custom_firewall_nft() +{ + # stop logic is not required + + local MODE_OVERRIDE=nfqws + local f + local first_packets_only="ct original packets 1-3" + local desync="mark and $DESYNC_MARK == 0" + local udplen="udp length 600-1500" + local short_header4="@th,64,2 1" + + zapret_apply_firewall_rules_nft + + f="udp dport 443" + nft_fw_nfqws_post $1 "$f $desync $first_packets_only" "$f $desync $first_packets_only" $QNUM2 + # this for ipv4 only. do not have access to ipv6 enabled TSPU to test + nft_fw_nfqws_post $1 "$f $desync $udplen $short_header4" "" $QNUM3 + # add flow offload exemption for the whole stream because we dont know when short_header rule triggers + nft_add_flow_offload_exemption "$f" "" "QUIC exemption for complex TSPU hack" +}