Template
1
0
mirror of https://github.com/bol-van/zapret2.git synced 2026-03-14 14:21:41 +00:00

31 Commits
v0.2 ... v0.4

Author SHA1 Message Date
bol-van
7f4bdd5eb4 update changes.txt 2025-12-05 11:09:48 +03:00
bol-van
0588240d8d nfqws2: LUA_COMPAT_VER tracking 2025-12-05 11:07:14 +03:00
bol-van
b9e03ef71b nfqws2: --template <name> , --cookie 2025-12-05 10:23:33 +03:00
bol-van
9c0c7cfa8c init.d: remove stun_binding_req 2025-12-04 23:24:05 +03:00
bol-van
807ad5953b nfqws2: profile templates, remove stun_bindin_req detalisation 2025-12-04 23:22:27 +03:00
bol-van
ee031db3a1 blockcheck2: fix broken DNS cache 2025-12-04 14:57:00 +03:00
bol-van
93e4df72e5 zapret-lib: apply_arg_prefix, posdebug, argdebug 2025-12-03 16:08:12 +03:00
bol-van
e62d3919f4 nfqws2: proper conntrack position of replay pieces 2025-12-03 16:07:10 +03:00
bol-van
63414f8608 zapret-lib: typo 2025-12-02 21:55:09 +03:00
bol-van
18974e6c1f zapret-lib: separate execution plan replay function 2025-12-02 21:54:28 +03:00
bol-van
e61967ac2b nfqws2: profile names 2025-12-02 21:38:45 +03:00
bol-van
6010307667 nfqws2: post payload filter and range in exec plan, zapret-lib: duplicate range check logic 2025-12-02 20:48:03 +03:00
bol-van
04ceb589e0 nfqws2: desync orchestration luacalls 2025-12-02 17:49:23 +03:00
bol-van
69b08f0a36 install_easy 2025-12-02 15:45:57 +03:00
bol-van
ec6021898b install_bin: remove macos support 2025-12-02 11:48:56 +03:00
bol-van
711eefed3e init.d: custom scripts replace spaces with tabs 2025-12-02 11:44:43 +03:00
bol-van
032b24f5b4 init.d: openwrt beautify pid file name 2025-12-02 11:38:31 +03:00
bol-van
ef4b427836 nfqws2: test lua init scripts accessibility in --dry-run mode 2025-12-02 11:33:56 +03:00
bol-van
a795b0f10c init.d: 40-webserver replace spaces with tabs 2025-12-02 11:11:24 +03:00
bol-van
b56be286c9 init.d: 40-webserver custom script 2025-12-02 10:51:57 +03:00
bol-van
63668fc84e nft optimize rules 2025-12-02 10:49:50 +03:00
bol-van
837833feaf optimize nft chain activation 2025-12-02 09:19:09 +03:00
bol-van
90afc96283 fix 'which' function behavior with absolute paths 2025-12-02 08:53:03 +03:00
bol-van
b0455bfee2 nft do not apply FILTER_MARK to incoming 2025-12-01 20:07:07 +03:00
bol-van
d279fab308 nft optimize FILTER_MARK 2025-12-01 20:00:08 +03:00
bol-van
27695a892f nft remove unneeded nozapret set checks 2025-12-01 19:52:32 +03:00
bol-van
151226dfc2 update config.default 2025-12-01 16:44:47 +03:00
bol-van
ef78f8d30c ipset 2025-12-01 15:49:03 +03:00
bol-van
a01408a5c9 init.d launch scripts 2025-12-01 15:30:45 +03:00
bol-van
50ae834005 compile docs: fix path to /opt/zapret2 2025-12-01 13:19:45 +03:00
bol-van
b21e85bfe9 nfqws2: wrong function name 2025-11-30 17:41:45 +03:00
96 changed files with 6693 additions and 519 deletions

View File

@@ -275,44 +275,45 @@ mdig_cache()
mdig_resolve()
{
# $1 - ip version 4/6
# $2 - hostname, possibly with uri : rutracker.org/xxx/xxxx
local hostvar cachevar countvar count ip n sdom
# $2 - var to receive result
# $3 - hostname, possibly with uri : rutracker.org/xxx/xxxx
local hostvar cachevar countvar count n sdom
split_by_separator "$2" / sdom
split_by_separator "$3" / sdom
mdig_vars "$1" "$sdom"
if [ -n "$count" ]; then
n=$(random 0 $(($count-1)))
eval ip=\$${cachevar}_$n
echo $ip
eval $2=\$${cachevar}_$n
return 0
else
mdig_cache "$1" "$sdom" && mdig_resolve "$1" "$sdom"
mdig_cache "$1" "$sdom" && mdig_resolve "$1" "$2" "$sdom"
fi
}
mdig_resolve_all()
{
# $1 - ip version 4/6
# $2 - hostname
# $2 - var to receive result
# $3 - hostname
local hostvar cachevar countvar count ip ips n sdom
local hostvar cachevar countvar count ip__ ips__ n sdom
split_by_separator "$2" / sdom
split_by_separator "$3" / sdom
mdig_vars "$1" "$sdom"
if [ -n "$count" ]; then
n=0
while [ "$n" -le $count ]; do
eval ip=\$${cachevar}_$n
if [ -n "$ips" ]; then
ips="$ips $ip"
eval ip__=\$${cachevar}_$n
if [ -n "$ips__" ]; then
ips__="$ips__ $ip__"
else
ips="$ip"
ips__="$ip__"
fi
n=$(($n + 1))
done
echo "$ips"
eval $2="\$ips__"
return 0
else
mdig_cache "$1" "$sdom" && mdig_resolve_all "$1" "$sdom"
mdig_cache "$1" "$sdom" && mdig_resolve_all "$1" "$2" "$sdom"
fi
}
@@ -640,7 +641,7 @@ curl_with_dig()
local sdom suri ip
split_by_separator "$dom" / sdom suri
ip=$(mdig_resolve $1 $sdom)
mdig_resolve $1 ip $sdom
shift ; shift ; shift
if [ -n "$ip" ]; then
curl_with_subst_ip "$sdom" "$port" "$ip" "$@"
@@ -965,7 +966,7 @@ check_domain_port_block()
echo
echo \* port block tests ipv$IPV $1:$2
if netcat_setup; then
ips=$(mdig_resolve_all $IPV $1)
mdig_resolve_all $IPV ips $1
if [ -n "$ips" ]; then
for ip in $ips; do
if netcat_test $ip $2; then
@@ -1254,7 +1255,7 @@ check_dpi_ip_block()
echo "> testing $UNBLOCKED_DOM on it's original ip"
if curl_test $1 $UNBLOCKED_DOM; then
unblocked_ip=$(mdig_resolve $IPV $UNBLOCKED_DOM)
mdig_resolve $IPV unblocked_ip $UNBLOCKED_DOM
[ -n "$unblocked_ip" ] || {
echo $UNBLOCKED_DOM does not resolve. tests not possible.
return 1
@@ -1263,7 +1264,7 @@ check_dpi_ip_block()
echo "> testing $blocked_dom on $unblocked_ip ($UNBLOCKED_DOM)"
curl_test $1 $blocked_dom $unblocked_ip detail
blocked_ips=$(mdig_resolve_all $IPV $blocked_dom)
mdig_resolve_all $IPV blocked_ips $blocked_dom
for blocked_ip in $blocked_ips; do
echo "> testing $UNBLOCKED_DOM on $blocked_ip ($blocked_dom)"
curl_test $1 $UNBLOCKED_DOM $blocked_ip detail
@@ -1314,6 +1315,8 @@ check_domain_http_tcp()
# $3 - encrypted test : 0 = plain, 1 - encrypted with server reply risk, 2 - encrypted without server reply risk
# $4 - domain
local ips
# in case was interrupted before
pktws_ipt_unprepare_tcp $2
ws_kill
@@ -1325,7 +1328,8 @@ check_domain_http_tcp()
[ "$SKIP_PKTWS" = 1 ] || {
echo
echo preparing $PKTWSD redirection
pktws_ipt_prepare_tcp $2 "$(mdig_resolve_all $IPV $4)"
mdig_resolve_all $IPV ips $4
pktws_ipt_prepare_tcp $2 "$ips"
pktws_check_domain_http_bypass $1 $3 $4
@@ -1339,6 +1343,8 @@ check_domain_http_udp()
# $2 - port
# $3 - domain
local ips
# in case was interrupted before
pktws_ipt_unprepare_udp $2
ws_kill
@@ -1348,7 +1354,8 @@ check_domain_http_udp()
[ "$SKIP_PKTWS" = 1 ] || {
echo
echo preparing $PKTWSD redirection
pktws_ipt_prepare_udp $2 "$(mdig_resolve_all $IPV $3)"
mdig_resolve_all $IPV ips $4
pktws_ipt_prepare_udp $2 "$ips"
pktws_check_domain_http3_bypass $1 $3

View File

@@ -4,6 +4,10 @@ which()
# 'command -v' replacement does not work exactly the same way. it outputs shell aliases if present
# $1 - executable name
local IFS=:
[ "$1" != "${1#/}" ] && [ -x "$1" ] && {
echo "$1"
return 0
}
for p in $PATH; do
[ -x "$p/$1" ] && {
echo "$p/$1"
@@ -416,10 +420,10 @@ alloc_num()
std_ports()
{
NFQWS2_PORTS_TCP_IPT=$(replace_char - : $NFQWS_PORTS_TCP)
NFQWS2_PORTS_TCP_KEEPALIVE_IPT=$(replace_char - : $NFQWS_PORTS_TCP_KEEPALIVE)
NFQWS2_PORTS_UDP_IPT=$(replace_char - : $NFQWS_PORTS_UDP)
NFQWS2_PORTS_UDP_KEEPALIVE_IPT=$(replace_char - : $NFQWS_PORTS_UDP_KEEPALIVE)
NFQWS2_PORTS_TCP_IPT=$(replace_char - : $NFQWS2_PORTS_TCP)
NFQWS2_PORTS_TCP_KEEPALIVE_IPT=$(replace_char - : $NFQWS2_PORTS_TCP_KEEPALIVE)
NFQWS2_PORTS_UDP_IPT=$(replace_char - : $NFQWS2_PORTS_UDP)
NFQWS2_PORTS_UDP_KEEPALIVE_IPT=$(replace_char - : $NFQWS2_PORTS_UDP_KEEPALIVE)
}
has_bad_ws_options()

34
common/custom.sh Normal file
View File

@@ -0,0 +1,34 @@
custom_runner()
{
# $1 - function name
# $2+ - params
[ "$DISABLE_CUSTOM" = 1 ] && return 0
local n script FUNC=$1
shift
[ -d "$CUSTOM_DIR/custom.d" ] && {
dir_is_not_empty "$CUSTOM_DIR/custom.d" && {
for script in "$CUSTOM_DIR/custom.d/"*; do
[ -f "$script" ] || continue
unset -f $FUNC
. "$script"
existf $FUNC && $FUNC "$@"
done
}
}
}
alloc_qnum()
{
# $1 - target var name
alloc_num NUMPOOL_QNUM $1 65300 65399
}
alloc_dnum()
{
# alloc daemon number
# $1 - target var name
alloc_num NUMPOOL_DNUM $1 2000 2999
}

803
common/installer.sh Normal file
View File

@@ -0,0 +1,803 @@
GET_LIST_PREFIX=/ipset/get_
SYSTEMD_DIR=/lib/systemd
[ -d "$SYSTEMD_DIR" ] || SYSTEMD_DIR=/usr/lib/systemd
[ -d "$SYSTEMD_DIR" ] && SYSTEMD_SYSTEM_DIR="$SYSTEMD_DIR/system"
INIT_SCRIPT=/etc/init.d/zapret2
exitp()
{
echo
echo press enter to continue
read A
exit $1
}
extract_var_def()
{
# $1 - var name
# this sed script parses single or multi line shell var assignments with optional ' or " enclosure
sed -n \
"/^$1=\"/ {
:s1
/\".*\"/ {
p
b
}
N
t c1
b s1
:c1
}
/^$1='/ {
:s2
/'.*'/ {
p
b
}
N
t c2
b s2
:c2
}
/^$1=/p
"
}
replace_var_def()
{
# $1 - var name
# $2 - new val
# $3 - conf file
# this sed script replaces single or multi line shell var assignments with optional ' or " enclosure
local repl
if [ -z "$2" ]; then
repl="#$1="
elif contains "$2" " "; then
repl="$1=\"$2\""
else
repl="$1=$2"
fi
local script=\
"/^#*[[:space:]]*$1=\"/ {
:s1
/\".*\"/ {
c\\
$repl
b
}
N
t c1
b s1
:c1
}
/^#*[[:space:]]*$1='/ {
:s2
/'.*'/ {
c\\
$repl
b
}
N
t c2
b s2
:c2
}
/^#*[[:space:]]*$1=/c\\
$repl"
# there's incompatibility with -i option on BSD and busybox/GNU
if [ "$UNAME" = "Linux" ]; then
sed -i -e "$script" "$3"
else
sed -i '' -e "$script" "$3"
fi
}
parse_var_checked()
{
# $1 - file name
# $2 - var name
local tmp="/tmp/zvar-pid-$$.sh"
local v
cat "$1" | extract_var_def "$2" >"$tmp"
. "$tmp"
rm -f "$tmp"
eval v="\$$2"
# trim
v="$(echo "$v" | trim)"
eval $2=\""$v"\"
}
parse_vars_checked()
{
# $1 - file name
# $2,$3,... - var names
local f="$1"
shift
while [ -n "$1" ]; do
parse_var_checked "$f" $1
shift
done
}
edit_file()
{
# $1 - file name
local ed="$EDITOR"
[ -n "$ed" ] || {
for e in mcedit nano vim vi; do
exists "$e" && {
ed="$e"
break
}
done
}
[ -n "$ed" ] && "$ed" "$1"
}
echo_var()
{
local v delimeter delims=
eval v="\$$1"
if find_str_in_list $1 "$EDITVAR_NEWLINE_VARS"; then
echo "$1=\""
for delimeter in $EDITVAR_NEWLINE_DELIMETERS; do
delims="${delims:+$delims }-e "'"'"s/$delimeter/"'\\n'"$delimeter/g"'"'
done
echo "$v\"" | tr '\n' ' ' | tr -d '\r' | eval sed -e 's/^\ *//' -e 's/\ *$//' $delims
else
if contains "$v" " "; then
echo $1=\"$v\"
else
echo $1=$v
fi
fi
}
edit_vars()
{
# $1,$2,... - var names
local n=1 var tmp="/tmp/zvars-pid-$$.txt"
rm -f "$tmp"
while : ; do
eval var="\${$n}"
[ -n "$var" ] || break
echo_var $var >> "$tmp"
n=$(($n+1))
done
edit_file "$tmp" && parse_vars_checked "$tmp" "$@"
rm -f "$tmp"
}
list_vars()
{
while [ -n "$1" ] ; do
echo_var $1
shift
done
echo
}
openrc_test()
{
exists rc-update || return 1
# some systems do not usse openrc-init but launch openrc from inittab
[ "$INIT" = "openrc-init" ] || grep -qE "sysinit.*openrc" /etc/inittab 2>/dev/null
}
check_system()
{
# $1 - nonempty = do not fail on unknown rc system
echo \* checking system
SYSTEM=
SUBSYS=
SYSTEMCTL="$(whichq systemctl)"
get_fwtype
OPENWRT_FW3=
OPENWRT_FW4=
local info
UNAME=$(uname)
if [ "$UNAME" = "Linux" ]; then
# do not use 'exe' because it requires root
local INIT="$(sed 's/\x0/\n/g' /proc/1/cmdline | head -n 1)"
[ -L "$INIT" ] && INIT=$(readlink "$INIT")
INIT="$(basename "$INIT")"
# some distros include systemctl without systemd
if [ -d "$SYSTEMD_DIR" ] && [ -x "$SYSTEMCTL" ] && [ "$INIT" = "systemd" ]; then
SYSTEM=systemd
[ -f "$EXEDIR/init.d/sysv/functions" ] && . "$EXEDIR/init.d/sysv/functions"
elif [ -f "/etc/openwrt_release" ] && exists opkg || exists apk && exists uci && [ "$INIT" = "procd" ] ; then
SYSTEM=openwrt
OPENWRT_PACKAGER=opkg
OPENWRT_PACKAGER_INSTALL="opkg install"
OPENWRT_PACKAGER_UPDATE="opkg update"
exists apk && {
OPENWRT_PACKAGER=apk
OPENWRT_PACKAGER_INSTALL="apk add"
OPENWRT_PACKAGER_UPDATE=
}
info="package manager $OPENWRT_PACKAGER\n"
if openwrt_fw3 ; then
OPENWRT_FW3=1
info="${info}firewall fw3"
if is_ipt_flow_offload_avail; then
info="$info. hardware flow offloading requires iptables."
else
info="$info. flow offloading unavailable."
fi
elif openwrt_fw4; then
OPENWRT_FW4=1
info="${info}firewall fw4. flow offloading requires nftables."
fi
[ -f "$EXEDIR/init.d/openwrt/functions" ] && . "$EXEDIR/init.d/openwrt/functions"
elif openrc_test; then
SYSTEM=openrc
[ -f "$EXEDIR/init.d/sysv/functions" ] && . "$EXEDIR/init.d/sysv/functions"
else
echo system is not either systemd, openrc or openwrt based
echo easy installer can set up config settings but can\'t configure auto start
echo you have to do it manually. check readme.md for manual setup info.
if [ -n "$1" ] || ask_yes_no N "do you want to continue"; then
SYSTEM=linux
else
exitp 5
fi
[ -f "$EXEDIR/init.d/sysv/functions" ] && . "$EXEDIR/init.d/sysv/functions"
fi
linux_get_subsys
else
echo easy installer only supports Linux. check readme.md for supported systems and manual setup info.
exitp 5
fi
echo system is based on $SYSTEM
[ -n "$info" ] && printf "${info}\n"
}
get_free_space_mb()
{
df -m $PWD | awk '/[0-9]%/{print $(NF-2)}'
}
get_ram_kb()
{
grep MemTotal /proc/meminfo | awk '{print $2}'
}
get_ram_mb()
{
local R=$(get_ram_kb)
echo $(($R/1024))
}
crontab_del()
{
exists crontab || return
echo \* removing crontab entry
CRONTMP=/tmp/cron.tmp
crontab -l >$CRONTMP 2>/dev/null
if grep -q "$GET_LIST_PREFIX" $CRONTMP; then
echo removing following entries from crontab :
grep "$GET_LIST_PREFIX" $CRONTMP
grep -v "$GET_LIST_PREFIX" $CRONTMP >$CRONTMP.2
crontab $CRONTMP.2
rm -f $CRONTMP.2
fi
rm -f $CRONTMP
}
crontab_del_quiet()
{
exists crontab || return
CRONTMP=/tmp/cron.tmp
crontab -l >$CRONTMP 2>/dev/null
if grep -q "$GET_LIST_PREFIX" $CRONTMP; then
grep -v "$GET_LIST_PREFIX" $CRONTMP >$CRONTMP.2
crontab $CRONTMP.2
rm -f $CRONTMP.2
fi
rm -f $CRONTMP
}
crontab_add()
{
# $1 - hour min
# $2 - hour max
[ -x "$GET_LIST" ] && {
echo \* adding crontab entry
if exists crontab; then
CRONTMP=/tmp/cron.tmp
crontab -l >$CRONTMP 2>/dev/null
if grep -q "$GET_LIST_PREFIX" $CRONTMP; then
echo some entries already exist in crontab. check if this is corrent :
grep "$GET_LIST_PREFIX" $CRONTMP
else
end_with_newline <"$CRONTMP" || echo >>"$CRONTMP"
echo "$(random 0 59) $(random $1 $2) */2 * * $GET_LIST" >>$CRONTMP
crontab $CRONTMP
fi
rm -f $CRONTMP
else
echo '!!! CRON IS ABSENT !!! LISTS AUTO UPDATE WILL NOT WORK !!!'
fi
}
}
cron_ensure_running()
{
# if no crontabs present in /etc/cron openwrt init script does not launch crond. this is default
[ "$SYSTEM" = "openwrt" ] && {
/etc/init.d/cron enable
/etc/init.d/cron start
}
}
service_start_systemd()
{
echo \* starting zapret2 service
"$SYSTEMCTL" start zapret2 || {
echo could not start zapret2 service
exitp 30
}
}
service_stop_systemd()
{
echo \* stopping zapret2 service
"$SYSTEMCTL" daemon-reload
"$SYSTEMCTL" disable zapret2
"$SYSTEMCTL" stop zapret2
}
service_remove_systemd()
{
echo \* removing zapret2 service
rm -f "$SYSTEMD_SYSTEM_DIR/zapret2.service"
"$SYSTEMCTL" daemon-reload
}
timer_remove_systemd()
{
echo \* removing zapret2-list-update timer
"$SYSTEMCTL" daemon-reload
"$SYSTEMCTL" disable zapret2-list-update.timer
"$SYSTEMCTL" stop zapret2-list-update.timer
rm -f "$SYSTEMD_SYSTEM_DIR/zapret2-list-update.service" "$SYSTEMD_SYSTEM_DIR/zapret2-list-update.timer"
"$SYSTEMCTL" daemon-reload
}
install_sysv_init()
{
# $1 - "0"=disable
echo \* installing init script
[ -x "$INIT_SCRIPT" ] && {
"$INIT_SCRIPT" stop
"$INIT_SCRIPT" disable
}
ln -fs "$INIT_SCRIPT_SRC" "$INIT_SCRIPT"
[ "$1" != "0" ] && "$INIT_SCRIPT" enable
}
install_openrc_init()
{
# $1 - "0"=disable
echo \* installing init script
[ -x "$INIT_SCRIPT" ] && {
"$INIT_SCRIPT" stop
rc-update del zapret2
}
ln -fs "$INIT_SCRIPT_SRC" "$INIT_SCRIPT"
[ "$1" != "0" ] && rc-update add zapret2
}
service_remove_openrc()
{
echo \* removing zapret2 service
[ -x "$INIT_SCRIPT" ] && {
rc-update del zapret2
"$INIT_SCRIPT" stop
}
rm -f "$INIT_SCRIPT"
}
service_start_sysv()
{
[ -x "$INIT_SCRIPT" ] && {
echo \* starting zapret2 service
"$INIT_SCRIPT" start || {
echo could not start zapret2 service
exitp 30
}
}
}
service_stop_sysv()
{
[ -x "$INIT_SCRIPT" ] && {
echo \* stopping zapret2 service
"$INIT_SCRIPT" stop
}
}
service_remove_sysv()
{
echo \* removing zapret2 service
[ -x "$INIT_SCRIPT" ] && {
"$INIT_SCRIPT" disable
"$INIT_SCRIPT" stop
}
rm -f "$INIT_SCRIPT"
}
check_kmod()
{
[ -f "/lib/modules/$(uname -r)/$1.ko" ]
}
check_package_exists_openwrt()
{
[ -n "$($OPENWRT_PACKAGER list $1)" ]
}
check_package_openwrt()
{
case $OPENWRT_PACKAGER in
opkg)
[ -n "$(opkg list-installed $1)" ] && return 0
local what="$(opkg whatprovides $1 | tail -n +2 | head -n 1)"
[ -n "$what" ] || return 1
[ -n "$(opkg list-installed $what)" ]
;;
apk)
apk info -e $1
;;
esac
}
check_packages_openwrt()
{
for pkg in $@; do
check_package_openwrt $pkg || return
done
}
install_openwrt_iface_hook()
{
echo \* installing ifup hook
ln -fs "$OPENWRT_IFACE_HOOK" /etc/hotplug.d/iface
}
remove_openwrt_iface_hook()
{
echo \* removing ifup hook
rm -f /etc/hotplug.d/iface/??-zapret2
}
openwrt_fw_section_find()
{
# $1 - fw include postfix
# echoes section number
i=0
while true
do
path=$(uci -q get firewall.@include[$i].path)
[ -n "$path" ] || break
[ "$path" = "$OPENWRT_FW_INCLUDE$1" ] && {
echo $i
return 0
}
i=$(($i+1))
done
return 1
}
openwrt_fw_section_del()
{
# $1 - fw include postfix
local id="$(openwrt_fw_section_find $1)"
[ -n "$id" ] && {
uci delete firewall.@include[$id] && uci commit firewall
rm -f "$OPENWRT_FW_INCLUDE$1"
}
}
openwrt_fw_section_add()
{
openwrt_fw_section_find ||
{
uci add firewall include >/dev/null || return
echo -1
}
}
openwrt_fw_section_configure()
{
local id="$(openwrt_fw_section_add $1)"
[ -z "$id" ] ||
! uci set firewall.@include[$id].path="$OPENWRT_FW_INCLUDE" ||
! uci set firewall.@include[$id].reload="1" ||
! uci commit firewall &&
{
echo could not add firewall include
exitp 50
}
}
install_openwrt_firewall()
{
echo \* installing firewall script $1
[ -n "MODE" ] || {
echo should specify MODE in $ZAPRET_CONFIG
exitp 7
}
echo "linking : $FW_SCRIPT_SRC => $OPENWRT_FW_INCLUDE"
ln -fs "$FW_SCRIPT_SRC" "$OPENWRT_FW_INCLUDE"
openwrt_fw_section_configure $1
}
restart_openwrt_firewall()
{
echo \* restarting firewall
local FW=fw4
[ -n "$OPENWRT_FW3" ] && FW=fw3
exists $FW && $FW -q restart || {
echo could not restart firewall $FW
}
}
remove_openwrt_firewall()
{
echo \* removing firewall script
openwrt_fw_section_del
}
clear_ipset()
{
echo "* clearing ipset(s)"
# free some RAM
"$IPSET_DIR/create_ipset.sh" clear
}
write_config_var()
{
# $1 - mode var
local M
eval M="\$$1"
# replace / => \/
#M=${M//\//\\\/}
M=$(echo $M | sed 's/\//\\\//g' | trim)
grep -q "^[[:space:]]*$1=\|^#*[[:space:]]*$1=" "$ZAPRET_CONFIG" || {
# var does not exist in config. add it
echo $1= >>"$ZAPRET_CONFIG"
}
replace_var_def $1 "$M" "$ZAPRET_CONFIG"
}
no_prereq_exit()
{
echo could not install prerequisites
exitp 6
}
check_prerequisites_linux()
{
echo \* checking prerequisites
local s cmd PKGS UTILS req="curl curl"
local APTGET DNF YUM PACMAN ZYPPER EOPKG APK
case "$FWTYPE" in
iptables)
req="$req iptables iptables ip6tables iptables ipset ipset"
;;
nftables)
req="$req nft nftables"
;;
esac
PKGS=$(for s in $req; do echo $s; done |
while read cmd; do
read pkg
exists $cmd || echo $pkg
done | sort -u | xargs)
UTILS=$(for s in $req; do echo $s; done |
while read cmd; do
read pkg
echo $cmd
done | sort -u | xargs)
if [ -z "$PKGS" ] ; then
echo required utilities exist : $UTILS
else
echo \* installing prerequisites
echo packages required : $PKGS
APTGET=$(whichq apt-get)
DNF=$(whichq dnf)
YUM=$(whichq yum)
PACMAN=$(whichq pacman)
ZYPPER=$(whichq zypper)
EOPKG=$(whichq eopkg)
APK=$(whichq apk)
if [ -x "$APTGET" ] ; then
"$APTGET" update
"$APTGET" install -y --no-install-recommends $PKGS dnsutils || no_prereq_exit
elif [ -x "$DNF" ] ; then
"$DNF" -y install $PKGS || no_prereq_exit
elif [ -x "$YUM" ] ; then
"$YUM" -y install $PKGS || no_prereq_exit
elif [ -x "$PACMAN" ] ; then
"$PACMAN" -Syy
"$PACMAN" --noconfirm -S $PKGS || no_prereq_exit
elif [ -x "$ZYPPER" ] ; then
"$ZYPPER" --non-interactive install $PKGS || no_prereq_exit
elif [ -x "$EOPKG" ] ; then
"$EOPKG" -y install $PKGS || no_prereq_exit
elif [ -x "$APK" ] ; then
"$APK" update
# for alpine
[ "$FWTYPE" = iptables ] && [ -n "$($APK list ip6tables)" ] && PKGS="$PKGS ip6tables"
"$APK" add $PKGS || no_prereq_exit
else
echo supported package manager not found
echo you must manually install : $UTILS
exitp 5
fi
fi
}
removable_pkgs_openwrt()
{
local pkg PKGS2
[ -n "$OPENWRT_FW4" ] && PKGS2="$PKGS2 iptables-zz-legacy iptables ip6tables-zz-legacy ip6tables"
[ -n "$OPENWRT_FW3" ] && PKGS2="$PKGS2 nftables-json nftables-nojson nftables"
PKGS=
for pkg in $PKGS2; do
check_package_exists_openwrt $pkg && PKGS="${PKGS:+$PKGS }$pkg"
done
PKGS="ipset iptables-mod-extra iptables-mod-nfqueue iptables-mod-filter iptables-mod-ipopt iptables-mod-conntrack-extra iptables-mod-u32 ip6tables-mod-nat ip6tables-extra kmod-nft-queue gzip coreutils-sort coreutils-sleep curl $PKGS"
}
openwrt_fix_broken_apk_uninstall_scripts()
{
# at least in early snapshots with apk removing gnu gzip, sort, ... does not restore links to busybox
# system may become unusable
exists sort || { echo fixing missing sort; ln -fs /bin/busybox /usr/bin/sort; }
exists gzip || { echo fixing missing gzip; ln -fs /bin/busybox /bin/gzip; }
exists sleep || { echo fixing missing sleep; ln -fs /bin/busybox /bin/sleep; }
}
remove_extra_pkgs_openwrt()
{
local PKGS
echo \* remove dependencies
removable_pkgs_openwrt
echo these packages may have been installed by install_easy.sh : $PKGS
ask_yes_no N "do you want to remove them" && {
case $OPENWRT_PACKAGER in
opkg)
opkg remove --autoremove $PKGS
;;
apk)
apk del $PKGS
openwrt_fix_broken_apk_uninstall_scripts
;;
esac
}
}
check_prerequisites_openwrt()
{
echo \* checking prerequisites
local PKGS="curl" UPD=0 local pkg_iptables
case "$FWTYPE" in
iptables)
pkg_iptables=iptables
check_package_exists_openwrt iptables-zz-legacy && pkg_iptables=iptables-zz-legacy
PKGS="$PKGS ipset $pkg_iptables iptables-mod-extra iptables-mod-nfqueue iptables-mod-filter iptables-mod-ipopt iptables-mod-conntrack-extra iptables-mod-u32"
check_package_exists_openwrt ip6tables-zz-legacy && pkg_iptables=ip6tables-zz-legacy
[ "$DISABLE_IPV6" = 1 ] || PKGS="$PKGS $pkg_iptables ip6tables-mod-nat ip6tables-extra"
;;
nftables)
PKGS="$PKGS nftables kmod-nft-nat kmod-nft-offload kmod-nft-queue"
;;
esac
if check_packages_openwrt $PKGS ; then
echo everything is present
else
echo \* installing prerequisites
$OPENWRT_PACKAGER_UPDATE
UPD=1
$OPENWRT_PACKAGER_INSTALL $PKGS || {
echo could not install prerequisites
exitp 6
}
fi
is_linked_to_busybox gzip && {
echo
echo your system uses default busybox gzip. its several times slower than GNU gzip.
echo ip/host list scripts will run much faster with GNU gzip
echo installer can install GNU gzip but it requires about 100 Kb space
if ask_yes_no N "do you want to install GNU gzip"; then
[ "$UPD" = "0" ] && {
$OPENWRT_PACKAGER_UPDATE
UPD=1
}
$OPENWRT_PACKAGER_INSTALL --force-overwrite gzip
fi
}
is_linked_to_busybox sort && {
echo
echo your system uses default busybox sort. its much slower and consumes much more RAM than GNU sort
echo ip/host list scripts will run much faster with GNU sort
echo installer can install GNU sort but it requires about 100 Kb space
if ask_yes_no N "do you want to install GNU sort"; then
[ "$UPD" = "0" ] && {
$OPENWRT_PACKAGER_UPDATE
UPD=1
}
$OPENWRT_PACKAGER_INSTALL --force-overwrite coreutils-sort
fi
}
[ "$FSLEEP" = 0 ] && is_linked_to_busybox sleep && {
echo
echo no methods of sub-second sleep were found.
echo if you want to speed up blockcheck install coreutils-sleep. it requires about 40 Kb space
if ask_yes_no N "do you want to install COREUTILS sleep"; then
[ "$UPD" = "0" ] && {
$OPENWRT_PACKAGER_UPDATE
UPD=1
}
$OPENWRT_PACKAGER_INSTALL --force-overwrite coreutils-sleep
fsleep_setup
fi
}
}
select_ipv6()
{
local T=N
[ "$DISABLE_IPV6" != '1' ] && T=Y
local old6=$DISABLE_IPV6
echo
if ask_yes_no $T "enable ipv6 support"; then
DISABLE_IPV6=0
else
DISABLE_IPV6=1
fi
[ "$old6" != "$DISABLE_IPV6" ] && write_config_var DISABLE_IPV6
}
select_fwtype()
{
echo
[ $(get_ram_mb) -le 400 ] && {
echo WARNING ! you are running a low RAM system
echo WARNING ! nft requires lots of RAM to load huge ip sets, much more than ipsets require
echo WARNING ! if you need large lists it may be necessary to fall back to iptables+ipset firewall
}
echo select firewall type :
ask_list FWTYPE "iptables nftables" "$FWTYPE" && write_config_var FWTYPE
}
dry_run_nfqws_()
{
local NFQWS="$ZAPRET_BASE/nfq2/nfqws2"
echo verifying nfqws options
"$NFQWS" --dry-run ${WS_USER:+--user=$WS_USER} "$@"
}
dry_run_nfqws()
{
[ "$NFQWS2_ENABLE" = 1 ] || return 0
local opt="$NFQWS2_OPT" qn=${QNUM:-300}
filter_apply_hostlist_target opt
dry_run_nfqws_ --qnum=$qn $opt
echo NOTE ! LUA code validity cannot be verified at this stage !
}

333
common/ipt.sh Normal file
View File

@@ -0,0 +1,333 @@
std_ports
ipt_connbytes="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes"
IPSET_EXCLUDE="-m set ! --match-set nozapret"
IPSET_EXCLUDE6="-m set ! --match-set nozapret6"
IPBAN_EXCLUDE="-m set ! --match-set ipban"
IPBAN_EXCLUDE6="-m set ! --match-set ipban6"
ipt()
{
iptables $FW_EXTRA_PRE -C "$@" $FW_EXTRA_POST >/dev/null 2>/dev/null || iptables $FW_EXTRA_PRE -I "$@" $FW_EXTRA_POST
}
ipta()
{
iptables $FW_EXTRA_PRE -C "$@" $FW_EXTRA_POST >/dev/null 2>/dev/null || iptables $FW_EXTRA_PRE -A "$@" $FW_EXTRA_POST
}
ipt_del()
{
iptables $FW_EXTRA_PRE -C "$@" $FW_EXTRA_POST >/dev/null 2>/dev/null && iptables $FW_EXTRA_PRE -D "$@" $FW_EXTRA_POST
}
ipt_add_del()
{
on_off_function ipt ipt_del "$@"
}
ipta_add_del()
{
on_off_function ipta ipt_del "$@"
}
ipt6()
{
ip6tables -C "$@" >/dev/null 2>/dev/null || ip6tables -I "$@"
}
ipt6a()
{
ip6tables -C "$@" >/dev/null 2>/dev/null || ip6tables -A "$@"
}
ipt6_del()
{
ip6tables -C "$@" >/dev/null 2>/dev/null && ip6tables -D "$@"
}
ipt6_add_del()
{
on_off_function ipt6 ipt6_del "$@"
}
ipt6a_add_del()
{
on_off_function ipt6 ipt6a_del "$@"
}
is_ipt_flow_offload_avail()
{
# $1 = '' for ipv4, '6' for ipv6
grep -q FLOWOFFLOAD 2>/dev/null /proc/net/ip$1_tables_targets
}
filter_apply_ipset_target4()
{
# $1 - var name of ipv4 iptables filter
if [ "$MODE_FILTER" = "ipset" ]; then
eval $1="\"\$$1 -m set --match-set zapret dst\""
fi
}
filter_apply_ipset_target6()
{
# $1 - var name of ipv6 iptables filter
if [ "$MODE_FILTER" = "ipset" ]; then
eval $1="\"\$$1 -m set --match-set zapret6 dst\""
fi
}
filter_apply_ipset_target()
{
# $1 - var name of ipv4 iptables filter
# $2 - var name of ipv6 iptables filter
filter_apply_ipset_target4 $1
filter_apply_ipset_target6 $2
}
reverse_nfqws_rule_stream()
{
sed -e 's/-o /-i /g' -e 's/--dport /--sport /g' -e 's/--dports /--sports /g' -e 's/ dst$/ src/' -e 's/ dst / src /g' -e 's/--connbytes-dir=original/--connbytes-dir=reply/g' -e "s/-m mark ! --mark $DESYNC_MARK\/$DESYNC_MARK//g"
}
reverse_nfqws_rule()
{
echo "$@" | reverse_nfqws_rule_stream
}
ipt_mark_filter()
{
[ -n "$FILTER_MARK" ] && echo "-m mark --mark $FILTER_MARK/$FILTER_MARK"
}
ipt_print_op()
{
if [ "$1" = "1" ]; then
echo "Inserting ip$4tables rule for $3 : $2"
else
echo "Deleting ip$4tables rule for $3 : $2"
fi
}
_fw_nfqws_post4()
{
# $1 - 1 - add, 0 - del
# $2 - iptable filter for ipv4
# $3 - queue number
# $4 - wan interface names space separated
[ "$DISABLE_IPV4" = "1" -o -z "$2" ] || {
local i
ipt_print_op $1 "$2" "nfqws postrouting (qnum $3)"
rule="$(ipt_mark_filter) -m mark ! --mark $DESYNC_MARK/$DESYNC_MARK $2 $IPSET_EXCLUDE dst -j NFQUEUE --queue-num $3 --queue-bypass"
if [ -n "$4" ] ; then
for i in $4; do
ipt_add_del $1 POSTROUTING -t mangle -o $i $rule
done
else
ipt_add_del $1 POSTROUTING -t mangle $rule
fi
}
}
_fw_nfqws_post6()
{
# $1 - 1 - add, 0 - del
# $2 - iptable filter for ipv6
# $3 - queue number
# $4 - wan interface names space separated
[ "$DISABLE_IPV6" = "1" -o -z "$2" ] || {
local i
ipt_print_op $1 "$2" "nfqws postrouting (qnum $3)" 6
rule="$(ipt_mark_filter) -m mark ! --mark $DESYNC_MARK/$DESYNC_MARK $2 $IPSET_EXCLUDE6 dst -j NFQUEUE --queue-num $3 --queue-bypass"
if [ -n "$4" ] ; then
for i in $4; do
ipt6_add_del $1 POSTROUTING -t mangle -o $i $rule
done
else
ipt6_add_del $1 POSTROUTING -t mangle $rule
fi
}
}
fw_nfqws_post()
{
# $1 - 1 - add, 0 - del
# $2 - iptable filter for ipv4
# $3 - iptable filter for ipv6
# $4 - queue number
fw_nfqws_post4 $1 "$2" $4
fw_nfqws_post6 $1 "$3" $4
}
_fw_nfqws_pre4()
{
# $1 - 1 - add, 0 - del
# $2 - iptable filter for ipv4
# $3 - queue number
# $4 - wan interface names space separated
[ "$DISABLE_IPV4" = "1" -o -z "$2" ] || {
local i
ipt_print_op $1 "$2" "nfqws input+forward (qnum $3)"
rule="$2 $IPSET_EXCLUDE src -j NFQUEUE --queue-num $3 --queue-bypass"
if [ -n "$4" ] ; then
for i in $4; do
# iptables PREROUTING chain is before NAT. not possible to have DNATed ip's there
ipt_add_del $1 INPUT -t mangle -i $i $rule
ipt_add_del $1 FORWARD -t mangle -i $i $rule
done
else
ipt_add_del $1 INPUT -t mangle $rule
ipt_add_del $1 FORWARD -t mangle $rule
fi
}
}
_fw_nfqws_pre6()
{
# $1 - 1 - add, 0 - del
# $2 - iptable filter for ipv6
# $3 - queue number
# $4 - wan interface names space separated
[ "$DISABLE_IPV6" = "1" -o -z "$2" ] || {
local i
ipt_print_op $1 "$2" "nfqws input+forward (qnum $3)" 6
rule="$2 $IPSET_EXCLUDE6 src -j NFQUEUE --queue-num $3 --queue-bypass"
if [ -n "$4" ] ; then
for i in $4; do
# iptables PREROUTING chain is before NAT. not possible to have DNATed ip's there
ipt6_add_del $1 INPUT -t mangle -i $i $rule
ipt6_add_del $1 FORWARD -t mangle -i $i $rule
done
else
ipt6_add_del $1 INPUT -t mangle $rule
ipt6_add_del $1 FORWARD -t mangle $rule
fi
}
}
fw_nfqws_pre()
{
# $1 - 1 - add, 0 - del
# $2 - iptable filter for ipv4
# $3 - iptable filter for ipv6
# $4 - queue number
fw_nfqws_pre4 $1 "$2" $4
fw_nfqws_pre6 $1 "$3" $4
}
fw_reverse_nfqws_rule4()
{
fw_nfqws_pre4 $1 "$(reverse_nfqws_rule "$2")" $3
}
fw_reverse_nfqws_rule6()
{
fw_nfqws_pre6 $1 "$(reverse_nfqws_rule "$2")" $3
}
fw_reverse_nfqws_rule()
{
# ensure that modes relying on incoming traffic work
# $1 - 1 - add, 0 - del
# $2 - rule4
# $3 - rule6
# $4 - queue number
fw_reverse_nfqws_rule4 $1 "$2" $4
fw_reverse_nfqws_rule6 $1 "$3" $4
}
ipt_first_packets()
{
# $1 - packet count
[ -n "$1" -a "$1" != keepalive ] && [ "$1" -ge 1 ] && echo "$ipt_connbytes 1:$1"
}
ipt_do_nfqws_in_out()
{
# $1 - 1 - add, 0 - del
# $2 - tcp,udp
# $3 - ports
# $4 - PKT_OUT. special value : 'keepalive'
# $5 - PKT_IN
local f4 f6 first_packets_only
[ -n "$3" ] || return
[ -n "$4" -a "$4" != 0 ] &&
{
first_packets_only="$(ipt_first_packets $4)"
f4="-p $2 -m multiport --dports $3 $first_packets_only"
f6=$f4
filter_apply_ipset_target f4 f6
fw_nfqws_post $1 "$f4" "$f6" $QNUM
}
[ -n "$5" -a "$5" != 0 ] &&
{
first_packets_only="$(ipt_first_packets $5)"
f4="-p $2 -m multiport --dports $3 $first_packets_only"
f6=$f4
filter_apply_ipset_target f4 f6
fw_reverse_nfqws_rule $1 "$f4" "$f6" $QNUM
}
}
zapret_do_firewall_standard_nfqws_rules_ipt()
{
# $1 - 1 - add, 0 - del
[ "$NFQWS2_ENABLE" = 1 ] && {
ipt_do_nfqws_in_out $1 tcp "$NFQWS2_PORTS_TCP_IPT" "$NFQWS2_TCP_PKT_OUT" "$NFQWS2_TCP_PKT_IN"
ipt_do_nfqws_in_out $1 tcp "$NFQWS2_PORTS_TCP_KEEPALIVE_IPT" keepalive "$NFQWS2_TCP_PKT_IN"
ipt_do_nfqws_in_out $1 udp "$NFQWS2_PORTS_UDP_IPT" "$NFQWS2_UDP_PKT_OUT" "$NFQWS2_UDP_PKT_IN"
ipt_do_nfqws_in_out $1 udp "$NFQWS2_PORTS_UDP_KEEPALIVE_IPT" keepalive "$NFQWS2_UDP_PKT_IN"
}
}
zapret_do_firewall_standard_rules_ipt()
{
# $1 - 1 - add, 0 - del
zapret_do_firewall_standard_nfqws_rules_ipt $1
}
zapret_do_firewall_rules_ipt()
{
# $1 - 1 - add, 0 - del
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()
{
# $1 - 1 - add, 0 - del
if [ "$1" = 1 ]; then
echo Applying iptables
else
echo Clearing iptables
fi
# always create ipsets. ip_exclude ipset is required
[ "$1" = 1 ] && create_ipset no-update
zapret_do_firewall_rules_ipt "$@"
if [ "$1" = 1 ] ; then
existf flow_offloading_exempt && flow_offloading_exempt
else
existf flow_offloading_unexempt && flow_offloading_unexempt
fi
return 0
}

33
common/linux_daemons.sh Normal file
View File

@@ -0,0 +1,33 @@
standard_mode_nfqws()
{
# $1 - 1 - run, 0 - stop
local opt
[ "$NFQWS2_ENABLE" = 1 ] && check_bad_ws_options $1 "$NFQWS2_OPT" && {
opt="--qnum=$QNUM $NFQWS2_OPT"
filter_apply_hostlist_target opt
do_nfqws $1 1 "$opt"
}
}
standard_mode_daemons()
{
# $1 - 1 - run, 0 - stop
standard_mode_nfqws $1
}
zapret_do_daemons()
{
# $1 - 1 - run, 0 - stop
standard_mode_daemons $1
custom_runner zapret_custom_daemons $1
return 0
}
zapret_run_daemons()
{
zapret_do_daemons 1 "$@"
}
zapret_stop_daemons()
{
zapret_do_daemons 0 "$@"
}

40
common/linux_fw.sh Normal file
View File

@@ -0,0 +1,40 @@
set_conntrack_liberal_mode()
{
[ -n "$SKIP_CONNTRACK_LIBERAL_MODE" ] || sysctl -w net.netfilter.nf_conntrack_tcp_be_liberal=$1
}
zapret_do_firewall()
{
linux_fwtype
[ "$1" = 1 -a -n "$INIT_FW_PRE_UP_HOOK" ] && $INIT_FW_PRE_UP_HOOK
[ "$1" = 0 -a -n "$INIT_FW_PRE_DOWN_HOOK" ] && $INIT_FW_PRE_DOWN_HOOK
case "$FWTYPE" in
iptables)
zapret_do_firewall_ipt "$@"
;;
nftables)
zapret_do_firewall_nft "$@"
;;
esac
# russian DPI sends RST,ACK with wrong ACK.
# this is sometimes treated by conntrack as invalid and connbytes fw rules do not pass RST packet to nfqws.
# switch on liberal mode on zapret firewall start and switch off on zapret firewall stop
# this is only required for processing incoming bad RSTs. incoming rules are only applied in autohostlist mode
# calling this after firewall because conntrack module can be not loaded before applying conntrack firewall rules
[ "$MODE_FILTER" = "autohostlist" ] && set_conntrack_liberal_mode $1
[ "$1" = 1 -a -n "$INIT_FW_POST_UP_HOOK" ] && $INIT_FW_POST_UP_HOOK
[ "$1" = 0 -a -n "$INIT_FW_POST_DOWN_HOOK" ] && $INIT_FW_POST_DOWN_HOOK
return 0
}
zapret_apply_firewall()
{
zapret_do_firewall 1 "$@"
}
zapret_unapply_firewall()
{
zapret_do_firewall 0 "$@"
}

24
common/linux_iphelper.sh Normal file
View File

@@ -0,0 +1,24 @@
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
[ -d "/sys/class/net/$1" ] && {
find "/sys/class/net/$1" -follow -maxdepth 1 -name "lower_*" |
{
local l lower lowers
while read lower; do
lower="$(basename "$lower")"
l="${lower#lower_*}"
[ "$l" != "$lower" ] && append_separator_list lowers ' ' '' "$l"
done
printf "$lowers"
}
}
}

55
common/list.sh Normal file
View File

@@ -0,0 +1,55 @@
HOSTLIST_MARKER="<HOSTLIST>"
HOSTLIST_NOAUTO_MARKER="<HOSTLIST_NOAUTO>"
find_hostlists()
{
[ -n "$HOSTLIST_BASE" ] || HOSTLIST_BASE="$ZAPRET_BASE/ipset"
HOSTLIST="$HOSTLIST_BASE/zapret-hosts.txt.gz"
[ -f "$HOSTLIST" ] || HOSTLIST="$HOSTLIST_BASE/zapret-hosts.txt"
[ -f "$HOSTLIST" ] || HOSTLIST=
HOSTLIST_USER="$HOSTLIST_BASE/zapret-hosts-user.txt.gz"
[ -f "$HOSTLIST_USER" ] || HOSTLIST_USER="$HOSTLIST_BASE/zapret-hosts-user.txt"
[ -f "$HOSTLIST_USER" ] || HOSTLIST_USER=
HOSTLIST_EXCLUDE="$HOSTLIST_BASE/zapret-hosts-user-exclude.txt.gz"
[ -f "$HOSTLIST_EXCLUDE" ] || HOSTLIST_EXCLUDE="$HOSTLIST_BASE/zapret-hosts-user-exclude.txt"
[ -f "$HOSTLIST_EXCLUDE" ] || HOSTLIST_EXCLUDE=
HOSTLIST_AUTO="$HOSTLIST_BASE/zapret-hosts-auto.txt"
HOSTLIST_AUTO_DEBUGLOG="$HOSTLIST_BASE/zapret-hosts-auto-debug.log"
}
filter_apply_hostlist_target()
{
# $1 - var name of nfqws params
local v parm parm1 parm2 parm3 parm4 parm5 parm6 parm7 parm8 parmNA
eval v="\$$1"
if contains "$v" "$HOSTLIST_MARKER" || contains "$v" "$HOSTLIST_NOAUTO_MARKER"; then
[ "$MODE_FILTER" = hostlist -o "$MODE_FILTER" = autohostlist ] &&
{
find_hostlists
parm1="${HOSTLIST_USER:+--hostlist=$HOSTLIST_USER}"
parm2="${HOSTLIST:+--hostlist=$HOSTLIST}"
parm3="${HOSTLIST_EXCLUDE:+--hostlist-exclude=$HOSTLIST_EXCLUDE}"
[ "$MODE_FILTER" = autohostlist ] &&
{
parm4="--hostlist-auto=$HOSTLIST_AUTO"
parm5="${AUTOHOSTLIST_FAIL_THRESHOLD:+--hostlist-auto-fail-threshold=$AUTOHOSTLIST_FAIL_THRESHOLD}"
parm6="${AUTOHOSTLIST_FAIL_TIME:+--hostlist-auto-fail-time=$AUTOHOSTLIST_FAIL_TIME}"
parm7="${AUTOHOSTLIST_RETRANS_THRESHOLD:+--hostlist-auto-retrans-threshold=$AUTOHOSTLIST_RETRANS_THRESHOLD}"
parm8="--hostlist=$HOSTLIST_AUTO"
}
parm="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm4:+ $parm4}${parm5:+ $parm5}${parm6:+ $parm6}${parm7:+ $parm7}"
parmNA="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm8:+ $parm8}"
}
v="$(replace_str $HOSTLIST_NOAUTO_MARKER "$parmNA" "$v")"
v="$(replace_str $HOSTLIST_MARKER "$parm" "$v")"
[ "$MODE_FILTER" = autohostlist -a "$AUTOHOSTLIST_DEBUGLOG" = 1 ] && {
v="$v --hostlist-auto-debug=$HOSTLIST_AUTO_DEBUGLOG"
}
eval $1=\""$v"\"
fi
}

708
common/nft.sh Normal file
View File

@@ -0,0 +1,708 @@
[ -n "$ZAPRET_NFT_TABLE" ] || ZAPRET_NFT_TABLE=zapret2
nft_connbytes="ct original packets"
# required for : nft -f -
create_dev_stdin
std_ports
nft_create_table()
{
nft add table inet $ZAPRET_NFT_TABLE
}
nft_del_table()
{
nft delete table inet $ZAPRET_NFT_TABLE 2>/dev/null
}
nft_list_table()
{
nft -t list table inet $ZAPRET_NFT_TABLE
}
nft_create_set()
{
# $1 - set name
# $2 - params
nft create set inet $ZAPRET_NFT_TABLE $1 "{ $2 }" 2>/dev/null
}
nft_del_set()
{
# $1 - set name
nft delete set inet $ZAPRET_NFT_TABLE $1
}
nft_flush_set()
{
# $1 - set name
nft flush set inet $ZAPRET_NFT_TABLE $1
}
nft_flush_chain()
{
# $1 - set name
nft flush chain inet $ZAPRET_NFT_TABLE $1
}
nft_set_exists()
{
# $1 - set name
nft -t list set inet $ZAPRET_NFT_TABLE $1 2>/dev/null >/dev/null
}
nft_flush_chain()
{
# $1 - chain name
nft flush chain inet $ZAPRET_NFT_TABLE $1
}
nft_chain_empty()
{
# $1 - chain name
local count=$(nft list chain inet $ZAPRET_NFT_TABLE prerouting | wc -l)
[ "$count" -le 4 ]
}
nft_rule_exists()
{
# $1 - chain
# $2 - rule
local rule
# convert rule to nft output form
nft_flush_chain ruletest
nft_add_rule ruletest "$2"
rule=$(nft list chain inet $ZAPRET_NFT_TABLE ruletest | sed -n '3s/\t//gp')
nft_flush_chain ruletest
local yes=$(nft list chain inet $ZAPRET_NFT_TABLE $1 | sed -n "s/^[\t]*$rule\$/1/p")
[ -n "$yes" ]
}
nft_del_all_chains_from_table()
{
# $1 - table_name with or without family
# delete all chains with possible references to each other
# cannot just delete all in the list because of references
# avoid infinite loops
local chains deleted=1 error=1
while [ -n "$deleted" -a -n "$error" ]; do
chains=$(nft -t list table $1 2>/dev/null | sed -nre "s/^[ ]*chain ([^ ]+) \{/\1/p" | xargs)
[ -n "$chains" ] || break
deleted=
error=
for chain in $chains; do
if nft delete chain $1 $chain 2>/dev/null; then
deleted=1
else
error=1
fi
done
done
}
# ipset checks cost some CPU. do not populate jump from hook until something is added to the chain
nft_activate_chain4()
{
# $1 - chain name
# $2 - saddr/daddr
local b rule markf= act
[ "$DISABLE_IPV4" = "1" ] || {
eval act="\$${1}_act4"
[ -n "$act" ] && return
b=0
nft_wanif_filter_present && b=1
[ "$2" = daddr ] && markf=$(nft_mark_filter)
rule="meta mark and $DESYNC_MARK == 0 $markf"
[ $b = 1 ] && rule="$rule oifname @wanif"
rule="$rule ip $2 != @nozapret jump $1"
nft_rule_exists ${1}_hook "$rule" || nft_add_rule ${1}_hook $rule
eval ${1}_act4=1
}
}
nft_activate_chain6()
{
# $1 - chain name
# $2 - saddr/daddr
local b rule markf=
[ "$DISABLE_IPV6" = "1" ] || {
eval act="\$${1}_act6"
[ -n "$act" ] && return
b=0
nft_wanif6_filter_present && b=1
[ "$2" = daddr ] && markf=$(nft_mark_filter)
rule="meta mark and $DESYNC_MARK == 0 $markf"
[ $b = 1 ] && rule="$rule oifname @wanif6"
rule="$rule ip6 $2 != @nozapret6 jump $1"
nft_rule_exists ${1}_hook "$rule" || nft_add_rule ${1}_hook $rule
eval ${1}_act6=1
}
}
nft_create_chains()
{
cat << EOF | nft -f -
add chain inet $ZAPRET_NFT_TABLE forward_hook { type filter hook forward priority -1; }
flush chain inet $ZAPRET_NFT_TABLE forward_hook
add chain inet $ZAPRET_NFT_TABLE flow_offload
flush chain inet $ZAPRET_NFT_TABLE flow_offload
add chain inet $ZAPRET_NFT_TABLE flow_offload_zapret
flush chain inet $ZAPRET_NFT_TABLE flow_offload_zapret
add chain inet $ZAPRET_NFT_TABLE flow_offload_always
flush chain inet $ZAPRET_NFT_TABLE flow_offload_always
add chain inet $ZAPRET_NFT_TABLE postrouting
flush chain inet $ZAPRET_NFT_TABLE postrouting
add chain inet $ZAPRET_NFT_TABLE postrouting_hook { type filter hook postrouting priority 99; }
flush chain inet $ZAPRET_NFT_TABLE postrouting_hook
add chain inet $ZAPRET_NFT_TABLE postnat
flush chain inet $ZAPRET_NFT_TABLE postnat
add chain inet $ZAPRET_NFT_TABLE postnat_hook { type filter hook postrouting priority 101; }
flush chain inet $ZAPRET_NFT_TABLE postnat_hook
add chain inet $ZAPRET_NFT_TABLE prerouting_hook { type filter hook prerouting priority -99; }
flush chain inet $ZAPRET_NFT_TABLE prerouting_hook
add chain inet $ZAPRET_NFT_TABLE prerouting
flush chain inet $ZAPRET_NFT_TABLE prerouting
add chain inet $ZAPRET_NFT_TABLE prenat_hook { type filter hook prerouting priority -101; }
flush chain inet $ZAPRET_NFT_TABLE prenat_hook
add chain inet $ZAPRET_NFT_TABLE prenat
flush chain inet $ZAPRET_NFT_TABLE prenat
add chain inet $ZAPRET_NFT_TABLE predefrag { type filter hook output priority -401; }
flush chain inet $ZAPRET_NFT_TABLE predefrag
add chain inet $ZAPRET_NFT_TABLE predefrag_nfqws
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 & 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 wanif { type ifname; }
add set inet $ZAPRET_NFT_TABLE wanif6 { type ifname; }
add chain inet $ZAPRET_NFT_TABLE ruletest
flush chain inet $ZAPRET_NFT_TABLE ruletest
EOF
[ -n "$POSTNAT_ALL" ] && {
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_hook 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_hook 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_hook icmpv6 type time-exceeded ct mark and $DESYNC_MARK != 0 drop comment \"nfqws related : prevent ttl expired socket errors\"
}
}
}
nft_del_chains()
{
# do not delete all chains because of additional user hooks
# they must be inside zapret table to use nfsets
cat << EOF | nft -f - 2>/dev/null
delete chain inet $ZAPRET_NFT_TABLE postrouting_hook
delete chain inet $ZAPRET_NFT_TABLE postnat_hook
delete chain inet $ZAPRET_NFT_TABLE prerouting_hook
delete chain inet $ZAPRET_NFT_TABLE prenat_hook
delete chain inet $ZAPRET_NFT_TABLE forward_hook
delete chain inet $ZAPRET_NFT_TABLE postrouting
delete chain inet $ZAPRET_NFT_TABLE postnat
delete chain inet $ZAPRET_NFT_TABLE prerouting
delete chain inet $ZAPRET_NFT_TABLE prenat
delete chain inet $ZAPRET_NFT_TABLE predefrag
delete chain inet $ZAPRET_NFT_TABLE predefrag_nfqws
delete chain inet $ZAPRET_NFT_TABLE flow_offload
delete chain inet $ZAPRET_NFT_TABLE flow_offload_zapret
delete chain inet $ZAPRET_NFT_TABLE flow_offload_always
delete chain inet $ZAPRET_NFT_TABLE ruletest
EOF
# unfortunately this approach breaks udp desync of the connection initiating packet (new, first one)
# delete chain inet $ZAPRET_NFT_TABLE predefrag
}
nft_del_flowtable()
{
nft delete flowtable inet $ZAPRET_NFT_TABLE ft 2>/dev/null
}
nft_create_or_update_flowtable()
{
# $1 = flags ('offload' for hw offload)
# $2,$3,$4,... - interfaces
# can be called multiple times to add interfaces. interfaces can only be added , not removed
local flags=$1 devices makelist
shift
# warning ! nft versions at least up to 1.0.1 do not allow interface names starting with digit in flowtable and do not allow quoting
# warning ! openwrt fixes this in post-21.x snapshots with special nft patch
# warning ! in traditional linux distros nft is unpatched and will fail with quoted interface definitions if unfixed
[ -n "$flags" ] && flags="flags $flags;"
for makelist in make_quoted_comma_list make_comma_list; do
$makelist devices "$@"
[ -n "$devices" ] && devices="devices={$devices};"
nft add flowtable inet $ZAPRET_NFT_TABLE ft "{ hook ingress priority -1; $flags $devices }" && break
done
}
nft_flush_ifsets()
{
cat << EOF | nft -f - 2>/dev/null
flush set inet $ZAPRET_NFT_TABLE wanif
flush set inet $ZAPRET_NFT_TABLE wanif6
EOF
}
nft_list_ifsets()
{
nft list set inet $ZAPRET_NFT_TABLE wanif
nft list set inet $ZAPRET_NFT_TABLE wanif6
nft list flowtable inet $ZAPRET_NFT_TABLE ft 2>/dev/null
}
nft_create_firewall()
{
nft_create_table
nft_del_flowtable
nft_create_chains
}
nft_del_firewall()
{
nft_del_chains
nft_del_flowtable
# leave ifsets and ipsets because they may be used by custom rules
}
nft_add_rule()
{
# $1 - chain
# $2,$3,... - rule(s)
local chain="$1"
shift
nft add rule inet $ZAPRET_NFT_TABLE $chain $FW_EXTRA_PRE "$@"
}
nft_insert_rule()
{
# $1 - chain
# $2,$3,... - rule(s)
local chain="$1"
shift
nft insert rule inet $ZAPRET_NFT_TABLE $chain $FW_EXTRA_PRE "$@"
}
nft_add_set_element()
{
# $1 - set or map name
# $2 - element
[ -z "$2" ] || nft add element inet $ZAPRET_NFT_TABLE $1 "{ $2 }"
}
nft_add_set_elements()
{
# $1 - set or map name
# $2,$3,... - element(s)
local set="$1" elements
shift
make_comma_list elements "$@"
nft_add_set_element $set "$elements"
}
nft_reverse_nfqws_rule()
{
echo "$@" | sed -e 's/oifname /iifname /g' -e 's/dport /sport /g' -e 's/daddr /saddr /g' -e 's/ct original /ct reply /g' -e "s/mark and $DESYNC_MARK == 0//g"
}
nft_add_nfqws_flow_exempt_rule()
{
# $1 - rule (must be all filters in one var)
local FW_EXTRA_POST= FW_EXTRA_PRE=
[ "$FLOWOFFLOAD" = 'software' -o "$FLOWOFFLOAD" = 'hardware' ] && \
nft_insert_rule flow_offload_zapret "$1" return comment \"direct flow offloading exemption\"
}
nft_apply_flow_offloading()
{
# ft can be absent
nft_add_rule flow_offload_always flow add @ft 2>/dev/null && {
nft_add_rule flow_offload_always counter comment \"if offload works here must not be too much traffic\"
[ "$DISABLE_IPV4" = "1" ] || {
# allow only outgoing packets to initiate flow offload
nft_add_rule forward_hook meta l4proto "{ tcp, udp }" oifname @wanif jump flow_offload
nft_add_rule flow_offload ip daddr == @nozapret goto flow_offload_always
}
[ "$DISABLE_IPV6" = "1" ] || {
nft_add_rule forward_hook meta l4proto "{ tcp, udp }" oifname @wanif6 jump flow_offload
nft_add_rule flow_offload ip6 daddr == @nozapret6 goto flow_offload_always
}
nft_add_rule flow_offload jump flow_offload_zapret
nft_add_rule flow_offload_zapret goto flow_offload_always
}
}
nft_filter_apply_ipset_target4()
{
# $1 - var name of ipv4 nftables filter
if [ "$MODE_FILTER" = "ipset" ]; then
eval $1="\"\$$1 ip daddr @zapret\""
fi
}
nft_filter_apply_ipset_target6()
{
# $1 - var name of ipv6 nftables filter
if [ "$MODE_FILTER" = "ipset" ]; then
eval $1="\"\$$1 ip6 daddr @zapret6\""
fi
}
nft_filter_apply_ipset_target()
{
# $1 - var name of ipv4 nftables filter
# $2 - var name of ipv6 nftables filter
nft_filter_apply_ipset_target4 $1
nft_filter_apply_ipset_target6 $2
}
nft_mark_filter()
{
[ -n "$FILTER_MARK" ] && echo "mark and $FILTER_MARK != 0"
}
nft_script_add_ifset_element()
{
# $1 - set name
# $2 - space separated elements
local elements
[ -n "$2" ] && {
make_quoted_comma_list elements $2
script="${script}
add element inet $ZAPRET_NFT_TABLE $1 { $elements }"
}
}
nft_fill_ifsets()
{
# $1 - space separated lan interface names
# $2 - space separated wan interface names
# $3 - space separated wan6 interface names
# 4,5,6 is needed for pppoe+openwrt case. looks like it's not easily possible to resolve ethernet device behind a pppoe interface
# $4 - space separated lan physical interface names (optional)
# $5 - space separated wan physical interface names (optional)
# $6 - space separated wan6 physical interface names (optional)
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
# calling all in one shot helps not to waste cpu time many times
script="flush set inet $ZAPRET_NFT_TABLE wanif
flush set inet $ZAPRET_NFT_TABLE wanif6"
[ "$DISABLE_IPV4" = "1" ] || nft_script_add_ifset_element wanif "$2"
[ "$DISABLE_IPV6" = "1" ] || nft_script_add_ifset_element wanif6 "$3"
echo "$script" | nft -f -
case "$FLOWOFFLOAD" in
software)
ALLDEVS=$(unique $1 $2 $3)
# unbound flowtable may cause error in older nft version
nft_create_or_update_flowtable '' $ALLDEVS 2>/dev/null
;;
hardware)
ALLDEVS=$(unique $1 $2 $3 $4 $5 $6)
# first create unbound flowtable. may cause error in older nft version
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
# 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 && 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
}
nft_only()
{
linux_fwtype
case "$FWTYPE" in
nftables)
"$@"
;;
esac
}
nft_print_op()
{
echo "Inserting nftables ipv$3 rule for $2 : $1"
}
is_postnat()
{
[ "$POSTNAT" != 0 -o "$POSTNAT_ALL" = 1 ]
}
get_postchain()
{
if is_postnat ; then
echo -n postnat
else
echo -n postrouting
fi
}
get_prechain()
{
if is_postnat ; then
echo -n prenat
else
echo -n prerouting
fi
}
_nft_fw_nfqws_post4()
{
# $1 - filter ipv4
# $2 - queue number
# $3 - not-empty if wan interface filtering required
[ "$DISABLE_IPV4" = "1" -o -z "$1" ] || {
local filter="$1" port="$2" rule chain=$(get_postchain) setmark
nft_print_op "$filter" "nfqws postrouting (qnum $port)" 4
rule="meta nfproto ipv4 $filter"
is_postnat && setmark="meta mark set meta mark or $DESYNC_MARK_POSTNAT"
nft_insert_rule $chain $rule $setmark $CONNMARKER $FW_EXTRA_POST queue num $port bypass
nft_add_nfqws_flow_exempt_rule "$rule"
nft_activate_chain4 $chain daddr
}
}
_nft_fw_nfqws_post6()
{
# $1 - filter ipv6
# $2 - queue number
# $3 - not-empty if wan interface filtering required
[ "$DISABLE_IPV6" = "1" -o -z "$1" ] || {
local filter="$1" port="$2" rule chain=$(get_postchain) setmark
nft_print_op "$filter" "nfqws postrouting (qnum $port)" 6
rule="meta nfproto ipv6 $filter"
is_postnat && setmark="meta mark set meta mark or $DESYNC_MARK_POSTNAT"
nft_insert_rule $chain $rule $setmark $CONNMARKER $FW_EXTRA_POST queue num $port bypass
nft_add_nfqws_flow_exempt_rule "$rule"
nft_activate_chain6 $chain daddr
}
}
nft_fw_nfqws_post()
{
# $1 - filter ipv4
# $2 - filter ipv6
# $3 - queue number
nft_fw_nfqws_post4 "$1" $3
nft_fw_nfqws_post6 "$2" $3
}
_nft_fw_nfqws_pre4()
{
# $1 - filter ipv4
# $2 - queue number
# $3 - not-empty if wan interface filtering required
[ "$DISABLE_IPV4" = "1" -o -z "$1" ] || {
local filter="$1" port="$2" rule chain=$(get_prechain)
nft_print_op "$filter" "nfqws prerouting (qnum $port)" 4
rule="meta nfproto ipv4 $filter"
nft_insert_rule $chain $rule $CONNMARKER $FW_EXTRA_POST queue num $port bypass
nft_activate_chain4 $chain saddr
}
}
_nft_fw_nfqws_pre6()
{
# $1 - filter ipv6
# $2 - queue number
# $3 - not-empty if wan interface filtering required
[ "$DISABLE_IPV6" = "1" -o -z "$1" ] || {
local filter="$1" port="$2" rule chain=$(get_prechain)
nft_print_op "$filter" "nfqws prerouting (qnum $port)" 6
rule="meta nfproto ipv6 $filter"
nft_insert_rule $chain $rule $CONNMARKER $FW_EXTRA_POST queue num $port bypass
nft_activate_chain6 $chain saddr
}
}
nft_fw_nfqws_pre()
{
# $1 - filter ipv4
# $2 - filter ipv6
# $3 - queue number
nft_fw_nfqws_pre4 "$1" $3
nft_fw_nfqws_pre6 "$2" $3
}
nft_fw_nfqws_both4()
{
# $1 - filter ipv4
# $2 - queue number
nft_fw_nfqws_post4 "$@"
nft_fw_nfqws_pre4 "$(nft_reverse_nfqws_rule $1)" $2
}
nft_fw_nfqws_both6()
{
# $1 - filter ipv6
# $2 - queue number
nft_fw_nfqws_post6 "$@"
nft_fw_nfqws_pre6 "$(nft_reverse_nfqws_rule $1)" $2
}
nft_fw_nfqws_both()
{
# $1 - filter ipv4
# $2 - filter ipv6
# $3 - queue number
nft_fw_nfqws_both4 "$1" "$3"
nft_fw_nfqws_both6 "$2" "$3"
}
zapret_reload_ifsets()
{
nft_only nft_create_table ; nft_fill_ifsets_overload
return 0
}
zapret_list_ifsets()
{
nft_only nft_list_ifsets
return 0
}
zapret_list_table()
{
nft_only nft_list_table
return 0
}
nft_fw_reverse_nfqws_rule4()
{
nft_fw_nfqws_pre4 "$(nft_reverse_nfqws_rule "$1")" $2
}
nft_fw_reverse_nfqws_rule6()
{
nft_fw_nfqws_pre6 "$(nft_reverse_nfqws_rule "$1")" $2
}
nft_fw_reverse_nfqws_rule()
{
# ensure that modes relying on incoming traffic work
# $1 - rule4
# $2 - rule6
# $3 - queue number
nft_fw_reverse_nfqws_rule4 "$1" $3
nft_fw_reverse_nfqws_rule6 "$2" $3
}
nft_first_packets()
{
# $1 - packet count
[ -n "$1" -a "$1" != keepalive ] && [ "$1" -ge 1 ] &&
{
if [ "$1" = 1 ] ; then
echo "$nft_connbytes 1"
else
echo "$nft_connbytes 1-$1"
fi
}
}
nft_apply_nfqws_in_out()
{
# $1 - tcp,udp
# $2 - ports
# $3 - PKT_OUT. special value : 'keepalive'
# $4 - PKT_IN
local f4 f6 first_packets_only
[ -n "$2" ] || return
[ -n "$3" -a "$3" != 0 ] &&
{
first_packets_only="$(nft_first_packets $3)"
f4="$1 dport {$2} $first_packets_only"
f6=$f4
nft_filter_apply_ipset_target f4 f6
nft_fw_nfqws_post "$f4" "$f6" $QNUM
}
[ -n "$4" -a "$4" != 0 ] &&
{
first_packets_only="$(nft_first_packets $4)"
f4="$1 dport {$2} $first_packets_only"
f6=$f4
nft_filter_apply_ipset_target f4 f6
nft_fw_reverse_nfqws_rule "$f4" "$f6" $QNUM
}
}
zapret_apply_firewall_standard_nfqws_rules_nft()
{
[ "$NFQWS2_ENABLE" = 1 ] && {
nft_apply_nfqws_in_out tcp "$NFQWS2_PORTS_TCP" "$NFQWS2_TCP_PKT_OUT" "$NFQWS2_TCP_PKT_IN"
nft_apply_nfqws_in_out tcp "$NFQWS2_PORTS_TCP_KEEPALIVE" keepalive "$NFQWS2_TCP_PKT_IN"
nft_apply_nfqws_in_out udp "$NFQWS2_PORTS_UDP" "$NFQWS2_UDP_PKT_OUT" "$NFQWS2_UDP_PKT_IN"
nft_apply_nfqws_in_out udp "$NFQWS2_PORTS_UDP_KEEPALIVE" keepalive "$NFQWS2_UDP_PKT_IN"
}
}
zapret_apply_firewall_standard_rules_nft()
{
zapret_apply_firewall_standard_nfqws_rules_nft
}
zapret_apply_firewall_rules_nft()
{
zapret_apply_firewall_standard_rules_nft
custom_runner zapret_custom_firewall_nft
}
zapret_apply_firewall_nft()
{
echo Applying nftables
create_ipset no-update
nft_create_firewall
nft_fill_ifsets_overload
zapret_apply_firewall_rules_nft
[ "$FLOWOFFLOAD" = 'software' -o "$FLOWOFFLOAD" = 'hardware' ] && nft_apply_flow_offloading
return 0
}
zapret_unapply_firewall_nft()
{
echo Clearing nftables
nft_del_firewall
custom_runner zapret_custom_firewall_nft_flush
return 0
}
zapret_do_firewall_nft()
{
# $1 - 1 - add, 0 - del
if [ "$1" = 0 ] ; then
zapret_unapply_firewall_nft
else
zapret_apply_firewall_nft
fi
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

@@ -2,7 +2,7 @@
# change values here
# can help in case /tmp has not enough space
#TMPDIR=/opt/zapret/tmp
#TMPDIR=/opt/zapret2/tmp
# redefine user for zapret daemons. required on Keenetic
#WS_USER=nobody
@@ -20,7 +20,7 @@ SET_MAXELEM=522288
# too large hashsize will waste lots of RAM
IPSET_OPT="hashsize 262144 maxelem $SET_MAXELEM"
# dynamically generate additional ip. $1 = ipset/nfset/table name
#IPSET_HOOK="/etc/zapret.ipset.hook"
#IPSET_HOOK="/etc/zapret2.ipset.hook"
# options for ip2net. "-4" or "-6" auto added by ipset create script
IP2NET_OPT4="--prefix-length=22-30 --v4-threshold=3/4"
@@ -75,9 +75,9 @@ NFQWS2_UDP_PKT_IN=0
# hostlist markers are replaced to empty string if MODE_FILTER does not satisfy
# <HOSTLIST_NOAUTO> appends ipset/zapret-hosts-auto.txt as normal list
NFQWS2_OPT="
--filter-tcp=80 --payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_md5 --lua-desync=multisplit:pos=method+2 <HOSTLIST> --new
--filter-tcp=443 --payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5:tcp_seq=-10000 --lua-desync=multidisorder:pos=1,midsld <HOSTLIST> --new
--filter-udp=443 --payload=quic_initial --lua-desync=fake:blob=fake_default_quic:repeats=6 <HOSTLIST_NOAUTO> --new
--filter-tcp=80 --filter-l7=http <HOSTLIST> --payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_md5 --lua-desync=multisplit:pos=method+2 --new
--filter-tcp=443 --filter-l7=tls <HOSTLIST> --payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5:tcp_seq=-10000 --lua-desync=multidisorder:pos=1,midsld --new
--filter-udp=443 --filter-l7=quic <HOSTLIST_NOAUTO> --payload=quic_initial --lua-desync=fake:blob=fake_default_quic:repeats=6
"
# none,ipset,hostlist,autohostlist
@@ -86,12 +86,14 @@ MODE_FILTER=none
# donttouch,none,software,hardware
FLOWOFFLOAD=donttouch
# openwrt: specify networks to be treated as LAN. default is "lan"
#OPENWRT_LAN="lan lan2 lan3"
# openwrt: specify networks to be treated as WAN. default wans are interfaces with default route
#OPENWRT_WAN4="wan vpn"
#OPENWRT_WAN6="wan6 vpn6"
# for routers based on desktop linux and macos. has no effect in openwrt.
# optionally CHOOSE WAN/WAN6 NETWORK INTERFACES
# CHOOSE LAN and optinally WAN/WAN6 NETWORK INTERFACES
# or leave them commented if its not router
# it's possible to specify multiple interfaces like this : IFACE_WAN="eth0 eth1 eth2"
# if IFACE_WAN6 is not defined it take the value of IFACE_WAN
@@ -102,10 +104,10 @@ FLOWOFFLOAD=donttouch
# not applicable to openwrt with firewall3+iptables
INIT_APPLY_FW=1
# firewall apply hooks
#INIT_FW_PRE_UP_HOOK="/etc/firewall.zapret.hook.pre_up"
#INIT_FW_POST_UP_HOOK="/etc/firewall.zapret.hook.post_up"
#INIT_FW_PRE_DOWN_HOOK="/etc/firewall.zapret.hook.pre_down"
#INIT_FW_POST_DOWN_HOOK="/etc/firewall.zapret.hook.post_down"
#INIT_FW_PRE_UP_HOOK="/etc/firewall.zapret2.hook.pre_up"
#INIT_FW_POST_UP_HOOK="/etc/firewall.zapret2.hook.post_up"
#INIT_FW_PRE_DOWN_HOOK="/etc/firewall.zapret2.hook.pre_down"
#INIT_FW_POST_DOWN_HOOK="/etc/firewall.zapret2.hook.post_down"
# do not work with ipv4
#DISABLE_IPV4=1

View File

@@ -34,3 +34,18 @@ v0.2
* zapret-lib: bugfixes
* zapret-lib: remove ip6_hopbyhop_x2 fooling, separately add second hopbyhop header using ip6_hopbyhop2
* zapret-pcap
v0.3
* init.d launch scripts
* init.d: 40-webserver custom script
* install_easy
v0.4
* nfqws2: profile names and cookies
* nfqws2: profile templates
* nfqws2: remove stun_binding_req, replace to stun. no more message type details
* nfqws2: proper conntack position for replayed packets
* blockcheck2: fix broken dns cache
* nfqws2: LUA_COMPAT_VER tracking

7
docs/changes_compat.txt Normal file
View File

@@ -0,0 +1,7 @@
Here listed all api breaking changes.
When something changes capable of breaking things NFQWS2_COMPAT_VER increases.
v2
* removed "stun_binding_req" specialized payload. replaced with common "stun" - any stun packets, not only binding request.
every LUA relying on desync.l7payload should be revised.
nfqws2 --payload option and init.d custom scripts must be updated.

View File

@@ -24,8 +24,8 @@ define Build/Compile
endef
define Package/ip2net/install
$(INSTALL_DIR) $(1)/opt/zapret/binaries/my
$(INSTALL_BIN) $(PKG_BUILD_DIR)/ip2net $(1)/opt/zapret/binaries/my
$(INSTALL_DIR) $(1)/opt/zapret2/binaries/my
$(INSTALL_BIN) $(PKG_BUILD_DIR)/ip2net $(1)/opt/zapret2/binaries/my
endef
$(eval $(call BuildPackage,ip2net))

View File

@@ -24,8 +24,8 @@ define Build/Compile
endef
define Package/mdig/install
$(INSTALL_DIR) $(1)/opt/zapret/binaries/my
$(INSTALL_BIN) $(PKG_BUILD_DIR)/mdig $(1)/opt/zapret/binaries/my
$(INSTALL_DIR) $(1)/opt/zapret2/binaries/my
$(INSTALL_BIN) $(PKG_BUILD_DIR)/mdig $(1)/opt/zapret2/binaries/my
endef
$(eval $(call BuildPackage,mdig))

View File

@@ -31,7 +31,7 @@ endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./nfq/* $(PKG_BUILD_DIR)/
$(CP) ./nfq2/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
@@ -39,8 +39,8 @@ define Build/Compile
endef
define Package/nfqws2/install
$(INSTALL_DIR) $(1)/opt/zapret/binaries/my
$(INSTALL_BIN) $(PKG_BUILD_DIR)/nfqws2 $(1)/opt/zapret/binaries/my
$(INSTALL_DIR) $(1)/opt/zapret2/binaries/my
$(INSTALL_BIN) $(PKG_BUILD_DIR)/nfqws2 $(1)/opt/zapret2/binaries/my
endef
$(eval $(call BuildPackage,nfqws2))

View File

@@ -1 +1 @@
Copy "nfq" folder here !
Copy "nfq2" folder here !

View File

@@ -361,7 +361,7 @@ start "zapret: http,https,quic" /min "%~dp0winws2.exe" ^
--new ^
--filter-l7=wireguard,stun,discord ^
--out-range=-d10 ^
--payload=wireguard_initiation,wireguard_cookie,stun_binding_req,discord_ip_discovery ^
--payload=wireguard_initiation,wireguard_cookie,stun,discord_ip_discovery ^
--lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2
```

View File

@@ -0,0 +1,22 @@
# This script fixes keenetic issue with nfqws generated udp packets
# Keenetic uses proprietary ndmmark and does not masquerade without this mark
# If not masqueraded packets go to WAN with LAN IP and get dropped by ISP
# It's advised to set IFACE_WAN in config
zapret_custom_firewall()
{
# $1 - 1 - add, 0 - stop
local wan wanif rule
[ "$DISABLE_IPV4" = "1" ] || {
# use IFACE_WAN if defined. if not - search for interfaces with default route.
wanif=${IFACE_WAN:-$(sed -nre 's/^([^\t]+)\t00000000\t[0-9A-F]{8}\t[0-9A-F]{4}\t[0-9]+\t[0-9]+\t[0-9]+\t00000000.*$/\1/p' /proc/net/route | sort -u | xargs)}
for wan in $wanif; do
rule="-o $wan -p udp -m mark --mark $DESYNC_MARK/$DESYNC_MARK"
ipt_print_op $1 "$rule" "keenetic udp fix"
ipt_add_del $1 POSTROUTING -t nat $rule -j MASQUERADE
done
}
}

View File

@@ -0,0 +1,66 @@
# this custom script runs standard mode with extra firewall rules
# config: use TPWS_ENABLE_OVERRIDE, NFQWS_ENABLE_OVERRIDE to enable standard mode daemons
# standard and override switches cannot be enabled simultaneously !
TPWS_ENABLE_OVERRIDE=${TPWS_ENABLE_OVERRIDE:-0}
NFQWS_ENABLE_OVERRIDE=${NFQWS_ENABLE_OVERRIDE:-0}
# config: some if these values must be set in config. not setting any of these makes this script meaningless.
# pre vars put ipt/nft code to the rule beginning
#FW_EXTRA_PRE_TPWS_IPT=
#FW_EXTRA_PRE_TPWS_NFT=
#FW_EXTRA_PRE_NFQWS_IPT="-m mark --mark 0x10000000/0x10000000"
#FW_EXTRA_PRE_NFQWS_NFT="mark and 0x10000000 != 0"
# post vars put ipt/nft code to the rule end
#FW_EXTRA_POST_TPWS_IPT=
#FW_EXTRA_POST_TPWS_NFT=
#FW_EXTRA_POST_NFQWS_IPT=
#FW_EXTRA_POST_NFQWS_NFT=
check_std_intersect()
{
[ "$TPWS_ENABLE_OVERRIDE" = 1 -a "$TPWS_ENABLE" = 1 ] && {
echo "ERROR ! both TPWS_ENABLE_OVERRIDE and TPWS_ENABLE are enabled"
return 1
}
[ "$NFQWS_ENABLE_OVERRIDE" = 1 -a "$NFQWS_ENABLE" = 1 ] && {
echo "ERROR ! both NFQWS_ENABLE_OVERRIDE and NFQWS_ENABLE are enabled"
return 1
}
return 0
}
zapret_custom_daemons()
{
# $1 - 1 - add, 0 - stop
check_std_intersect || return
local TPWS_SOCKS_ENABLE=0 TPWS_ENABLE=$TPWS_ENABLE_OVERRIDE NFQWS_ENABLE=$NFQWS_ENABLE_OVERRIDE
standard_mode_daemons "$1"
}
zapret_custom_firewall()
{
# $1 - 1 - run, 0 - stop
check_std_intersect || return
local FW_EXTRA_PRE FW_EXTRA_POST TPWS_ENABLE=$TPWS_ENABLE_OVERRIDE NFQWS_ENABLE=$NFQWS_ENABLE_OVERRIDE
FW_EXTRA_PRE="$FW_EXTRA_PRE_TPWS_IPT" FW_EXTRA_POST="$FW_EXTRA_POST_TPWS_IPT"
zapret_do_firewall_standard_tpws_rules_ipt $1
FW_EXTRA_PRE="$FW_EXTRA_PRE_NFQWS_IPT" FW_EXTRA_POST="$FW_EXTRA_POST_NFQWS_IPT"
zapret_do_firewall_standard_nfqws_rules_ipt $1
}
zapret_custom_firewall_nft()
{
# stop logic is not required
check_std_intersect || return
local FW_EXTRA_PRE FW_EXTRA_POST TPWS_ENABLE=$TPWS_ENABLE_OVERRIDE NFQWS_ENABLE=$NFQWS_ENABLE_OVERRIDE
FW_EXTRA_PRE="$FW_EXTRA_PRE_TPWS_NFT" FW_EXTRA_POST="$FW_EXTRA_POST_TPWS_NFT"
zapret_apply_firewall_standard_tpws_rules_nft
FW_EXTRA_PRE="$FW_EXTRA_PRE_NFQWS_NFT" FW_EXTRA_POST="$FW_EXTRA_POST_NFQWS_NFT"
zapret_apply_firewall_standard_nfqws_rules_nft
}

View File

@@ -0,0 +1,39 @@
# this custom script runs nfqws2 in server mode for typical webserver
WEBSERVER_DEFAULT_STRATEGY="
--server
--payload http_reply,tls_server_hello --lua-desync=fake:blob=0x00000000000000000000000000000000:badsum:repeats=2 --lua-desync=multisplit
--payload empty --lua-desync=synack_split"
# can override in config :
NFQWS_OPT_DESYNC_WEBSERVER="${NFQWS_OPT_DESYNC_WEBSERVER:-$WEBSERVER_DEFAULT_STRATEGY}"
WEBSERVER_PORTS="${WEBSERVER_PORTS:-80,443}"
WEBSERVER_PKT_OUT="${WEBSERVER_PKT_OUT:-15}"
alloc_dnum DNUM_WEBSERVER
alloc_qnum QNUM_WEBSERVER
zapret_custom_daemons()
{
# $1 - 1 - add, 0 - stop
local opt="--qnum=$QNUM_WEBSERVER $NFQWS_OPT_DESYNC_WEBSERVER"
do_nfqws $1 $DNUM_WEBSERVER "$opt"
}
zapret_custom_firewall()
{
# $1 - 1 - run, 0 - stop
local PORTS=$(replace_char - : $WEBSERVER_PORTS)
local first_packets=$(ipt_first_packets $WEBSERVER_PKT_OUT)
local f="-p tcp -m multiport --sports $PORTS $first_packets"
fw_nfqws_post $1 "$f" "$f" $QNUM_WEBSERVER
}
zapret_custom_firewall_nft()
{
# stop logic is not required
local first_packets=$(nft_first_packets $WEBSERVER_PKT_OUT)
local f="tcp sport {$WEBSERVER_PORTS} $first_packets"
nft_fw_nfqws_post "$f" "$f" $QNUM_WEBSERVER
}

View File

@@ -0,0 +1,38 @@
# this custom script runs desync to DHT packets with udp payload length 101..399 , without ipset/hostlist filtering
# NOTE: @ih requires nft 1.0.1+ and updated kernel version. it's confirmed to work on 5.15 (openwrt 23) and not work on 5.10 (openwrt 22)
# can override in config :
NFQWS_OPT_DESYNC_DHT="${NFQWS_OPT_DESYNC_DHT:---payload dht --lua-desync=dht_dn}"
alloc_dnum DNUM_DHT4ALL
alloc_qnum QNUM_DHT4ALL
zapret_custom_daemons()
{
# $1 - 1 - add, 0 - stop
local opt="--qnum=$QNUM_DHT4ALL $NFQWS_OPT_DESYNC_DHT"
do_nfqws $1 $DNUM_DHT4ALL "$opt"
}
zapret_custom_firewall()
{
# $1 - 1 - run, 0 - stop
local f uf4 uf6
local first_packet_only="$ipt_connbytes 1:1"
f='-p udp -m length --length 109:407 -m u32 --u32'
uf4='0>>22&0x3C@8>>16=0x6431'
uf6='48>>16=0x6431'
fw_nfqws_post $1 "$f $uf4 $first_packet_only" "$f $uf6 $first_packet_only" $QNUM_DHT4ALL
}
zapret_custom_firewall_nft()
{
# stop logic is not required
local f
local first_packet_only="$nft_connbytes 1"
f="meta length 109-407 meta l4proto udp @ih,0,16 0x6431"
nft_fw_nfqws_post "$f $first_packet_only" "$f $first_packet_only" $QNUM_DHT4ALL
}

View File

@@ -0,0 +1,35 @@
# this custom script runs desync to all discord media packets
# NOTE: @ih requires nft 1.0.1+ and updated kernel version. it's confirmed to work on 5.15 (openwrt 23) and not work on 5.10 (openwrt 22)
# can override in config :
NFQWS_OPT_DESYNC_DISCORD_MEDIA="${NFQWS_OPT_DESYNC_DISCORD_MEDIA:---payload discord_ip_discovery --lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2}"
DISCORD_MEDIA_PORT_RANGE="${DISCORD_MEDIA_PORT_RANGE:-50000-50099}"
alloc_dnum DNUM_DISCORD_MEDIA
alloc_qnum QNUM_DISCORD_MEDIA
zapret_custom_daemons()
{
# $1 - 1 - add, 0 - stop
local opt="--qnum=$QNUM_DISCORD_MEDIA $NFQWS_OPT_DESYNC_DISCORD_MEDIA"
do_nfqws $1 $DNUM_DISCORD_MEDIA "$opt"
}
zapret_custom_firewall()
{
# $1 - 1 - run, 0 - stop
local DISABLE_IPV6=1
local port_range=$(replace_char - : $DISCORD_MEDIA_PORT_RANGE)
local f="-p udp --dport $port_range -m u32 --u32"
# this is simplified test to skip writing monstrous rule. instead of checking 64 bytes for zeroes only check 2 dwords for zero
fw_nfqws_post $1 "$f 0>>22&0x3C@4>>16=0x52&&0>>22&0x3C@8=0x00010046&&0>>22&0x3C@16=0&&0>>22&0x3C@76=0" '' $QNUM_DISCORD_MEDIA
}
zapret_custom_firewall_nft()
{
# stop logic is not required
local DISABLE_IPV6=1
local f="udp dport $DISCORD_MEDIA_PORT_RANGE udp length == 82 @ih,0,32 0x00010046 @ih,64,128 0x00000000000000000000000000000000 @ih,192,128 0x00000000000000000000000000000000 @ih,320,128 0x00000000000000000000000000000000 @ih,448,128 0x00000000000000000000000000000000"
nft_fw_nfqws_post "$f" '' $QNUM_DISCORD_MEDIA
}

View File

@@ -0,0 +1,144 @@
# this custom script demonstrates how to launch extra nfqws instance limited by ipset
# can override in config :
NFQWS_MY1_OPT="${NFQWS_MY1_OPT:---filter-udp=* --payload known,unknown --lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2:payload=all --new --filter-tcp=* --payload=known,unknown --lua-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_NAME4=my1nfqws4
NFQWS_MY1_NAME6=my1nfqws6
zapret_custom_daemons()
{
# $1 - 1 - run, 0 - stop
local opt="--qnum=$QNUM_NFQWS_MY1 $NFQWS_MY1_OPT"
do_nfqws $1 $DNUM_NFQWS_MY1 "$opt"
}
zapret_custom_firewall()
{
# $1 - 1 - run, 0 - stop
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)
[ "$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
}
[ -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_NAME4 2>/dev/null
ipset destroy $NFQWS_MY1_NAME6 2>/dev/null
}
}
zapret_custom_firewall_nft()
{
local f4 f6 subnets
local first_packets_only="$nft_connbytes 1-$NFQWS_MY1_PKT_OUT"
[ "$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"
}
[ -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_NAME4 2>/dev/null
nft_del_set $NFQWS_MY1_NAME6 2>/dev/null
}

View File

@@ -0,0 +1,30 @@
# this custom script runs desync to all IETF QUIC initials
# NOTE: @ih requires nft 1.0.1+ and updated kernel version. it's confirmed to work on 5.15 (openwrt 23) and not work on 5.10 (openwrt 22)
# can override in config :
NFQWS_OPT_DESYNC_QUIC="${NFQWS_OPT_DESYNC_QUIC:---payload quic_initial --lua-desync=fake:blob=fake_default_quic:repeats=2}"
alloc_dnum DNUM_QUIC4ALL
alloc_qnum QNUM_QUIC4ALL
zapret_custom_daemons()
{
# $1 - 1 - add, 0 - stop
local opt="--qnum=$QNUM_QUIC4ALL $NFQWS_OPT_DESYNC_QUIC"
do_nfqws $1 $DNUM_QUIC4ALL "$opt"
}
zapret_custom_firewall()
{
# $1 - 1 - run, 0 - stop
local f='-p udp -m u32 --u32'
fw_nfqws_post $1 "$f 0>>22&0x3C@4>>16=264:65535&&0>>22&0x3C@8>>28=0xC&&0>>22&0x3C@9=0x00000001" "$f 44>>16=264:65535&&48>>28=0xC&&49=0x00000001" $QNUM_QUIC4ALL
}
zapret_custom_firewall_nft()
{
# stop logic is not required
local f="udp length >= 264 @ih,0,4 0xC @ih,8,32 0x00000001"
nft_fw_nfqws_post "$f" "$f" $QNUM_QUIC4ALL
}

View File

@@ -0,0 +1,30 @@
# this custom script runs desync to all stun packets
# NOTE: @ih requires nft 1.0.1+ and updated kernel version. it's confirmed to work on 5.15 (openwrt 23) and not work on 5.10 (openwrt 22)
# can override in config :
NFQWS_OPT_DESYNC_STUN="${NFQWS_OPT_DESYNC_STUN:---payload stun --lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2}"
alloc_dnum DNUM_STUN4ALL
alloc_qnum QNUM_STUN4ALL
zapret_custom_daemons()
{
# $1 - 1 - add, 0 - stop
local opt="--qnum=$QNUM_STUN4ALL $NFQWS_OPT_DESYNC_STUN"
do_nfqws $1 $DNUM_STUN4ALL "$opt"
}
zapret_custom_firewall()
{
# $1 - 1 - run, 0 - stop
local f='-p udp -m u32 --u32'
fw_nfqws_post $1 "$f 0>>22&0x3C@4>>16=28:65535&&0>>22&0x3C@12=0x2112A442&&0>>22&0x3C@8&0xC0000003=0" "$f 44>>16=28:65535&&52=0x2112A442&&48&0xC0000003=0" $QNUM_STUN4ALL
}
zapret_custom_firewall_nft()
{
# stop logic is not required
local f="udp length >= 28 @ih,32,32 0x2112A442 @ih,0,2 0 @ih,30,2 0"
nft_fw_nfqws_post "$f" "$f" $QNUM_STUN4ALL
}

View File

@@ -0,0 +1,32 @@
# this custom script runs desync to all wireguard handshake initiation packets
# NOTE: this works for original wireguard and may not work for 3rd party implementations such as xray
# NOTE: @ih requires nft 1.0.1+ and updated kernel version. it's confirmed to work on 5.15 (openwrt 23) and not work on 5.10 (openwrt 22)
# can override in config :
NFQWS_OPT_DESYNC_WG="${NFQWS_OPT_DESYNC_WG:---payload wireguard_initiation --lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2}"
alloc_dnum DNUM_WG4ALL
alloc_qnum QNUM_WG4ALL
zapret_custom_daemons()
{
# $1 - 1 - add, 0 - stop
local opt="--qnum=$QNUM_WG4ALL $NFQWS_OPT_DESYNC_WG"
do_nfqws $1 $DNUM_WG4ALL "$opt"
}
# size = 156 (8 udp header + 148 payload) && payload starts with 0x01000000
zapret_custom_firewall()
{
# $1 - 1 - run, 0 - stop
local f='-p udp -m u32 --u32'
fw_nfqws_post $1 "$f 0>>22&0x3C@4>>16=0x9c&&0>>22&0x3C@8=0x01000000" "$f 44>>16=0x9c&&48=0x01000000" $QNUM_WG4ALL
}
zapret_custom_firewall_nft()
{
# stop logic is not required
local f="udp length 156 @ih,0,32 0x01000000"
nft_fw_nfqws_post "$f" "$f" $QNUM_WG4ALL
}

69
init.d/openrc/zapret2 Executable file
View File

@@ -0,0 +1,69 @@
#!/sbin/openrc-run
# zapret openrc to sysv adapter
# on some systems (alpine) for unknown reason non-openrc-run scripts are not started from /etc/init.d
EXEDIR=$(dirname "$RC_SERVICE")
EXEDIR="$(cd "$EXEDIR"; pwd)"
ZAPRET_BASE="$EXEDIR/../.."
ZAPRET_INIT="$ZAPRET_BASE/init.d/sysv/zapret2"
extra_commands="start_fw stop_fw restart_fw start_daemons stop_daemons restart_daemons reload_ifsets list_ifsets list_table"
description="extra commands :"
description_stop_fw="Stop zapret firewall"
description_start_fw="Start zapret firewall"
description_restart_fw="Restart zapret firewall"
description_reload_ifsets="Reload interface lists (nftables only)"
description_list_ifsets="Display interface lists (nftables only)"
description_list_table="Display zapret nftable (nftables only)"
description_stop_daemons="Stop zapret daemons only"
description_start_daemons="Start zapret daemons only"
description_restart_daemons="Restart zapret firewall only"
depend() {
rc-service -e networking && need networking
}
start()
{
"$ZAPRET_INIT" start
}
stop()
{
"$ZAPRET_INIT" stop
}
start_fw()
{
"$ZAPRET_INIT" start_fw
}
stop_fw()
{
"$ZAPRET_INIT" stop_fw
}
restart_fw()
{
"$ZAPRET_INIT" restart_fw
}
start_daemons()
{
"$ZAPRET_INIT" start_daemons
}
stop_daemons()
{
"$ZAPRET_INIT" stop_daemons
}
restart_daemons()
{
"$ZAPRET_INIT" restart_daemons
}
reload_ifsets()
{
"$ZAPRET_INIT" reload_ifsets
}
list_ifsets()
{
"$ZAPRET_INIT" list_ifsets
}
list_table()
{
"$ZAPRET_INIT" list_table
}

46
init.d/openwrt/90-zapret2 Normal file
View File

@@ -0,0 +1,46 @@
#!/bin/sh
ZAPRET=/etc/init.d/zapret2
check_lan()
{
IS_LAN=
[ -n "$OPENWRT_LAN" ] || OPENWRT_LAN=lan
for lan in $OPENWRT_LAN; do
[ "$INTERFACE" = "$lan" ] && {
IS_LAN=1
break
}
done
}
[ -n "$INTERFACE" ] && [ "$ACTION" = ifup -o "$ACTION" = ifdown ] && [ -x "$ZAPRET" ] && "$ZAPRET" enabled && {
SCRIPT=$(readlink "$ZAPRET")
if [ -n "$SCRIPT" ]; then
EXEDIR=$(dirname "$SCRIPT")
ZAPRET_BASE=$(readlink -f "$EXEDIR/../..")
else
ZAPRET_BASE=/opt/zapret2
fi
ZAPRET_RW=${ZAPRET_RW:-"$ZAPRET_BASE"}
ZAPRET_CONFIG=${ZAPRET_CONFIG:-"$ZAPRET_RW/config"}
CUSTOM_DIR="$ZAPRET_RW/init.d/openwrt"
. "$ZAPRET_CONFIG"
. "$ZAPRET_BASE/common/base.sh"
. "$ZAPRET_BASE/common/fwtype.sh"
linux_fwtype
case "$FWTYPE" in
nftables)
logger -t zapret reloading nftables ifsets due to $ACTION of $INTERFACE
"$ZAPRET" reload_ifsets
;;
iptables)
openwrt_fw3 || {
logger -t zapret reloading iptables due to $ACTION of $INTERFACE
"$ZAPRET" restart_fw
}
;;
esac
}

View File

View File

@@ -0,0 +1,11 @@
SCRIPT=$(readlink /etc/init.d/zapret2)
if [ -n "$SCRIPT" ]; then
EXEDIR=$(dirname "$SCRIPT")
ZAPRET_BASE=$(readlink -f "$EXEDIR/../..")
else
ZAPRET_BASE=/opt/zapret2
fi
. "$ZAPRET_BASE/init.d/openwrt/functions"
zapret_apply_firewall

218
init.d/openwrt/functions Normal file
View File

@@ -0,0 +1,218 @@
. /lib/functions/network.sh
ZAPRET_BASE=${ZAPRET_BASE:-/opt/zapret2}
ZAPRET_RW=${ZAPRET_RW:-"$ZAPRET_BASE"}
ZAPRET_CONFIG=${ZAPRET_CONFIG:-"$ZAPRET_RW/config"}
. "$ZAPRET_CONFIG"
. "$ZAPRET_BASE/common/base.sh"
. "$ZAPRET_BASE/common/fwtype.sh"
. "$ZAPRET_BASE/common/linux_iphelper.sh"
. "$ZAPRET_BASE/common/ipt.sh"
. "$ZAPRET_BASE/common/nft.sh"
. "$ZAPRET_BASE/common/linux_fw.sh"
. "$ZAPRET_BASE/common/linux_daemons.sh"
. "$ZAPRET_BASE/common/list.sh"
. "$ZAPRET_BASE/common/custom.sh"
CUSTOM_DIR="$ZAPRET_RW/init.d/openwrt"
QNUM=${QNUM:-300}
WS_USER=${WS_USER:-daemon}
DESYNC_MARK=${DESYNC_MARK:-0x40000000}
DESYNC_MARK_POSTNAT=${DESYNC_MARK_POSTNAT:-0x20000000}
OPENWRT_LAN=${OPENWRT_LAN:-lan}
IPSET_CR="$ZAPRET_BASE/ipset/create_ipset.sh"
# can be multiple ipv6 outgoing interfaces
# uplink from isp, tunnelbroker, vpn, ...
# want them all. who knows what's the real one that blocks sites
# dont want any manual configuration - want to do it automatically
# standard network_find_wan[6] return only the first
# we use low level function from network.sh to avoid this limitation
# it can change theoretically and stop working
network_find_wan4_all()
{
if [ -n "$OPENWRT_WAN4" ]; then
eval $1="\$OPENWRT_WAN4"
else
__network_ifstatus "$1" "" "[@.route[@.target='0.0.0.0' && !@.table]].interface" "" 10 2>/dev/null && return
network_find_wan $1
fi
}
network_find_wan_all()
{
network_find_wan4_all "$@"
}
network_find_wan6_all()
{
if [ -n "$OPENWRT_WAN6" ]; then
eval $1="\$OPENWRT_WAN6"
else
__network_ifstatus "$1" "" "[@.route[@.target='::' && !@.table]].interface" "" 10 2>/dev/null && return
network_find_wan6 $1
fi
}
network_find_wanX_devices()
{
# $1 - ip version: 4 or 6
# $2 - variable to put result to
local ifaces
network_find_wan${1}_all ifaces
call_for_multiple_items network_get_device $2 "$ifaces"
}
fw_nfqws_prepost_x()
{
# $1 - 1 - add, 0 - del
# $2 - filter
# $3 - queue number
# $4 - 4/6
# $5 - post/pre
local ifaces DWAN
network_find_wan${4}_all ifaces
call_for_multiple_items network_get_device DWAN "$ifaces"
[ -n "$DWAN" ] && _fw_nfqws_${5}${4} $1 "$2" $3 "$(unique $DWAN)"
}
fw_nfqws_post4()
{
fw_nfqws_prepost_x $1 "$2" $3 4 post
}
fw_nfqws_post6()
{
fw_nfqws_prepost_x $1 "$2" $3 6 post
}
fw_nfqws_pre4()
{
fw_nfqws_prepost_x $1 "$2" $3 4 pre
}
fw_nfqws_pre6()
{
fw_nfqws_prepost_x $1 "$2" $3 6 pre
}
create_ipset()
{
echo "Creating ip list table (firewall type $FWTYPE)"
"$IPSET_CR" "$@"
}
list_nfqws_rules()
{
# $1 = '' for ipv4, '6' for ipv6
ip$1tables -S POSTROUTING -t mangle | \
grep -E "NFQUEUE --queue-num $QNUM --queue-bypass|NFQUEUE --queue-num $(($QNUM+1)) --queue-bypass|NFQUEUE --queue-num $(($QNUM+2)) --queue-bypass|NFQUEUE --queue-num $(($QNUM+3)) --queue-bypass|NFQUEUE --queue-num $(($QNUM+10)) --queue-bypass|NFQUEUE --queue-num $(($QNUM+11)) --queue-bypass" | \
sed -re 's/^-A POSTROUTING (.*) -j NFQUEUE.*$/\1/' -e "s/-m mark ! --mark $DESYNC_MARK\/$DESYNC_MARK//"
}
apply_flow_offloading_enable_rule()
{
# $1 = '' for ipv4, '6' for ipv6
local i off='-j FLOWOFFLOAD'
[ "$FLOWOFFLOAD" = "hardware" ] && off="$off --hw"
i="forwarding_rule_zapret -m comment --comment zapret_traffic_offloading_enable -m conntrack --ctstate RELATED,ESTABLISHED $off"
echo enabling ipv${1:-4} flow offloading : $i
ip$1tables -A $i
}
apply_flow_offloading_exempt_rule()
{
# $1 = '' for ipv4, '6' for ipv6
local i v
v=$1
shift
i="forwarding_rule_zapret $@ -m comment --comment zapret_traffic_offloading_exemption -j RETURN"
echo applying ipv${v:-4} flow offloading exemption : $i
ip${v}tables -A $i
}
flow_offloading_unexempt_v()
{
# $1 = '' for ipv4, '6' for ipv6
local DWAN
network_find_wanX_devices ${1:-4} DWAN
for i in $DWAN; do ipt$1_del FORWARD -o $i -j forwarding_rule_zapret ; done
ip$1tables -F forwarding_rule_zapret 2>/dev/null
ip$1tables -X forwarding_rule_zapret 2>/dev/null
}
flow_offloading_exempt_v()
{
# $1 = '' for ipv4, '6' for ipv6
is_ipt_flow_offload_avail $1 || return 0
flow_offloading_unexempt_v $1
[ "$FLOWOFFLOAD" = 'software' -o "$FLOWOFFLOAD" = 'hardware' ] && {
ip$1tables -N forwarding_rule_zapret
# remove outgoing interface
list_nfqws_rules $1 | sed -re 's/-o +[^ ]+//g' |
while read rule; do
apply_flow_offloading_exempt_rule "$1" $rule
done
apply_flow_offloading_enable_rule $1
# only outgoing to WAN packets trigger flow offloading
local DWAN
network_find_wanX_devices ${1:-4} DWAN
for i in $DWAN; do ipt$1 FORWARD -o $i -j forwarding_rule_zapret; done
}
return 0
}
flow_offloading_exempt()
{
[ "$DISABLE_IPV4" = "1" ] || flow_offloading_exempt_v
[ "$DISABLE_IPV6" = "1" ] || flow_offloading_exempt_v 6
}
flow_offloading_unexempt()
{
[ "$DISABLE_IPV4" = "1" ] || flow_offloading_unexempt_v
[ "$DISABLE_IPV6" = "1" ] || flow_offloading_unexempt_v 6
}
nft_fill_ifsets_overload()
{
local ifaces DLAN DWAN DWAN6 PDLAN PDWAN PDWAN6
call_for_multiple_items network_get_device DLAN "$OPENWRT_LAN"
call_for_multiple_items network_get_physdev PDLAN "$OPENWRT_LAN"
network_find_wan4_all ifaces
call_for_multiple_items network_get_device DWAN "$ifaces"
call_for_multiple_items network_get_physdev PDWAN "$ifaces"
network_find_wan6_all ifaces
call_for_multiple_items network_get_device DWAN6 "$ifaces"
call_for_multiple_items network_get_physdev PDWAN6 "$ifaces"
nft_fill_ifsets "$DLAN" "$DWAN" "$DWAN6" "$PDLAN" "$PDWAN" "$PDWAN6"
}
nft_wanif_filter_present()
{
# in openwrt we always use wanif filter
return 0
}
nft_wanif6_filter_present()
{
# in openwrt we always use wanif6 filter
return 0
}
nft_fw_nfqws_post4()
{
_nft_fw_nfqws_post4 "$1" $2 always_apply_wan_filter
}
nft_fw_nfqws_post6()
{
_nft_fw_nfqws_post6 "$1" $2 always_apply_wan_filter
}
nft_fw_nfqws_pre4()
{
_nft_fw_nfqws_pre4 "$1" $2 always_apply_wan_filter
}
nft_fw_nfqws_pre6()
{
_nft_fw_nfqws_pre6 "$1" $2 always_apply_wan_filter
}

135
init.d/openwrt/zapret2 Executable file
View File

@@ -0,0 +1,135 @@
#!/bin/sh /etc/rc.common
USE_PROCD=1
# after network
START=21
my_extra_command() {
local cmd="$1"
local help="$2"
local extra="$(printf "%-16s%s" "${cmd}" "${help}")"
EXTRA_HELP="${EXTRA_HELP} ${extra}
"
EXTRA_COMMANDS="${EXTRA_COMMANDS} ${cmd}"
}
my_extra_command stop_fw "Stop zapret firewall (noop in iptables+fw3 case)"
my_extra_command start_fw "Start zapret firewall (noop in iptables+fw3 case)"
my_extra_command restart_fw "Restart zapret firewall (noop in iptables+fw3 case)"
my_extra_command reload_ifsets "Reload interface lists (nftables only)"
my_extra_command list_ifsets "Display interface lists (nftables only)"
my_extra_command list_table "Display zapret nftable (nftables only)"
my_extra_command stop_daemons "Stop zapret daemons only (=stop in iptables+fw3 case)"
my_extra_command start_daemons "Start zapret daemons only (=start in iptables+fw3 case)"
my_extra_command restart_daemons "Restart zapret firewall only (=restart in iptables+fw3 case)"
SCRIPT=$(readlink /etc/init.d/zapret2)
if [ -n "$SCRIPT" ]; then
EXEDIR=$(dirname "$SCRIPT")
ZAPRET_BASE=$(readlink -f "$EXEDIR/../..")
else
ZAPRET_BASE=/opt/zapret2
fi
. "$ZAPRET_BASE/init.d/openwrt/functions"
# !!!!! in old openwrt 21.x- with iptables firewall rules are configured separately
# !!!!! in new openwrt >21.x with nftables firewall is configured here
PIDDIR=/var/run
USEROPT="--user=$WS_USER"
NFQWS2="${NFQWS2:-$ZAPRET_BASE/nfq2/nfqws2}"
LUAOPT="--lua-init=@$ZAPRET_BASE/lua/zapret-lib.lua --lua-init=@$ZAPRET_BASE/lua/zapret-antidpi.lua"
NFQWS2_OPT_BASE="$USEROPT --fwmark=$DESYNC_MARK $LUAOPT"
run_daemon()
{
# $1 - daemon string id or number. can use 1,2,3,...
# $2 - daemon
# $3 - daemon args
# use $PIDDIR/$DAEMONBASE$1.pid as pidfile
local DAEMONBASE="$(basename "$2")"
echo "Starting daemon $1: $2 $3"
procd_open_instance
procd_set_param command $2 $3
procd_set_param pidfile $PIDDIR/${DAEMONBASE}_$1.pid
procd_close_instance
}
run_nfqws()
{
run_daemon $1 "$NFQWS2" "$NFQWS2_OPT_BASE $2"
}
do_nfqws()
{
[ "$1" = 0 ] || { shift; run_nfqws "$@"; }
}
start_daemons_procd()
{
standard_mode_daemons 1
custom_runner zapret_custom_daemons 1
return 0
}
start_daemons()
{
rc_procd start_daemons_procd "$@"
}
stop_daemons()
{
local svc="$(basename ${basescript:-$initscript})"
procd_running "$svc" "$1" && procd_kill "$svc" "$1"
}
restart_daemons()
{
stop_daemons
start_daemons
}
start_fw()
{
zapret_apply_firewall
}
stop_fw()
{
zapret_unapply_firewall
}
restart_fw()
{
stop_fw
start_fw
}
reload_ifsets()
{
zapret_reload_ifsets
}
list_ifsets()
{
zapret_list_ifsets
}
list_table()
{
zapret_list_table
}
start_service()
{
start_daemons_procd
[ "$INIT_APPLY_FW" != "1" ] || {
linux_fwtype
openwrt_fw3_integration || start_fw
}
}
stop_service()
{
# this procedure is called from stop()
# stop() already stop daemons
[ "$INIT_APPLY_FW" != "1" ] || {
linux_fwtype
openwrt_fw3_integration || stop_fw
}
}

27
init.d/pfsense/zapret2.sh Executable file
View File

@@ -0,0 +1,27 @@
#!/bin/sh
# this file should be placed to /usr/local/etc/rc.d and chmod 755
# copy 'lua' dir there
ZDIR=/usr/local/etc/zapret2
# prepare system
kldload ipfw
kldload ipdivert
# for older pfsense versions. newer do not have these sysctls
sysctl net.inet.ip.pfil.outbound=ipfw,pf
sysctl net.inet.ip.pfil.inbound=ipfw,pf
sysctl net.inet6.ip6.pfil.outbound=ipfw,pf
sysctl net.inet6.ip6.pfil.inbound=ipfw,pf
# required for newer pfsense versions (2.6.0 tested) to return ipfw to functional state
pfctl -d ; pfctl -e
# add ipfw rules and start daemon
ipfw delete 100
ipfw add 100 divert 990 tcp from any to any 80,443 out not diverted not sockarg
pkill ^dvtws2$
dvtws2 --daemon --port 990 --lua-init=@$ZDIR/zapret-lib.lua --lua-init=@$ZDIR/zapret-antidpi.lua --lua-desync=multisplit

2
init.d/runit/zapret2/finish Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
/opt/zapret2/init.d/sysv/zapret2 stop

3
init.d/runit/zapret2/run Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
/opt/zapret2/init.d/sysv/zapret2 start
exec chpst -b zapret2 sleep infinity

2
init.d/s6/zapret2/down Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/execlineb -P
exec /opt/zapret2/init.d/sysv/zapret2 stop

1
init.d/s6/zapret2/type Normal file
View File

@@ -0,0 +1 @@
oneshot

2
init.d/s6/zapret2/up Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/execlineb -P
exec /opt/zapret2/init.d/sysv/zapret2 start

View File

@@ -0,0 +1,62 @@
# Example systemd service unit for nfqws. Adjust for your installation.
# WARNING ! This unit requires to compile nfqws using `make systemd`
# WARNING ! This makefile target enables special systemd notify support.
# PREPARE
# install build depends
# make -C /opt/zapret2 systemd
# cp nfqws2\@.service /lib/systemd/system
# systemctl daemon-reload
# MANAGE INSTANCE
# prepare /etc/zapret2/nfqws1.conf with nfqws parameters
# systemctl start nfqws2@nfqws1
# systemctl status nfqws2@nfqws1
# systemctl restart nfqws2@nfqws1
# systemctl enable nfqws2@nfqws1
# systemctl disable nfqws2@nfqws1
# systemctl stop nfqws2@nfqws1
# DELETE
# rm /lib/systemd/system/nfqws@.service
# systemctl daemon-reload
[Unit]
After=network.target
[Service]
Type=notify
Restart=on-failure
ExecSearchPath=/opt/zapret2/nfq2
ExecStart=nfqws2 @${CONFIG_DIR}/${INSTANCE}.conf
Environment=CONFIG_DIR=/etc/zapret2
Environment=INSTANCE=%i
RestrictAddressFamilies=AF_NETLINK AF_UNIX AF_INET6 AF_INET
LockPersonality=true
MemoryDenyWriteExecute=true
PrivateDevices=true
PrivateMounts=true
PrivateTmp=true
ProcSubset=pid
ProtectClock=true
ProtectControlGroups=true
ProtectHome=true
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectProc=invisible
ProtectSystem=full
RemoveIPC=true
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
UMask=0077
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,13 @@
[Unit]
Description=zapret2 ip/host list update
[Service]
Restart=no
IgnoreSIGPIPE=no
KillMode=control-group
GuessMainPID=no
RemainAfterExit=no
ExecStart=/opt/zapret2/ipset/get_config.sh
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,11 @@
[Unit]
Description=zapret2 ip/host list update timer
[Timer]
OnCalendar=*-*-2,4,6,8,10,12,14,16,18,20,22,24,26,28,30 00:00:00
RandomizedDelaySec=86400
Persistent=true
Unit=zapret2-list-update.service
[Install]
WantedBy=timers.target

View File

@@ -0,0 +1,17 @@
[Unit]
After=network-online.target
Wants=network-online.target
[Service]
Type=forking
Restart=no
TimeoutSec=30sec
IgnoreSIGPIPE=no
KillMode=none
GuessMainPID=no
RemainAfterExit=no
ExecStart=/opt/zapret2/init.d/sysv/zapret2 start
ExecStop=/opt/zapret2/init.d/sysv/zapret2 stop
[Install]
WantedBy=multi-user.target

View File

191
init.d/sysv/functions Normal file
View File

@@ -0,0 +1,191 @@
# init script functions library for desktop linux systems
ZAPRET_BASE=${ZAPRET_BASE:-/opt/zapret2}
ZAPRET_RW=${ZAPRET_RW:-"$ZAPRET_BASE"}
ZAPRET_CONFIG=${ZAPRET_CONFIG:-"$ZAPRET_RW/config"}
. "$ZAPRET_CONFIG"
. "$ZAPRET_BASE/common/base.sh"
. "$ZAPRET_BASE/common/fwtype.sh"
. "$ZAPRET_BASE/common/linux_iphelper.sh"
. "$ZAPRET_BASE/common/ipt.sh"
. "$ZAPRET_BASE/common/nft.sh"
. "$ZAPRET_BASE/common/linux_fw.sh"
. "$ZAPRET_BASE/common/linux_daemons.sh"
. "$ZAPRET_BASE/common/list.sh"
. "$ZAPRET_BASE/common/custom.sh"
CUSTOM_DIR="$ZAPRET_RW/init.d/sysv"
user_exists()
{
id -u $1 >/dev/null 2>/dev/null
}
useradd_compat()
{
# $1 - username
# skip for readonly systems
[ -w "/etc" ] && {
if exists useradd ; then
useradd --no-create-home --system --shell /bin/false $1
elif is_linked_to_busybox adduser ; then
# some systems may miss nogroup group in /etc/group
# adduser fails if it's absent and no group is specified
addgroup nogroup 2>/dev/null
# busybox has special adduser syntax
adduser -S -H -D $1
elif exists adduser; then
adduser --no-create-home --system --disabled-login $1
fi
}
user_exists $1
}
prepare_user()
{
user_exists $WS_USER || {
# fallback to daemon if we cant add WS_USER
useradd_compat $WS_USER || {
for user in daemon nobody; do
user_exists $user && {
WS_USER=$user
return 0
}
done
return 1
}
}
}
# this complex user selection allows to survive in any locked/readonly/minimalistic environment
[ -n "$WS_USER" ] || WS_USER=tpws
if prepare_user; then
USEROPT="--user=$WS_USER"
else
WS_USER=1
USEROPT="--uid $WS_USER:$WS_USER"
fi
PIDDIR=/var/run
IPSET_CR="$ZAPRET_BASE/ipset/create_ipset.sh"
DESYNC_MARK=${DESYNC_MARK:-0x40000000}
DESYNC_MARK_POSTNAT=${DESYNC_MARK_POSTNAT:-0x20000000}
QNUM=${QNUM:-300}
NFQWS2="${NFQWS2:-$ZAPRET_BASE/nfq2/nfqws2}"
LUAOPT="--lua-init=@$ZAPRET_BASE/lua/zapret-lib.lua --lua-init=@$ZAPRET_BASE/lua/zapret-antidpi.lua"
NFQWS2_OPT_BASE="$USEROPT --fwmark=$DESYNC_MARK $LUAOPT"
fw_nfqws_post4()
{
_fw_nfqws_post4 $1 "$2" $3 "$IFACE_WAN"
}
fw_nfqws_post6()
{
_fw_nfqws_post6 $1 "$2" $3 "${IFACE_WAN6:-$IFACE_WAN}"
}
fw_nfqws_pre4()
{
_fw_nfqws_pre4 $1 "$2" $3 "$IFACE_WAN"
}
fw_nfqws_pre6()
{
_fw_nfqws_pre6 $1 "$2" $3 "${IFACE_WAN6:-$IFACE_WAN}"
}
nft_fw_nfqws_post4()
{
_nft_fw_nfqws_post4 "$1" $2 "$IFACE_WAN"
}
nft_fw_nfqws_post6()
{
_nft_fw_nfqws_post6 "$1" $2 "${IFACE_WAN6:-$IFACE_WAN}"
}
nft_fw_nfqws_pre4()
{
_nft_fw_nfqws_pre4 "$1" $2 "$IFACE_WAN"
}
nft_fw_nfqws_pre6()
{
_nft_fw_nfqws_pre6 "$1" $2 "${IFACE_WAN6:-$IFACE_WAN}"
}
nft_wanif_filter_present()
{
[ -n "$IFACE_WAN" ]
}
nft_wanif6_filter_present()
{
[ -n "${IFACE_WAN6:-$IFACE_WAN}" ]
}
nft_fill_ifsets_overload()
{
nft_fill_ifsets "$IFACE_WAN" "${IFACE_WAN6:-$IFACE_WAN}" "$IFACE_LAN"
}
run_daemon()
{
# $1 - daemon number : 1,2,3,...
# $2 - daemon
# $3 - daemon args
# use $PIDDIR/$DAEMONBASE$1.pid as pidfile
local DAEMONBASE="$(basename "$2")"
local PID= PIDFILE=$PIDDIR/${DAEMONBASE}_$1.pid
echo "Starting daemon $1: $2 $3"
[ -f "$PIDFILE" ] && {
read PID <"$PIDFILE"
[ -d "/proc/$PID" ] || PID=
}
if [ -n "$PID" ]; then
echo already running
else
"$2" $3 >/dev/null &
PID=$!
if [ -n "$PID" ]; then
echo $PID >$PIDFILE
else
echo could not start daemon $1 : $2 $3
false
fi
fi
}
stop_daemon()
{
# $1 - daemon number : 1,2,3,...
# $2 - daemon
# use $PIDDIR/$DAEMONBASE$1.pid as pidfile
local DAEMONBASE="$(basename "$2")"
local PID PIDFILE=$PIDDIR/${DAEMONBASE}_$1.pid
echo "Stopping daemon $1: $2"
if [ -f "$PIDFILE" ]; then
read PID <"$PIDFILE"
kill $PID
rm -f "$PIDFILE"
else
echo no pidfile : $PIDFILE
fi
}
do_daemon()
{
# $1 - 1 - run, 0 - stop
on_off_function run_daemon stop_daemon "$@"
}
do_nfqws()
{
# $1 : 1 - run, 0 - stop
# $2 : daemon number
# $3 : daemon args
do_daemon $1 $2 "$NFQWS2" "$NFQWS2_OPT_BASE $3"
}
create_ipset()
{
echo "Creating ip list table (firewall type $FWTYPE)"
"$IPSET_CR" "$@"
}

82
init.d/sysv/zapret2 Executable file
View File

@@ -0,0 +1,82 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: zapret
# Required-Start: $local_fs $network
# Required-Stop: $local_fs $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
### END INIT INFO
SCRIPT=$(readlink -f "$0")
EXEDIR=$(dirname "$SCRIPT")
ZAPRET_BASE=$(readlink -f "$EXEDIR/../..")
. "$EXEDIR/functions"
NAME=zapret
DESC=anti-zapret
do_start()
{
zapret_run_daemons
[ "$INIT_APPLY_FW" != "1" ] || { zapret_apply_firewall; }
}
do_stop()
{
zapret_stop_daemons
[ "$INIT_APPLY_FW" != "1" ] || zapret_unapply_firewall
}
case "$1" in
start)
do_start
;;
stop)
do_stop
;;
restart)
do_stop
do_start
;;
start-fw|start_fw)
zapret_apply_firewall
;;
stop-fw|stop_fw)
zapret_unapply_firewall
;;
restart-fw|restart_fw)
zapret_unapply_firewall
zapret_apply_firewall
;;
start-daemons|start_daemons)
zapret_run_daemons
;;
stop-daemons|stop_daemons)
zapret_stop_daemons
;;
restart-daemons|restart_daemons)
zapret_stop_daemons
zapret_run_daemons
;;
reload-ifsets|reload_ifsets)
zapret_reload_ifsets
;;
list-ifsets|list_ifsets)
zapret_list_ifsets
;;
list-table|list_table)
zapret_list_table
;;
*)
echo "Usage: $SCRIPT {start|stop|restart|start-fw|stop-fw|restart-fw|start-daemons|stop-daemons|restart-daemons|reload-ifsets|list-ifsets|list-table}" >&2
exit 1
;;
esac
exit 0

View File

@@ -0,0 +1,14 @@
Цель этих фильтров - отсекать полезную нагрузку в режиме ядра, не насилуя процессор перенаправлением целого потока на winws.
Задействуются через `winws --wf-raw-part=@filename`. Может быть несколько частичных фильтров. Они могут сочетаться с --wf-tcp и --wf-udp.
Однако, язык фильтров windivert не содержит операций с битовыми полями, сдвигов и побитовой логики.
Поэтому фильтры получились более слабыми, способными передавать неправильную нагрузку.
Дофильтрация производится силами winws.
Описание языка фильтров : https://reqrypt.org/windivert-doc.html#filter_language
Пример инстанса для пробития медиапотоков в discord : `winws --wf-raw-part=@windivert_part.discord_media.txt --wf-raw-part=@windivert_part.stun.txt --filter-l7=stun,discord --dpi-desync=fake`
These filters are invoked using `winws --wf-raw-part=@filename`. Multiple filter parts are supported. They can be combined with --wf-tcp and --wf-udp.
Filters are kernel mode and save great amount of CPU.
However windivert cannot filter by bit fields, lacks shift and bitwise logic operations.
Filters are relaxed and can pass wrong payloads. Finer filtering is done by winws.

View File

@@ -0,0 +1,20 @@
outbound and ip and
udp.DstPort>=50000 and udp.DstPort<=50099 and
udp.PayloadLength=74 and
udp.Payload32[0]=0x00010046 and
udp.Payload32[2]=0 and
udp.Payload32[3]=0 and
udp.Payload32[4]=0 and
udp.Payload32[5]=0 and
udp.Payload32[6]=0 and
udp.Payload32[7]=0 and
udp.Payload32[8]=0 and
udp.Payload32[9]=0 and
udp.Payload32[10]=0 and
udp.Payload32[11]=0 and
udp.Payload32[12]=0 and
udp.Payload32[13]=0 and
udp.Payload32[14]=0 and
udp.Payload32[15]=0 and
udp.Payload32[16]=0 and
udp.Payload32[17]=0

View File

@@ -0,0 +1,4 @@
outbound and
udp.PayloadLength>=256 and
udp.Payload[0]>=0xC0 and udp.Payload[0]<0xD0 and
udp.Payload[1]=0 and udp.Payload16[1]=0 and udp.Payload[4]=1

View File

@@ -0,0 +1,3 @@
outbound and
udp.PayloadLength>=20 and
udp.Payload32[1]=0x2112A442 and udp.Payload[0]<0x40

View File

@@ -0,0 +1,3 @@
outbound and
udp.PayloadLength=148 and
udp.Payload[0]=0x01

View File

@@ -30,7 +30,7 @@ select_test_method()
TEST=bash
elif exists zsh && [ "$UNAME" != CYGWIN ] ; then
TEST=zsh
elif [ "$UNAME" != Darwin -a "$UNAME" != CYGWIN ]; then
elif [ "$UNAME" != CYGWIN ]; then
if exists hexdump and exists dd; then
# macos does not use ELF
TEST=elf
@@ -64,12 +64,6 @@ select_test_method()
}
disable_antivirus()
{
# $1 - dir
[ "$UNAME" = Darwin ] && find "$1" -maxdepth 1 -type f -perm +111 -exec xattr -d com.apple.quarantine {} \; 2>/dev/null
}
check_dir()
{
local dir="$BINDIR/$1"
@@ -77,7 +71,6 @@ check_dir()
local out
if [ -f "$exe" ]; then
if [ -x "$exe" ]; then
disable_antivirus "$dir"
case $TEST in
bash)
out=$(echo 0.0.0.0 | bash -c "\"$exe"\" 2>/dev/null)
@@ -143,8 +136,7 @@ if [ ! -d "$BINDIR" ] || ! dir_is_not_empty "$BINDIR" ]; then
echo "to compile on other systems : make"
;;
Darwin)
echo "you need to download release from github or build binaries from source"
echo "to compile : make mac"
echo "macos is not supported"
;;
FreeBSD)
echo "you need to download release from github or build binaries from source"
@@ -168,9 +160,6 @@ case $UNAME in
ARCHLIST="my linux-x86_64 linux-x86 linux-arm64 linux-arm linux-mips64 linux-mipsel linux-mips linux-lexra linux-ppc"
PKTWS=nfqws2
;;
Darwin)
ARCHLIST="my mac64"
;;
FreeBSD)
ARCHLIST="my freebsd-x86_64"
PKTWS=dvtws2

833
install_easy.sh Executable file
View File

@@ -0,0 +1,833 @@
#!/bin/sh
# automated script for easy installing zapret
EXEDIR="$(dirname "$0")"
EXEDIR="$(cd "$EXEDIR"; pwd)"
ZAPRET_BASE=${ZAPRET_BASE:-"$EXEDIR"}
ZAPRET_TARGET=${ZAPRET_TARGET:-/opt/zapret2}
ZAPRET_TARGET_RW=${ZAPRET_RW:-"$ZAPRET_TARGET"}
ZAPRET_TARGET_CONFIG="$ZAPRET_TARGET_RW/config"
ZAPRET_RW=${ZAPRET_RW:-"$ZAPRET_BASE"}
ZAPRET_CONFIG=${ZAPRET_CONFIG:-"$ZAPRET_RW/config"}
ZAPRET_CONFIG_DEFAULT="$ZAPRET_BASE/config.default"
IPSET_DIR="$ZAPRET_BASE/ipset"
[ -f "$ZAPRET_CONFIG" ] || {
ZAPRET_CONFIG_DIR="$(dirname "$ZAPRET_CONFIG")"
[ -d "$ZAPRET_CONFIG_DIR" ] || mkdir -p "$ZAPRET_CONFIG_DIR"
cp "$ZAPRET_CONFIG_DEFAULT" "$ZAPRET_CONFIG"
}
. "$ZAPRET_CONFIG"
. "$ZAPRET_BASE/common/base.sh"
. "$ZAPRET_BASE/common/elevate.sh"
. "$ZAPRET_BASE/common/fwtype.sh"
. "$ZAPRET_BASE/common/dialog.sh"
. "$ZAPRET_BASE/common/ipt.sh"
. "$ZAPRET_BASE/common/installer.sh"
. "$ZAPRET_BASE/common/virt.sh"
. "$ZAPRET_BASE/common/list.sh"
GET_LIST="$IPSET_DIR/get_config.sh"
check_readonly_system()
{
local RO
echo \* checking readonly system
case $SYSTEM in
systemd)
[ -w "$SYSTEMD_SYSTEM_DIR" ] || RO=1
;;
openrc)
[ -w "$(dirname "$INIT_SCRIPT")" ] || RO=1
;;
esac
[ -z "$RO" ] || {
echo '!!! READONLY SYSTEM DETECTED !!!'
echo '!!! WILL NOT BE ABLE TO CONFIGURE STARTUP !!!'
echo '!!! MANUAL STARTUP CONFIGURATION IS REQUIRED !!!'
ask_yes_no N "do you want to continue" || exitp 5
}
}
check_source()
{
local bad=0
echo \* checking source files
case $SYSTEM in
systemd)
[ -f "$EXEDIR/init.d/systemd/zapret2.service" ] || bad=1
;;
openrc)
[ -f "$EXEDIR/init.d/openrc/zapret2" ] || bad=1
;;
esac
[ "$bad" = 1 ] && {
echo 'some critical files are missing'
echo 'are you sure you are not using embedded release ? you need full version for traditional systems'
exitp 5
}
}
check_bins()
{
echo \* checking executables
fix_perms_bin_test "$EXEDIR"
local arch="$(get_bin_arch)"
local make_target
local cf="-march=native"
[ "$FORCE_BUILD" = "1" ] && {
echo forced build mode
if [ "$arch" = "my" ]; then
echo already compiled
else
arch=""
fi
}
if [ -n "$arch" ] ; then
echo found architecture "\"$arch\""
elif [ -f "$EXEDIR/Makefile" ] && exists make; then
echo trying to compile
case $SYSTEM in
systemd)
make_target=systemd
;;
esac
CFLAGS="${cf:+$cf }${CFLAGS}" OPTIMIZE=-O2 make -C "$EXEDIR" $make_target || {
echo could not compile
make -C "$EXEDIR" clean
exitp 8
}
echo compiled
else
echo build tools not found
exitp 8
fi
}
call_install_bin()
{
sh "$EXEDIR/install_bin.sh" $1
}
get_bin_arch()
{
call_install_bin getarch
}
install_binaries()
{
echo \* installing binaries
call_install_bin || {
echo compatible binaries not found
exitp 8
}
}
ws_opt_validate()
{
# ПРИМЕЧАНИЕ ДЛЯ РАСПРОСТРАНИТЕЛЕЙ КОПИПАСТЫ
# ЭТОТ КОД СДЕЛАН СПЕЦИАЛЬНО ДЛЯ ВАС, ЧТОБЫ ВЫ НЕ ПОСТИЛИ В СЕТЬ ПЛОХИЕ РЕЦЕПТЫ
# ЕСЛИ ВАМ ХОЧЕТСЯ ЕГО УДАЛИТЬ И НАПИСАТЬ ИНСТРУКЦИЮ КАК ЕГО УДАЛЯТЬ, ВЫ ДЕЛАЕТЕ ХРЕНОВУЮ УСЛУГУ. НАПИШИТЕ ЛУЧШЕ custom script.
# custom script - ЭТО ФАЙЛИК, КОТОРЫЙ ДОСТАТОЧНО СКОПИРОВАТЬ В НУЖНУЮ ДИРЕКТОРИЮ, ЧТОБЫ ОН СДЕЛАЛ ТОЖЕ САМОЕ, НО ЭФФЕКТИВНО.
# ФИЛЬТРАЦИЯ ПО IPSET В ЯДРЕ НЕСРАВНИМО ЭФФЕКТИВНЕЕ, ЧЕМ ПЕРЕКИДЫВАТЬ ВСЕ ПАКЕТЫ В nfqws И ТАМ ФИЛЬТРОВАТЬ
# --ipset СУЩЕСТВУЕТ ТОЛЬКО ДЛЯ ВИНДЫ И LINUX СИСТЕМ БЕЗ ipset (НАПРИМЕР, Android).
# И ТОЛЬКО ПО ЭТОЙ ПРИЧИНЕ ОНО НЕ ВЫКИНУТО ПОЛНОСТЬЮ ИЗ LINUX ВЕРСИИ
has_bad_ws_options "$1" && {
help_bad_ws_options
return 1
}
return 0
}
nfqws_opt_validate()
{
ws_opt_validate "$1" || return 1
dry_run_nfqws || {
echo invalid nfqws2 options
return 1
}
}
select_mode_group()
{
# $1 - ENABLE var name
# $2 - ask text
# $3 - vars
# $4 - validator func
# $5 - validator func param var
local enabled var v edited bad Y param
echo
ask_yes_no_var $1 "$2"
write_config_var $1
eval enabled=\$$1
[ "$enabled" = 1 ] && {
echo
while : ; do
list_vars $3
bad=0; Y=N
[ -n "$4" ] && {
eval param="\$$5"
$4 "$param"; bad=$?
[ "$bad" = 1 ] && Y=Y
}
ask_yes_no $Y "do you want to edit the options" || {
[ "$bad" = 1 ] && {
echo installer will not allow to use bad options. exiting.
exitp 3
}
[ -n "$edited" ] && {
for var in $3; do
write_config_var $var
done
}
break
}
edit_vars $3
edited=1
echo ..edited..
done
}
}
select_mode_nfqws()
{
local EDITVAR_NEWLINE_DELIMETERS="--new --out-range --in-range --payload" EDITVAR_NEWLINE_VARS="NFQWS2_OPT"
select_mode_group NFQWS2_ENABLE "enable nfqws2 ?" "NFQWS2_PORTS_TCP NFQWS2_PORTS_UDP NFQWS2_TCP_PKT_OUT NFQWS2_TCP_PKT_IN NFQWS2_UDP_PKT_OUT NFQWS2_UDP_PKT_IN NFQWS2_PORTS_TCP_KEEPALIVE NFQWS2_PORTS_UDP_KEEPALIVE NFQWS2_OPT" nfqws_opt_validate NFQWS2_OPT
}
select_mode_mode()
{
select_mode_nfqws
echo
echo "current custom scripts in $CUSTOM_DIR/custom.d:"
[ -d "$CUSTOM_DIR/custom.d" ] && ls "$CUSTOM_DIR/custom.d"
echo "Make sure this is ok"
echo
}
select_mode_filter()
{
local filter="none ipset hostlist autohostlist"
echo
echo select filtering :
ask_list MODE_FILTER "$filter" none && write_config_var MODE_FILTER
}
select_mode()
{
select_mode_filter
select_mode_mode
select_mode_iface
}
select_getlist()
{
if [ "$MODE_FILTER" = "ipset" -o "$MODE_FILTER" = "hostlist" -o "$MODE_FILTER" = "autohostlist" ]; then
local D=N
[ -n "$GETLIST" ] && D=Y
echo
if ask_yes_no $D "do you want to auto download ip/host list"; then
if [ "$MODE_FILTER" = "hostlist" -o "$MODE_FILTER" = "autohostlist" ] ; then
GETLISTS="get_refilter_domains.sh get_antizapret_domains.sh get_reestr_resolvable_domains.sh get_reestr_hostlist.sh"
GETLIST_DEF="get_antizapret_domains.sh"
else
GETLISTS="get_user.sh get_refilter_ipsum.sh get_antifilter_ip.sh get_antifilter_ipsmart.sh get_antifilter_ipsum.sh get_antifilter_ipresolve.sh get_antifilter_allyouneed.sh get_reestr_resolve.sh get_reestr_preresolved.sh get_reestr_preresolved_smart.sh"
GETLIST_DEF="get_antifilter_allyouneed.sh"
fi
ask_list GETLIST "$GETLISTS" "$GETLIST_DEF" && write_config_var GETLIST
return
fi
fi
GETLIST=""
write_config_var GETLIST
}
ask_config()
{
select_mode
select_getlist
}
ask_config_offload()
{
[ "$FWTYPE" = nftables ] || is_ipt_flow_offload_avail && {
echo
echo flow offloading can greatly increase speed on slow devices and high speed links \(usually 150+ mbits\)
if [ "$SYSTEM" = openwrt ]; then
echo unfortuantely its not compatible with most nfqws options. nfqws traffic must be exempted from flow offloading.
echo donttouch = disable system flow offloading setting if nfqws mode was selected, dont touch it otherwise and dont configure selective flow offloading
echo none = always disable system flow offloading setting and dont configure selective flow offloading
echo software = always disable system flow offloading setting and configure selective software flow offloading
echo hardware = always disable system flow offloading setting and configure selective hardware flow offloading
else
echo offloading is applicable only to forwarded traffic. it has no effect on outgoing traffic
echo hardware flow offloading is available only on specific supporting hardware. most likely will not work on a generic system
fi
echo offloading likely breaks traffic shaper
echo select flow offloading :
local options="none software hardware"
local default="none"
[ "$SYSTEM" = openwrt ] && {
options="donttouch none software hardware"
default="donttouch"
}
ask_list FLOWOFFLOAD "$options" $default && write_config_var FLOWOFFLOAD
}
}
ask_config_tmpdir()
{
# ask tmpdir change for low ram systems with enough free disk space
[ -n "$GETLIST" ] && [ $(get_free_space_mb "$EXEDIR/tmp") -ge 128 ] && [ $(get_ram_mb) -le 400 ] && {
echo
echo /tmp in openwrt is tmpfs. on low RAM systems there may be not enough RAM to store downloaded files
echo default tmpfs has size of 50% RAM
echo "RAM : $(get_ram_mb) Mb"
echo "DISK : $(get_free_space_mb) Mb"
echo select temp file location
[ -z "$TMPDIR" ] && TMPDIR=/tmp
ask_list TMPDIR "/tmp $EXEDIR/tmp" && {
[ "$TMPDIR" = "/tmp" ] && TMPDIR=
write_config_var TMPDIR
}
}
}
nft_flow_offload()
{
[ "$UNAME" = Linux -a "$FWTYPE" = nftables ] && [ "$FLOWOFFLOAD" = software -o "$FLOWOFFLOAD" = hardware ]
}
ask_iface()
{
# $1 - var to ask
# $2 - additional name for empty string synonim
local ifs i0 def new
eval def="\$$1"
[ -n "$2" ] && i0="$2 "
ifs="$(ls /sys/class/net)"
[ -z "$def" ] && eval $1="$2"
ask_list $1 "$i0$ifs" && {
eval new="\$$1"
[ "$new" = "$2" ] && eval $1=""
write_config_var $1
}
}
ask_iface_lan()
{
echo LAN interface :
local opt
nft_flow_offload || opt=NONE
ask_iface IFACE_LAN $opt
}
ask_iface_wan()
{
echo WAN interface :
local opt
nft_flow_offload || opt=ANY
ask_iface IFACE_WAN $opt
}
select_mode_iface()
{
# openwrt has its own interface management scheme
# LAN interface names are used only to setup flow offloading rules
[ "$SYSTEM" = "openwrt" ] && return
ask_iface_lan
ask_iface_wan
}
default_files()
{
# $1 - ro location
# $2 - rw location (can be equal to $1)
[ -d "$2/ipset" ] || mkdir -p "$2/ipset"
[ -f "$2/ipset/zapret-hosts-user-exclude.txt" ] || cp "$1/ipset/zapret-hosts-user-exclude.txt.default" "$2/ipset/zapret-hosts-user-exclude.txt"
[ -f "$2/ipset/zapret-hosts-user.txt" ] || echo nonexistent.domain >> "$2/ipset/zapret-hosts-user.txt"
[ -f "$2/ipset/zapret-hosts-user-ipban.txt" ] || touch "$2/ipset/zapret-hosts-user-ipban.txt"
for dir in openwrt sysv macos; do
[ -d "$1/init.d/$dir" ] && {
[ -d "$2/init.d/$dir" ] || mkdir -p "$2/init.d/$dir"
[ -d "$2/init.d/$dir/custom.d" ] || mkdir -p "$2/init.d/$dir/custom.d"
}
done
}
copy_all()
{
local dir
cp -R "$1" "$2"
[ -d "$2/tmp" ] || mkdir "$2/tmp"
}
copy_openwrt()
{
local ARCH="$(get_bin_arch)"
local BINDIR="$1/binaries/$ARCH"
local file
[ -d "$2" ] || mkdir -p "$2"
mkdir "$2/nfq2" "$2/ip2net" "$2/mdig" "$2/binaries" "$2/binaries/$ARCH" "$2/init.d" "$2/tmp" "$2/files"
cp -R "$1/files/fake" "$2/files"
cp -R "$1/common" "$1/ipset" "$1/blockcheck2.d" "$1/lua" "$2"
cp -R "$1/init.d/openwrt" "$1/init.d/custom.d.examples.linux" "$2/init.d"
cp "$1/config" "$1/config.default" "$1/install_easy.sh" "$1/uninstall_easy.sh" "$1/install_bin.sh" "$1/install_prereq.sh" "$1/blockcheck2.sh" "$2"
cp "$BINDIR/nfqws2" "$BINDIR/ip2net" "$BINDIR/mdig" "$2/binaries/$ARCH"
}
fix_perms_bin_test()
{
[ -d "$1" ] || return
find "$1/binaries" -name ip2net ! -perm -111 -exec chmod +x {} \;
}
fix_perms()
{
[ -d "$1" ] || return
find "$1" -type d -exec chmod 755 {} \;
find "$1" -type f -exec chmod 644 {} \;
local chow
case "$UNAME" in
Linux)
chow=root:root
;;
*)
chow=root:wheel
esac
chown -R $chow "$1"
find "$1/binaries" '(' -name dvtws2 -o -name nfqws2 -o -name ip2net -o -name mdig ')' -exec chmod 755 {} \;
for f in \
install_bin.sh \
blockcheck2.sh \
install_easy.sh \
install_prereq.sh \
files/huawei/E8372/zapret-ip \
files/huawei/E8372/unzapret-ip \
files/huawei/E8372/run-zapret-hostlist \
files/huawei/E8372/unzapret \
files/huawei/E8372/zapret \
files/huawei/E8372/run-zapret-ip \
ipset/get_exclude.sh \
ipset/clear_lists.sh \
ipset/create_ipset.sh \
ipset/get_config.sh \
ipset/get_user.sh \
ipset/get_ipban.sh \
ipset/get_refilter_domains.sh \
ipset/get_refilter_ipsum.sh \
ipset/get_reestr_resolvable_domains.sh \
ipset/get_reestr_preresolved.sh \
ipset/get_reestr_preresolved_smart.sh \
ipset/get_reestr_resolve.sh \
ipset/get_reestr_hostlist.sh \
ipset/get_antifilter_allyouneed.sh \
ipset/get_antifilter_ipsum.sh \
ipset/get_antifilter_ipsmart.sh \
ipset/get_antifilter_ip.sh \
ipset/get_antifilter_ipresolve.sh \
ipset/get_antizapret_domains.sh \
init.d/pfsense/zapret2.sh \
init.d/runit/zapret2/run \
init.d/runit/zapret2/finish \
init.d/openrc/zapret2 \
init.d/sysv/zapret2 \
init.d/openwrt/zapret2 \
uninstall_easy.sh \
; do chmod 755 "$1/$f" 2>/dev/null ; done
}
_backup_settings()
{
local i=0
for f in "$@"; do
# safety check
[ -z "$f" -o "$f" = "/" ] && continue
[ -f "$ZAPRET_TARGET/$f" ] && cp -f "$ZAPRET_TARGET/$f" "/tmp/zapret2-bkp-$i"
[ -d "$ZAPRET_TARGET/$f" ] && cp -rf "$ZAPRET_TARGET/$f" "/tmp/zapret2-bkp-$i"
i=$(($i+1))
done
}
_restore_settings()
{
local i=0
for f in "$@"; do
# safety check
[ -z "$f" -o "$f" = "/" ] && continue
[ -f "/tmp/zapret2-bkp-$i" ] && {
mv -f "/tmp/zapret2-bkp-$i" "$ZAPRET_TARGET/$f" || rm -f "/tmp/zapret2-bkp-$i"
}
[ -d "/tmp/zapret2-bkp-$i" ] && {
[ -d "$ZAPRET_TARGET/$f" ] && rm -r "$ZAPRET_TARGET/$f"
mv -f "/tmp/zapret2-bkp-$i" "$ZAPRET_TARGET/$f" || rm -r "/tmp/zapret2-bkp-$i"
}
i=$(($i+1))
done
}
backup_restore_settings()
{
# $1 - 1 - backup, 0 - restore
local mode=$1
on_off_function _backup_settings _restore_settings $mode "config" "init.d/sysv/custom.d" "init.d/openwrt/custom.d" "ipset/zapret-hosts-user.txt" "ipset/zapret-hosts-user-exclude.txt" "ipset/zapret-hosts-user-ipban.txt" "ipset/zapret-hosts-auto.txt"
}
check_location()
{
# $1 - copy function
echo \* checking location
# use inodes in case something is linked
if [ -d "$ZAPRET_TARGET" ] && [ $(get_dir_inode "$EXEDIR") = $(get_dir_inode "$ZAPRET_TARGET") ]; then
default_files "$ZAPRET_TARGET" "$ZAPRET_RW"
else
local rwdir=0
[ $(get_dir_inode "$ZAPRET_BASE") = $(get_dir_inode "$ZAPRET_RW") ] || rwdir=1
echo
echo easy install is supported only from default location : $ZAPRET_TARGET
echo currently its run from $EXEDIR
if ask_yes_no N "do you want the installer to copy it for you"; then
local keep=N
if [ -d "$ZAPRET_TARGET" ]; then
echo
echo installer found existing $ZAPRET_TARGET
echo directory needs to be replaced. config and custom scripts can be kept or replaced with clean version
if ask_yes_no N "do you want to delete all files there and copy this version"; then
echo
if [ $rwdir != 1 ]; then
ask_yes_no Y "keep config, custom scripts and user lists" && keep=Y
[ "$keep" = "Y" ] && backup_restore_settings 1
fi
rm -r "$ZAPRET_TARGET"
else
echo refused to overwrite $ZAPRET_TARGET. exiting
exitp 3
fi
fi
local B="$(dirname "$ZAPRET_TARGET")"
[ -d "$B" ] || mkdir -p "$B"
$1 "$EXEDIR" "$ZAPRET_TARGET"
fix_perms "$ZAPRET_TARGET"
[ "$keep" = "Y" ] && backup_restore_settings 0
echo relaunching itself from $ZAPRET_TARGET
exec "$ZAPRET_TARGET/$(basename "$0")"
else
echo copying aborted. exiting
exitp 3
fi
fi
echo running from $EXEDIR
}
service_install_systemd()
{
echo \* installing zapret service
if [ -w "$SYSTEMD_SYSTEM_DIR" ] ; then
rm -f "$INIT_SCRIPT"
cp -f "$EXEDIR/init.d/systemd/zapret2.service" "$SYSTEMD_SYSTEM_DIR"
"$SYSTEMCTL" daemon-reload
"$SYSTEMCTL" enable zapret2 || {
echo could not enable systemd service
exitp 20
}
else
echo '!!! READONLY SYSTEM DETECTED !!! CANNOT INSTALL SYSTEMD UNITS !!!'
fi
}
timer_install_systemd()
{
echo \* installing zapret2-list-update timer
if [ -w "$SYSTEMD_SYSTEM_DIR" ] ; then
"$SYSTEMCTL" disable zapret2-list-update.timer
"$SYSTEMCTL" stop zapret2-list-update.timer
cp -f "$EXEDIR/init.d/systemd/zapret2-list-update.service" "$SYSTEMD_SYSTEM_DIR"
cp -f "$EXEDIR/init.d/systemd/zapret2-list-update.timer" "$SYSTEMD_SYSTEM_DIR"
"$SYSTEMCTL" daemon-reload
"$SYSTEMCTL" enable zapret2-list-update.timer || {
echo could not enable zapret2-list-update.timer
exitp 20
}
"$SYSTEMCTL" start zapret2-list-update.timer || {
echo could not start zapret2-list-update.timer
exitp 30
}
else
echo '!!! READONLY SYSTEM DETECTED !!! CANNOT INSTALL SYSTEMD UNITS !!!'
fi
}
download_list()
{
[ -x "$GET_LIST" ] && {
echo \* downloading blocked ip/host list
# can be txt or txt.gz
"$IPSET_DIR/clear_lists.sh"
"$GET_LIST"
}
}
dnstest()
{
# $1 - dns server. empty for system resolver
nslookup w3.org $1 >/dev/null 2>/dev/null
}
check_dns()
{
echo \* checking DNS
dnstest || {
echo -- DNS is not working. It's either misconfigured or blocked or you don't have inet access.
return 1
}
echo system DNS is working
return 0
}
install_systemd()
{
INIT_SCRIPT_SRC="$EXEDIR/init.d/sysv/zapret"
CUSTOM_DIR="$ZAPRET_RW/init.d/sysv"
check_bins
require_root
check_readonly_system
check_location copy_all
check_dns
check_virt
service_stop_systemd
select_fwtype
check_prerequisites_linux
install_binaries
select_ipv6
ask_config_offload
ask_config
service_install_systemd
download_list
# in case its left from old version of zapret
crontab_del_quiet
# now we use systemd timers
timer_install_systemd
service_start_systemd
}
_install_sysv()
{
# $1 - install init script
CUSTOM_DIR="$ZAPRET_RW/init.d/sysv"
check_bins
require_root
check_readonly_system
check_location copy_all
check_dns
check_virt
service_stop_sysv
select_fwtype
check_prerequisites_linux
install_binaries
select_ipv6
ask_config_offload
ask_config
$1
download_list
crontab_del_quiet
# desktop system. more likely up at daytime
crontab_add 10 22
service_start_sysv
}
install_sysv()
{
INIT_SCRIPT_SRC="$EXEDIR/init.d/sysv/zapret2"
_install_sysv install_sysv_init
}
install_openrc()
{
INIT_SCRIPT_SRC="$EXEDIR/init.d/openrc/zapret2"
_install_sysv install_openrc_init
}
install_linux()
{
INIT_SCRIPT_SRC="$EXEDIR/init.d/sysv/zapret2"
CUSTOM_DIR="$ZAPRET_RW/init.d/sysv"
check_bins
require_root
check_location copy_all
check_dns
check_virt
select_fwtype
check_prerequisites_linux
install_binaries
select_ipv6
ask_config_offload
ask_config
download_list
crontab_del_quiet
# desktop system. more likely up at daytime
crontab_add 10 22
echo
echo '!!! WARNING. YOUR SETUP IS INCOMPLETE !!!'
echo you must manually add to auto start : $INIT_SCRIPT_SRC start
echo make sure it\'s executed after your custom/firewall iptables configuration
echo "if your system uses sysv init : ln -fs $INIT_SCRIPT_SRC /etc/init.d/zapret ; chkconfig zapret on"
}
deoffload_openwrt_firewall()
{
echo \* checking flow offloading
[ "$FWTYPE" = "nftables" ] || is_ipt_flow_offload_avail || {
echo unavailable
return
}
local fo=$(uci -q get firewall.@defaults[0].flow_offloading)
if [ "$fo" = "1" ] ; then
local mod=0
printf "system wide flow offloading detected. "
case $FLOWOFFLOAD in
donttouch)
if [ "$NFQWS2_ENABLE" = "1" ]; then
echo its incompatible with nfqws tcp data tampering. disabling
uci set firewall.@defaults[0].flow_offloading=0
mod=1
else
if dir_is_not_empty "$CUSTOM_DIR/custom.d" ; then
echo
echo !!! CUSTOM SCRIPTS ARE PRESENT !!! only you can decide whether flow offloading is compatible.
echo !!! CUSTOM SCRIPTS ARE PRESENT !!! if they use nfqws they will not work. you have to disable system-wide offloading.
else
echo its compatible with selected options. not disabling
fi
fi
;;
*)
echo zapret will disable system wide offloading setting and add selective rules if required
uci set firewall.@defaults[0].flow_offloading=0
mod=1
esac
[ "$mod" = "1" ] && uci commit firewall
else
echo system wide software flow offloading disabled. ok
fi
}
install_openwrt()
{
INIT_SCRIPT_SRC="$EXEDIR/init.d/openwrt/zapret2"
CUSTOM_DIR="$ZAPRET_RW/init.d/openwrt"
FW_SCRIPT_SRC="$EXEDIR/init.d/openwrt/firewall.zapret2"
OPENWRT_FW_INCLUDE=/etc/firewall.zapret2
OPENWRT_IFACE_HOOK="$EXEDIR/init.d/openwrt/90-zapret2"
check_bins
require_root
check_location copy_openwrt
install_binaries
check_dns
check_virt
local FWTYPE_OLD=$FWTYPE
echo \* stopping current firewall rules/daemons
"$INIT_SCRIPT_SRC" stop_fw
"$INIT_SCRIPT_SRC" stop_daemons
select_fwtype
select_ipv6
check_prerequisites_openwrt
ask_config
ask_config_tmpdir
ask_config_offload
# stop and reinstall sysv init
install_sysv_init
[ "$FWTYPE_OLD" != "$FWTYPE" -a "$FWTYPE_OLD" = iptables -a -n "$OPENWRT_FW3" ] && remove_openwrt_firewall
# free some RAM
clear_ipset
download_list
crontab_del_quiet
# router system : works 24/7. night is the best time
crontab_add 0 6
cron_ensure_running
install_openwrt_iface_hook
# in case of nftables or iptables without fw3 sysv init script also controls firewall
[ -n "$OPENWRT_FW3" -a "$FWTYPE" = iptables ] && install_openwrt_firewall
service_start_sysv
deoffload_openwrt_firewall
restart_openwrt_firewall
}
remove_pf_zapret_hooks()
{
echo \* removing zapret PF hooks
pf_anchors_clear
}
macos_fw_reload_trigger_clear()
{
LISTS_RELOAD=
write_config_var LISTS_RELOAD
}
macos_fw_reload_trigger_set()
{
LISTS_RELOAD="$INIT_SCRIPT_SRC reload-fw-tables"
write_config_var LISTS_RELOAD
}
# build binaries, do not use precompiled
[ "$1" = "make" ] && FORCE_BUILD=1
umask 0022
fix_sbin_path
fsleep_setup
check_system
check_source
case $SYSTEM in
systemd)
install_systemd
;;
openrc)
install_openrc
;;
linux)
install_linux
;;
openwrt)
install_openwrt
;;
esac
exitp 0

51
install_prereq.sh Executable file
View File

@@ -0,0 +1,51 @@
#!/bin/sh
# install prerequisites
EXEDIR="$(dirname "$0")"
EXEDIR="$(cd "$EXEDIR"; pwd)"
ZAPRET_BASE=${ZAPRET_BASE:-"$EXEDIR"}
ZAPRET_RW=${ZAPRET_RW:-"$ZAPRET_BASE"}
ZAPRET_CONFIG=${ZAPRET_CONFIG:-"$ZAPRET_RW/config"}
ZAPRET_CONFIG_DEFAULT="$ZAPRET_BASE/config.default"
[ -f "$ZAPRET_CONFIG" ] || {
ZAPRET_CONFIG_DIR="$(dirname "$ZAPRET_CONFIG")"
[ -d "$ZAPRET_CONFIG_DIR" ] || mkdir -p "$ZAPRET_CONFIG_DIR"
cp "$ZAPRET_CONFIG_DEFAULT" "$ZAPRET_CONFIG"
}
. "$ZAPRET_CONFIG"
. "$ZAPRET_BASE/common/base.sh"
. "$ZAPRET_BASE/common/elevate.sh"
. "$ZAPRET_BASE/common/fwtype.sh"
. "$ZAPRET_BASE/common/dialog.sh"
. "$ZAPRET_BASE/common/installer.sh"
. "$ZAPRET_BASE/common/ipt.sh"
umask 0022
fix_sbin_path
fsleep_setup
check_system accept_unknown_rc
[ $UNAME = "Linux" ] || {
echo no prerequisites required for $UNAME
exitp 0
}
require_root
case $UNAME in
Linux)
select_fwtype
case $SYSTEM in
openwrt)
select_ipv6
check_prerequisites_openwrt
;;
*)
check_prerequisites_linux
;;
esac
;;
esac
exitp 0

19
ipset/antifilter.helper Normal file
View File

@@ -0,0 +1,19 @@
get_antifilter()
{
# $1 - list url
# $2 - target file
local ZIPLISTTMP="$TMPDIR/zapret-ip.txt"
[ "$DISABLE_IPV4" != "1" ] && {
curl --fail --max-time 150 --connect-timeout 20 --max-filesize 41943040 -k -L "$1" | cut_local >"$ZIPLISTTMP" &&
{
dlsize=$(LC_ALL=C LANG=C wc -c "$ZIPLISTTMP" | xargs | cut -f 1 -d ' ')
if [ $dlsize -lt 102400 ]; then
echo list file is too small. can be bad.
exit 2
fi
ip2net4 <"$ZIPLISTTMP" | zz "$2"
rm -f "$ZIPLISTTMP"
}
}
}

8
ipset/clear_lists.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/sh
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
rm -f "$ZIPLIST"* "$ZIPLIST6"* "$ZIPLIST_USER" "$ZIPLIST_USER6" "$ZIPLIST_IPBAN"* "$ZIPLIST_IPBAN6"* "$ZIPLIST_USER_IPBAN" "$ZIPLIST_USER_IPBAN6" "$ZIPLIST_EXCLUDE" "$ZIPLIST_EXCLUDE6" "$ZHOSTLIST"*

308
ipset/create_ipset.sh Executable file
View File

@@ -0,0 +1,308 @@
#!/bin/sh
# create ipset or ipfw table from resolved ip's
# $1=no-update - do not update ipset, only create if its absent
# $1=clear - clear ipset
EXEDIR="$(dirname "$0")"
EXEDIR="$(cd "$EXEDIR"; pwd)"
. "$EXEDIR/def.sh"
. "$ZAPRET_BASE/common/fwtype.sh"
. "$ZAPRET_BASE/common/nft.sh"
IPSET_CMD="$TMPDIR/ipset_cmd.txt"
IPSET_SAVERAM_CHUNK_SIZE=20000
IPSET_SAVERAM_MIN_FILESIZE=131072
NFSET_TEMP="$TMPDIR/nfset_temp.txt"
NFSET_SAVERAM_MIN_FILESIZE=16384
NFSET_SAVERAM_CHUNK_SIZE=1000
IPSET_HOOK_TEMP="$TMPDIR/ipset_hook.txt"
while [ -n "$1" ]; do
[ "$1" = "no-update" ] && NO_UPDATE=1
[ "$1" = "clear" ] && DO_CLEAR=1
shift
done
file_extract_lines()
{
# $1 - filename
# $2 - from line (starting with 0)
# $3 - line count
# awk "{ err=1 } NR < $(($2+1)) { next } { print; err=0 } NR == $(($2+$3)) { exit err } END {exit err}" "$1"
$AWK "NR < $(($2+1)) { next } { print } NR == $(($2+$3)) { exit }" "$1"
}
ipset_restore_chunked()
{
# $1 - filename
# $2 - chunk size
local pos lines
[ -f "$1" ] || return
lines=$(wc -l <"$1")
pos=$lines
while [ "$pos" -gt "0" ]; do
pos=$((pos-$2))
[ "$pos" -lt "0" ] && pos=0
file_extract_lines "$1" $pos $2 | ipset -! restore
sed -i "$(($pos+1)),$ d" "$1"
done
}
ipset_get_script()
{
# $1 - ipset name
sed -nEe "s/^.+$/add $1 &/p"
}
ipset_get_script_from_file()
{
# $1 - filename
# $2 - ipset name
zzcat "$1" | sort -u | ipset_get_script $2
}
ipset_restore()
{
# $1 - ipset name
# $2 - filename
zzexist "$2" || return
local fsize=$(zzsize "$2")
local svram=0
# do not saveram small files. file can also be gzipped
[ "$SAVERAM" = "1" ] && [ "$fsize" -ge "$IPSET_SAVERAM_MIN_FILESIZE" ] && svram=1
local T="Adding to ipset $1 "
[ "$svram" = "1" ] && T="$T (saveram)"
T="$T : $f"
echo $T
if [ "$svram" = "1" ]; then
ipset_get_script_from_file "$2" "$1" >"$IPSET_CMD"
ipset_restore_chunked "$IPSET_CMD" $IPSET_SAVERAM_CHUNK_SIZE
rm -f "$IPSET_CMD"
else
ipset_get_script_from_file "$2" "$1" | ipset -! restore
fi
}
create_ipset()
{
if [ "$1" -eq "6" ]; then
FAMILY=inet6
else
FAMILY=inet
fi
ipset create $2 $3 $4 family $FAMILY 2>/dev/null || {
[ "$NO_UPDATE" = "1" ] && return 0
}
ipset flush $2
[ "$DO_CLEAR" = "1" ] || {
for f in "$5" "$6" ; do
ipset_restore "$2" "$f"
done
[ -n "$IPSET_HOOK" ] && $IPSET_HOOK $2 | ipset_get_script $2 | ipset -! restore
}
return 0
}
nfset_get_script_multi()
{
# $1 - set name
# $2,$3,... - filenames
# all in one shot. this allows to merge overlapping ranges
# good but eats lots of RAM
local set=$1 nonempty N=1 f
shift
# first we need to make sure at least one element exists or nft will fail
while :
do
eval f=\$$N
[ -n "$f" ] || break
nonempty=$(zzexist "$f" && zzcat "$f" 2>/dev/null | head -n 1)
[ -n "$nonempty" ] && break
N=$(($N+1))
done
[ -n "$nonempty" ] && {
echo "add element inet $ZAPRET_NFT_TABLE $set {"
while [ -n "$1" ]; do
zzexist "$1" && zzcat "$1" | sed -nEe "s/^.+$/&,/p"
shift
done
echo "}"
}
}
nfset_restore()
{
# $1 - set name
# $2,$3,... - filenames
echo "Adding to nfset $1 : $2 $3 $4 $5"
local hookfile
[ -n "$IPSET_HOOK" ] && {
$IPSET_HOOK $1 >"$IPSET_HOOK_TEMP"
[ -s "$IPSET_HOOK_TEMP" ] && hookfile=$IPSET_HOOK_TEMP
}
nfset_get_script_multi "$@" $hookfile | nft -f -
rm -f "$IPSET_HOOK_TEMP"
}
create_nfset()
{
# $1 - family
# $2 - set name
# $3 - maxelem
# $4,$5 - list files
local policy
[ $SAVERAM = "1" ] && policy="policy memory;"
nft_create_set $2 "type ipv${1}_addr; size $3; flags interval; auto-merge; $policy" || {
[ "$NO_UPDATE" = "1" ] && return 0
nft flush set inet $ZAPRET_NFT_TABLE $2
}
[ "$DO_CLEAR" = "1" ] || {
nfset_restore $2 $4 $5
}
return 0
}
add_ipfw_table()
{
# $1 - table name
sed -nEe "s/^.+$/table $1 add &/p" | ipfw -q /dev/stdin
}
populate_ipfw_table()
{
# $1 - table name
# $2 - ip list file
zzexist "$2" || return
zzcat "$2" | sort -u | add_ipfw_table $1
}
create_ipfw_table()
{
# $1 - table name
# $2 - table options
# $3,$4, ... - ip list files. can be v4,v6 or mixed
local name=$1
ipfw table "$name" create $2 2>/dev/null || {
[ "$NO_UPDATE" = "1" ] && return 0
}
ipfw -q table $1 flush
shift
shift
[ "$DO_CLEAR" = "1" ] || {
while [ -n "$1" ]; do
echo "Adding to ipfw table $name : $1"
populate_ipfw_table $name "$1"
shift
done
[ -n "$IPSET_HOOK" ] && $IPSET_HOOK $name | add_ipfw_table $name
}
return 0
}
print_reloading_backend()
{
# $1 - backend name
local s="reloading $1 backend"
if [ "$NO_UPDATE" = 1 ]; then
s="$s (no-update)"
elif [ "$DO_CLEAR" = 1 ]; then
s="$s (clear)"
else
s="$s (forced-update)"
fi
echo $s
}
oom_adjust_high
get_fwtype
if [ -n "$LISTS_RELOAD" ] ; then
if [ "$LISTS_RELOAD" = "-" ] ; then
echo not reloading ip list backend
true
else
echo executing custom ip list reload command : $LISTS_RELOAD
$LISTS_RELOAD
[ -n "$IPSET_HOOK" ] && $IPSET_HOOK
fi
else
case "$FWTYPE" in
iptables)
# ipset seem to buffer the whole script to memory
# on low RAM system this can cause oom errors
# in SAVERAM mode we feed script lines in portions starting from the end, while truncating source file to free /tmp space
# only /tmp is considered tmpfs. other locations mean tmpdir was redirected to a disk
SAVERAM=0
[ "$TMPDIR" = "/tmp" ] && {
RAMSIZE=$($GREP MemTotal /proc/meminfo | $AWK '{print $2}')
[ "$RAMSIZE" -lt "110000" ] && SAVERAM=1
}
print_reloading_backend ipset
[ "$DISABLE_IPV4" != "1" ] && {
create_ipset 4 $ZIPSET hash:net "$IPSET_OPT" "$ZIPLIST" "$ZIPLIST_USER"
create_ipset 4 $ZIPSET_IPBAN hash:net "$IPSET_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN"
create_ipset 4 $ZIPSET_EXCLUDE hash:net "$IPSET_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE"
}
[ "$DISABLE_IPV6" != "1" ] && {
create_ipset 6 $ZIPSET6 hash:net "$IPSET_OPT" "$ZIPLIST6" "$ZIPLIST_USER6"
create_ipset 6 $ZIPSET_IPBAN6 hash:net "$IPSET_OPT" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6"
create_ipset 6 $ZIPSET_EXCLUDE6 hash:net "$IPSET_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE6"
}
true
;;
nftables)
nft_create_table && {
SAVERAM=0
RAMSIZE=$($GREP MemTotal /proc/meminfo | $AWK '{print $2}')
[ "$RAMSIZE" -lt "420000" ] && SAVERAM=1
print_reloading_backend "nftables set"
[ "$DISABLE_IPV4" != "1" ] && {
create_nfset 4 $ZIPSET $SET_MAXELEM "$ZIPLIST" "$ZIPLIST_USER"
create_nfset 4 $ZIPSET_IPBAN $SET_MAXELEM "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN"
create_nfset 4 $ZIPSET_EXCLUDE $SET_MAXELEM_EXCLUDE "$ZIPLIST_EXCLUDE"
}
[ "$DISABLE_IPV6" != "1" ] && {
create_nfset 6 $ZIPSET6 $SET_MAXELEM "$ZIPLIST6" "$ZIPLIST_USER6"
create_nfset 6 $ZIPSET_IPBAN6 $SET_MAXELEM "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6"
create_nfset 6 $ZIPSET_EXCLUDE6 $SET_MAXELEM_EXCLUDE "$ZIPLIST_EXCLUDE6"
}
true
}
;;
ipfw)
print_reloading_backend "ipfw table"
if [ "$DISABLE_IPV4" != "1" ] && [ "$DISABLE_IPV6" != "1" ]; then
create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST" "$ZIPLIST_USER" "$ZIPLIST6" "$ZIPLIST_USER6"
create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6"
create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE" "$ZIPLIST_EXCLUDE6"
elif [ "$DISABLE_IPV4" != "1" ]; then
create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST" "$ZIPLIST_USER"
create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN"
create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE"
elif [ "$DISABLE_IPV6" != "1" ]; then
create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST6" "$ZIPLIST_USER6"
create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6"
create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE6"
else
create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT"
create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT"
create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE"
fi
true
;;
*)
echo no supported ip list backend found
true
;;
esac
fi

283
ipset/def.sh Normal file
View File

@@ -0,0 +1,283 @@
EXEDIR="$(dirname "$0")"
EXEDIR="$(cd "$EXEDIR"; pwd)"
ZAPRET_BASE=${ZAPRET_BASE:-"$(cd "$EXEDIR/.."; pwd)"}
ZAPRET_RW=${ZAPRET_RW:-"$ZAPRET_BASE"}
ZAPRET_CONFIG=${ZAPRET_CONFIG:-"$ZAPRET_RW/config"}
IPSET_RW_DIR="$ZAPRET_RW/ipset"
[ -f "$ZAPRET_CONFIG" ] && . "$ZAPRET_CONFIG"
. "$ZAPRET_BASE/common/base.sh"
[ -z "$TMPDIR" ] && TMPDIR=/tmp
[ -z "$GZIP_LISTS" ] && GZIP_LISTS=1
[ -z "$SET_MAXELEM" ] && SET_MAXELEM=262144
[ -z "$IPSET_OPT" ] && IPSET_OPT="hashsize 262144 maxelem $SET_MAXELEM"
[ -z "$SET_MAXELEM_EXCLUDE" ] && SET_MAXELEM_EXCLUDE=65536
[ -z "$IPSET_OPT_EXCLUDE" ] && IPSET_OPT_EXCLUDE="hashsize 1024 maxelem $SET_MAXELEM_EXCLUDE"
[ -z "$IPFW_TABLE_OPT" ] && IPFW_TABLE_OPT="algo addr:radix"
[ -z "$IPFW_TABLE_OPT_EXCLUDE" ] && IPFW_TABLE_OPT_EXCLUDE="algo addr:radix"
ZIPSET=zapret
ZIPSET6=zapret6
ZIPSET_EXCLUDE=nozapret
ZIPSET_EXCLUDE6=nozapret6
ZIPLIST="$IPSET_RW_DIR/zapret-ip.txt"
ZIPLIST6="$IPSET_RW_DIR/zapret-ip6.txt"
ZIPLIST_EXCLUDE="$IPSET_RW_DIR/zapret-ip-exclude.txt"
ZIPLIST_EXCLUDE6="$IPSET_RW_DIR/zapret-ip-exclude6.txt"
ZIPLIST_USER="$IPSET_RW_DIR/zapret-ip-user.txt"
ZIPLIST_USER6="$IPSET_RW_DIR/zapret-ip-user6.txt"
ZUSERLIST="$IPSET_RW_DIR/zapret-hosts-user.txt"
ZHOSTLIST="$IPSET_RW_DIR/zapret-hosts.txt"
ZIPSET_IPBAN=ipban
ZIPSET_IPBAN6=ipban6
ZIPLIST_IPBAN="$IPSET_RW_DIR/zapret-ip-ipban.txt"
ZIPLIST_IPBAN6="$IPSET_RW_DIR/zapret-ip-ipban6.txt"
ZIPLIST_USER_IPBAN="$IPSET_RW_DIR/zapret-ip-user-ipban.txt"
ZIPLIST_USER_IPBAN6="$IPSET_RW_DIR/zapret-ip-user-ipban6.txt"
ZUSERLIST_IPBAN="$IPSET_RW_DIR/zapret-hosts-user-ipban.txt"
ZUSERLIST_EXCLUDE="$IPSET_RW_DIR/zapret-hosts-user-exclude.txt"
[ -n "$IP2NET" ] || IP2NET="$ZAPRET_BASE/ip2net/ip2net"
[ -n "$MDIG" ] || MDIG="$ZAPRET_BASE/mdig/mdig"
[ -z "$MDIG_THREADS" ] && MDIG_THREADS=30
# BSD grep is damn slow with -f option. prefer GNU grep (ggrep) if present
# MacoS in cron does not include /usr/local/bin to PATH
if [ -x /usr/local/bin/ggrep ] ; then
GREP=/usr/local/bin/ggrep
elif [ -x /usr/local/bin/grep ] ; then
GREP=/usr/local/bin/grep
elif exists ggrep; then
GREP=$(whichq ggrep)
else
GREP=$(whichq grep)
fi
# GNU awk is faster
if exists gawk; then
AWK=gawk
else
AWK=awk
fi
grep_supports_b()
{
# \b does not work with BSD grep
$GREP --version 2>&1 | $GREP -qE "BusyBox|GNU"
}
get_ip_regex()
{
REG_IPV4='((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[12][0-9]|3[012]))?'
REG_IPV6='[0-9a-fA-F]{1,4}:([0-9a-fA-F]{1,4}|:)+(\/([0-9][0-9]?|1[01][0-9]|12[0-8]))?'
# good but too slow
# REG_IPV6='([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}(/[0-9]+)?|([0-9a-fA-F]{1,4}:){1,7}:(/[0-9]+)?|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}(/[0-9]+)?|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}(/[0-9]+)?|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}(/[0-9]+)?|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}(/[0-9]+)?|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}(/[0-9]+)?|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})(/[0-9]+)?|:((:[0-9a-fA-F]{1,4}){1,7}|:)(/([0-9][0-9]?|1[01][0-9]|12[0-8]))?'
# grep_supports_b && {
# REG_IPV4="\b$REG_IPV4\b"
# REG_IPV6="\b$REG_IPV6\b"
# }
}
ip2net4()
{
if [ -x "$IP2NET" ]; then
"$IP2NET" -4 $IP2NET_OPT4
else
sort -u
fi
}
ip2net6()
{
if [ -x "$IP2NET" ]; then
"$IP2NET" -6 $IP2NET_OPT6
else
sort -u
fi
}
zzexist()
{
[ -f "$1.gz" ] || [ -f "$1" ]
}
zztest()
{
gzip -t "$1" 2>/dev/null
}
zzcat()
{
if [ -f "$1.gz" ]; then
gunzip -c "$1.gz"
elif [ -f "$1" ]; then
if zztest "$1"; then
gunzip -c "$1"
else
cat "$1"
fi
fi
}
zz()
{
if [ "$GZIP_LISTS" = "1" ]; then
gzip -c >"$1.gz"
rm -f "$1"
else
cat >"$1"
rm -f "$1.gz"
fi
}
zzsize()
{
local f="$1"
[ -f "$1.gz" ] && f="$1.gz"
if [ -f "$f" ]; then
wc -c <"$f" | xargs
else
printf 0
fi
}
zzcopy()
{
local is_gz=0
zztest "$1" && is_gz=1
if [ "$GZIP_LISTS" = 1 -a $is_gz = 1 ]; then
cp "$1" "${2}.gz"
elif [ "$GZIP_LISTS" != 1 -a $is_gz != 1 ]; then
cp "$1" "$2"
else
zzcat "$1" | zz "$2"
fi
}
digger()
{
# $1 - family (4|6)
# $2 - s=enable mdig stats
if [ -x "$MDIG" ]; then
local cmd
[ "$2" = "s" ] && cmd=--stats=1000
"$MDIG" --family=$1 --threads=$MDIG_THREADS $cmd
else
local A=A
[ "$1" = "6" ] && A=AAAA
dig $A +short +time=8 +tries=2 -f - | $GREP -E '^[^;].*[^\.]$'
fi
}
filedigger()
{
# $1 - hostlist
# $2 - family (4|6)
>&2 echo digging $(wc -l <"$1" | xargs) ipv$2 domains : "$1"
zzcat "$1" | digger $2 s
}
flush_dns_cache()
{
echo clearing all known DNS caches
if exists killall; then
killall -HUP dnsmasq 2>/dev/null
# MacOS
killall -HUP mDNSResponder 2>/dev/null
elif exists pkill; then
pkill -HUP ^dnsmasq$
else
echo no mass killer available ! cant flush dnsmasq
fi
if exists rndc; then
rndc flush
fi
if exists systemd-resolve; then
systemd-resolve --flush-caches
fi
}
dnstest()
{
local ip="$(echo w3.org | digger 46)"
[ -n "$ip" ]
}
dnstest_with_cache_clear()
{
flush_dns_cache
if dnstest ; then
echo DNS is working
return 0
else
echo "! DNS is not working"
return 1
fi
}
cut_local()
{
$GREP -vE '^192\.168\.|^127\.|^10\.'
}
cut_local6()
{
$GREP -vE '^::|^fc..:|^fd..:|^fe8.:|^fe9.:|^fea.:|^feb.:|^FC..:|^FD..:|^FE8.:|^FE9.:|^FEA.:|^FEB.:'
}
oom_adjust_high()
{
[ -f /proc/$$/oom_score_adj ] && {
echo setting high oom kill priority
echo -n 100 >/proc/$$/oom_score_adj
}
}
getexclude()
{
oom_adjust_high
dnstest_with_cache_clear || return
[ -f "$ZUSERLIST_EXCLUDE" ] && {
[ "$DISABLE_IPV4" != "1" ] && filedigger "$ZUSERLIST_EXCLUDE" 4 | sort -u > "$ZIPLIST_EXCLUDE"
[ "$DISABLE_IPV6" != "1" ] && filedigger "$ZUSERLIST_EXCLUDE" 6 | sort -u > "$ZIPLIST_EXCLUDE6"
}
return 0
}
_get_ipban()
{
[ -f "$ZUSERLIST_IPBAN" ] && {
[ "$DISABLE_IPV4" != "1" ] && filedigger "$ZUSERLIST_IPBAN" 4 | cut_local | sort -u > "$ZIPLIST_USER_IPBAN"
[ "$DISABLE_IPV6" != "1" ] && filedigger "$ZUSERLIST_IPBAN" 6 | cut_local6 | sort -u > "$ZIPLIST_USER_IPBAN6"
}
}
getuser()
{
getexclude || return
[ -f "$ZUSERLIST" ] && {
[ "$DISABLE_IPV4" != "1" ] && filedigger "$ZUSERLIST" 4 | cut_local | sort -u > "$ZIPLIST_USER"
[ "$DISABLE_IPV6" != "1" ] && filedigger "$ZUSERLIST" 6 | cut_local6 | sort -u > "$ZIPLIST_USER6"
}
_get_ipban
return 0
}
getipban()
{
getexclude || return
_get_ipban
return 0
}
hup_zapret_daemons()
{
echo forcing zapret daemons to reload their hostlist
if exists killall; then
killall -HUP tpws nfqws dvtws 2>/dev/null
elif exists pkill; then
pkill -HUP ^tpws$
pkill -HUP ^nfqws$
pkill -HUP ^dvtws$
else
echo no mass killer available ! cant HUP zapret daemons
fi
}

View File

@@ -0,0 +1,13 @@
#!/bin/sh
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
getuser && {
. "$IPSET_DIR/antifilter.helper"
get_antifilter https://antifilter.download/list/allyouneed.lst "$ZIPLIST"
}
"$IPSET_DIR/create_ipset.sh"

13
ipset/get_antifilter_ip.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
getuser && {
. "$IPSET_DIR/antifilter.helper"
get_antifilter https://antifilter.download/list/ip.lst "$ZIPLIST"
}
"$IPSET_DIR/create_ipset.sh"

View File

@@ -0,0 +1,13 @@
#!/bin/sh
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
getuser && {
. "$IPSET_DIR/antifilter.helper"
get_antifilter https://antifilter.download/list/ipresolve.lst "$ZIPLIST"
}
"$IPSET_DIR/create_ipset.sh"

13
ipset/get_antifilter_ipsmart.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
getuser && {
. "$IPSET_DIR/antifilter.helper"
get_antifilter https://antifilter.network/download/ipsmart.lst "$ZIPLIST"
}
"$IPSET_DIR/create_ipset.sh"

13
ipset/get_antifilter_ipsum.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
getuser && {
. "$IPSET_DIR/antifilter.helper"
get_antifilter https://antifilter.download/list/ipsum.lst "$ZIPLIST"
}
"$IPSET_DIR/create_ipset.sh"

36
ipset/get_antizapret_domains.sh Executable file
View File

@@ -0,0 +1,36 @@
#!/bin/sh
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
# useful in case ipban set is used in custom scripts
FAIL=
getipban || FAIL=1
"$IPSET_DIR/create_ipset.sh"
[ -n "$FAIL" ] && exit
ZURL=https://antizapret.prostovpn.org:8443/domains-export.txt
ZDOM="$TMPDIR/zapret.txt"
curl -H "Accept-Encoding: gzip" -k --fail --max-time 600 --connect-timeout 5 --retry 3 --max-filesize 251658240 "$ZURL" | gunzip - >"$ZDOM" ||
{
echo domain list download failed
exit 2
}
dlsize=$(LC_ALL=C LANG=C wc -c "$ZDOM" | xargs | cut -f 1 -d ' ')
if test $dlsize -lt 102400; then
echo list file is too small. can be bad.
exit 2
fi
sort -u "$ZDOM" | zz "$ZHOSTLIST"
rm -f "$ZDOM"
hup_zapret_daemons
exit 0

10
ipset/get_config.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/sh
# run script specified in config
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
[ -f "$IPSET_DIR/../config" ] && . "$IPSET_DIR/../config"
[ -z "$GETLIST" ] && GETLIST=get_ipban.sh
[ -x "$IPSET_DIR/$GETLIST" ] && exec "$IPSET_DIR/$GETLIST"

11
ipset/get_exclude.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
# resolve user host list
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
getexclude
"$IPSET_DIR/create_ipset.sh"

11
ipset/get_ipban.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
# resolve only ipban user host list
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
getipban
"$IPSET_DIR/create_ipset.sh"

65
ipset/get_reestr_hostlist.sh Executable file
View File

@@ -0,0 +1,65 @@
#!/bin/sh
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
ZREESTR="$TMPDIR/zapret.txt.gz"
IPB="$TMPDIR/ipb.txt"
ZURL_REESTR=https://raw.githubusercontent.com/zapret-info/z-i/master/dump.csv.gz
dl_checked()
{
# $1 - url
# $2 - file
# $3 - minsize
# $4 - maxsize
# $5 - maxtime
curl -k --fail --max-time $5 --connect-timeout 10 --retry 4 --max-filesize $4 -o "$2" "$1" ||
{
echo list download failed : $1
return 2
}
dlsize=$(LC_ALL=C LANG=C wc -c "$2" | xargs | cut -f 1 -d ' ')
if test $dlsize -lt $3; then
echo list is too small : $dlsize bytes. can be bad.
return 2
fi
return 0
}
reestr_list()
{
LC_ALL=C LANG=C gunzip -c "$ZREESTR" | cut -s -f2 -d';' | LC_ALL=C LANG=C nice -n 5 sed -Ee 's/^\*\.(.+)$/\1/' -ne 's/^[a-z0-9A-Z._-]+$/&/p' | $AWK '{ print tolower($0) }'
}
reestr_extract_ip()
{
LC_ALL=C LANG=C gunzip -c | nice -n 5 $AWK -F ';' '($1 ~ /^([0-9]{1,3}\.){3}[0-9]{1,3}/) && (($2 == "" && $3 == "") || ($1 == $2)) {gsub(/ \| /, RS); print $1}' | LC_ALL=C LANG=C $AWK '{split($1, a, /\|/); for (i in a) {print a[i]}}'
}
ipban_fin()
{
getipban
"$IPSET_DIR/create_ipset.sh"
}
dl_checked "$ZURL_REESTR" "$ZREESTR" 204800 251658240 600 || {
ipban_fin
exit 2
}
reestr_list | sort -u | zz "$ZHOSTLIST"
reestr_extract_ip <"$ZREESTR" >"$IPB"
rm -f "$ZREESTR"
[ "$DISABLE_IPV4" != "1" ] && $AWK '/^([0-9]{1,3}\.){3}[0-9]{1,3}($|(\/[0-9]{2}$))/' "$IPB" | cut_local | ip2net4 | zz "$ZIPLIST_IPBAN"
[ "$DISABLE_IPV6" != "1" ] && $AWK '/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}($|(\/[0-9]{2,3}$))/' "$IPB" | cut_local6 | ip2net6 | zz "$ZIPLIST_IPBAN6"
rm -f "$IPB"
hup_zapret_daemons
ipban_fin
exit 0

47
ipset/get_reestr_preresolved.sh Executable file
View File

@@ -0,0 +1,47 @@
#!/bin/sh
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
TMPLIST="$TMPDIR/list.txt"
BASEURL="https://raw.githubusercontent.com/bol-van/rulist/main"
URL4="$BASEURL/reestr_resolved4.txt"
URL6="$BASEURL/reestr_resolved6.txt"
IPB4="$BASEURL/reestr_ipban4.txt"
IPB6="$BASEURL/reestr_ipban6.txt"
dl()
{
# $1 - url
# $2 - file
# $3 - minsize
# $4 - maxsize
curl -H "Accept-Encoding: gzip" -k --fail --max-time 120 --connect-timeout 10 --retry 4 --max-filesize $4 -o "$TMPLIST" "$1" ||
{
echo list download failed : $1
exit 2
}
dlsize=$(LC_ALL=C LANG=C wc -c "$TMPLIST" | xargs | cut -f 1 -d ' ')
if test $dlsize -lt $3; then
echo list is too small : $dlsize bytes. can be bad.
exit 2
fi
zzcopy "$TMPLIST" "$2"
rm -f "$TMPLIST"
}
getuser && {
[ "$DISABLE_IPV4" != "1" ] && {
dl "$URL4" "$ZIPLIST" 32768 4194304
dl "$IPB4" "$ZIPLIST_IPBAN" 8192 1048576
}
[ "$DISABLE_IPV6" != "1" ] && {
dl "$URL6" "$ZIPLIST6" 8192 4194304
dl "$IPB6" "$ZIPLIST_IPBAN6" 128 1048576
}
}
"$IPSET_DIR/create_ipset.sh"

View File

@@ -0,0 +1,47 @@
#!/bin/sh
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
TMPLIST="$TMPDIR/list.txt"
BASEURL="https://raw.githubusercontent.com/bol-van/rulist/main"
URL4="$BASEURL/reestr_smart4.txt"
URL6="$BASEURL/reestr_smart6.txt"
IPB4="$BASEURL/reestr_ipban4.txt"
IPB6="$BASEURL/reestr_ipban6.txt"
dl()
{
# $1 - url
# $2 - file
# $3 - minsize
# $4 - maxsize
curl -H "Accept-Encoding: gzip" -k --fail --max-time 120 --connect-timeout 10 --retry 4 --max-filesize $4 -o "$TMPLIST" "$1" ||
{
echo list download failed : $1
exit 2
}
dlsize=$(LC_ALL=C LANG=C wc -c "$TMPLIST" | xargs | cut -f 1 -d ' ')
if test $dlsize -lt $3; then
echo list is too small : $dlsize bytes. can be bad.
exit 2
fi
zzcopy "$TMPLIST" "$2"
rm -f "$TMPLIST"
}
getuser && {
[ "$DISABLE_IPV4" != "1" ] && {
dl "$URL4" "$ZIPLIST" 32768 4194304
dl "$IPB4" "$ZIPLIST_IPBAN" 8192 1048576
}
[ "$DISABLE_IPV6" != "1" ] && {
dl "$URL6" "$ZIPLIST6" 8192 4194304
dl "$IPB6" "$ZIPLIST_IPBAN6" 128 1048576
}
}
"$IPSET_DIR/create_ipset.sh"

View File

@@ -0,0 +1,45 @@
#!/bin/sh
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
TMPLIST="$TMPDIR/list_nethub.txt"
BASEURL="https://raw.githubusercontent.com/bol-van/rulist/main"
URL="$BASEURL/reestr_hostname_resolvable.txt"
IPB4="$BASEURL/reestr_ipban4.txt"
IPB6="$BASEURL/reestr_ipban6.txt"
dl()
{
# $1 - url
# $2 - file
# $3 - minsize
# $4 - maxsize
curl -H "Accept-Encoding: gzip" -k --fail --max-time 120 --connect-timeout 10 --retry 4 --max-filesize $4 -o "$TMPLIST" "$1" ||
{
echo list download failed : $1
exit 2
}
dlsize=$(LC_ALL=C LANG=C wc -c "$TMPLIST" | xargs | cut -f 1 -d ' ')
if test $dlsize -lt $3; then
echo list is too small : $dlsize bytes. can be bad.
exit 2
fi
zzcopy "$TMPLIST" "$2"
rm -f "$TMPLIST"
}
dl "$URL" "$ZHOSTLIST" 65536 67108864
hup_zapret_daemons
[ "$DISABLE_IPV4" != "1" ] && dl "$IPB4" "$ZIPLIST_IPBAN" 8192 1048576
[ "$DISABLE_IPV6" != "1" ] && dl "$IPB6" "$ZIPLIST_IPBAN6" 128 1048576
getipban
"$IPSET_DIR/create_ipset.sh"
exit 0

83
ipset/get_reestr_resolve.sh Executable file
View File

@@ -0,0 +1,83 @@
#!/bin/sh
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
ZREESTR="$TMPDIR/zapret.txt.gz"
ZDIG="$TMPDIR/zapret-dig.txt"
IPB="$TMPDIR/ipb.txt"
ZIPLISTTMP="$TMPDIR/zapret-ip.txt"
#ZURL=https://reestr.rublacklist.net/api/current
ZURL_REESTR=https://raw.githubusercontent.com/zapret-info/z-i/master/dump.csv.gz
dl_checked()
{
# $1 - url
# $2 - file
# $3 - minsize
# $4 - maxsize
# $5 - maxtime
curl -k --fail --max-time $5 --connect-timeout 10 --retry 4 --max-filesize $4 -o "$2" "$1" ||
{
echo list download failed : $1
return 2
}
dlsize=$(LC_ALL=C LANG=C wc -c "$2" | xargs | cut -f 1 -d ' ')
if test $dlsize -lt $3; then
echo list is too small : $dlsize bytes. can be bad.
return 2
fi
return 0
}
reestr_list()
{
LC_ALL=C LANG=C gunzip -c "$ZREESTR" | cut -s -f2 -d';' | LC_ALL=C LANG=C nice -n 5 sed -Ee 's/^\*\.(.+)$/\1/' -ne 's/^[a-z0-9A-Z._-]+$/&/p' | $AWK '{ print tolower($0) }'
}
reestr_extract_ip()
{
LC_ALL=C LANG=C gunzip -c | nice -n 5 $AWK -F ';' '($1 ~ /^([0-9]{1,3}\.){3}[0-9]{1,3}/) && (($2 == "" && $3 == "") || ($1 == $2)) {gsub(/ \| /, RS); print $1}' | LC_ALL=C LANG=C $AWK '{split($1, a, /\|/); for (i in a) {print a[i]}}'
}
getuser && {
# both disabled
[ "$DISABLE_IPV4" = "1" ] && [ "$DISABLE_IPV6" = "1" ] && exit 0
dl_checked "$ZURL_REESTR" "$ZREESTR" 204800 251658240 600 || exit 2
echo preparing ipban list ..
reestr_extract_ip <"$ZREESTR" >"$IPB"
[ "$DISABLE_IPV4" != "1" ] && $AWK '/^([0-9]{1,3}\.){3}[0-9]{1,3}($|(\/[0-9]{2}$))/' "$IPB" | cut_local | ip2net4 | zz "$ZIPLIST_IPBAN"
[ "$DISABLE_IPV6" != "1" ] && $AWK '/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}($|(\/[0-9]{2,3}$))/' "$IPB" | cut_local6 | ip2net6 | zz "$ZIPLIST_IPBAN6"
rm -f "$IPB"
echo preparing dig list ..
reestr_list | sort -u >"$ZDIG"
rm -f "$ZREESTR"
echo digging started. this can take long ...
[ "$DISABLE_IPV4" != "1" ] && {
filedigger "$ZDIG" 4 | cut_local >"$ZIPLISTTMP" || {
rm -f "$ZDIG"
exit 1
}
ip2net4 <"$ZIPLISTTMP" | zz "$ZIPLIST"
rm -f "$ZIPLISTTMP"
}
[ "$DISABLE_IPV6" != "1" ] && {
filedigger "$ZDIG" 6 | cut_local6 >"$ZIPLISTTMP" || {
rm -f "$ZDIG"
exit 1
}
ip2net6 <"$ZIPLISTTMP" | zz "$ZIPLIST6"
rm -f "$ZIPLISTTMP"
}
rm -f "$ZDIG"
}
"$IPSET_DIR/create_ipset.sh"

42
ipset/get_refilter_domains.sh Executable file
View File

@@ -0,0 +1,42 @@
#!/bin/sh
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
TMPLIST="$TMPDIR/list.txt"
URL="https://github.com/1andrevich/Re-filter-lists/releases/latest/download/domains_all.lst"
dl()
{
# $1 - url
# $2 - file
# $3 - minsize
# $4 - maxsize
curl -L -H "Accept-Encoding: gzip" -k --fail --max-time 60 --connect-timeout 10 --retry 4 --max-filesize $4 -o "$TMPLIST" "$1" ||
{
echo list download failed : $1
exit 2
}
dlsize=$(LC_ALL=C LANG=C wc -c "$TMPLIST" | xargs | cut -f 1 -d ' ')
if test $dlsize -lt $3; then
echo list is too small : $dlsize bytes. can be bad.
exit 2
fi
zzcopy "$TMPLIST" "$2"
rm -f "$TMPLIST"
}
# useful in case ipban set is used in custom scripts
FAIL=
getipban || FAIL=1
"$IPSET_DIR/create_ipset.sh"
[ -n "$FAIL" ] && exit
dl "$URL" "$ZHOSTLIST" 32768 4194304
hup_zapret_daemons
exit 0

38
ipset/get_refilter_ipsum.sh Executable file
View File

@@ -0,0 +1,38 @@
#!/bin/sh
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
TMPLIST="$TMPDIR/list.txt"
URL="https://github.com/1andrevich/Re-filter-lists/releases/latest/download/ipsum.lst"
dl()
{
# $1 - url
# $2 - file
# $3 - minsize
# $4 - maxsize
curl -L -H "Accept-Encoding: gzip" -k --fail --max-time 60 --connect-timeout 10 --retry 4 --max-filesize $4 -o "$TMPLIST" "$1" ||
{
echo list download failed : $1
exit 2
}
dlsize=$(LC_ALL=C LANG=C wc -c "$TMPLIST" | xargs | cut -f 1 -d ' ')
if test $dlsize -lt $3; then
echo list is too small : $dlsize bytes. can be bad.
exit 2
fi
zzcopy "$TMPLIST" "$2"
rm -f "$TMPLIST"
}
getuser && {
[ "$DISABLE_IPV4" != "1" ] && {
dl "$URL" "$ZIPLIST" 32768 4194304
}
}
"$IPSET_DIR/create_ipset.sh"

11
ipset/get_user.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
# resolve user host list
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
getuser
"$IPSET_DIR/create_ipset.sh"

View File

@@ -0,0 +1,9 @@
127.0.0.0/8
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
169.254.0.0/16
100.64.0.0/10
::1
fc00::/7
fe80::/10

View File

@@ -35,7 +35,135 @@ function pktdebug(ctx, desync)
DLOG("desync:")
var_debug(desync)
end
-- basic desync function
-- prints function args
function argdebug(ctx,desync)
var_debug(desync.arg)
end
-- basic desync function
-- prints conntrack positions to DLOG
function posdebug(ctx,desync)
local s="posdebug:"
for i,pos in pairs({'n','d','b','s'}) do
s=s.." "..pos..pos_get(desync,pos)
end
s=s.." payload "..#desync.dis.payload
if desync.reasm_data then
s=s.." reasm "..#desync.reasm_data
end
if desync.decrypt_data then
s=s.." decrypt "..#desync.decrypt_data
end
if desync.replay_piece_count then
s=s.." replay "..desync.replay_piece.."/"..desync.replay_piece_count
end
DLOG(s)
end
-- applies # and $ prefixes. #var means var length, %var means var value
function apply_arg_prefix(arg)
for a,v in pairs(arg) do
local c = string.sub(v,1,1)
if v=='#' then
arg[a] = #_G[string.sub(v,2)]
elseif v=='%' then
arg[a] = _G[string.sub(v,2)]
elseif v=='\\' then
c = string.sub(v,2,2);
if c=='#' or c=='%' then
arg[a] = string.sub(v,2)
end
end
end
end
-- copy instance identification and args from execution plan to desync table
function apply_execution_plan(desync, plan)
desync.func = plan.func
desync.func_n = plan.func_n
desync.func_instance = plan.func_instance
desync.arg = deepcopy(plan.arg)
apply_arg_prefix(desync.arg)
end
-- redo what whould be done without orchestration
function replay_execution_plan(desync, plan)
for i=1,#plan do
if not payload_match_filter(desync.l7payload, plan[i].payload_filter) then
DLOG("orchestrator: not calling '"..desync.func_instance.."' because payload '"..desync.l7payload.."' does not match filter '"..plan[i].payload_filter.."'")
elseif not pos_check_range(desync, plan[i].range) then
DLOG("orchestrator: not calling '"..desync.func_instance.."' because pos "..pos_str(desync,plan[i].range.from).." "..pos_str(desync,plan[i].range.to).." is out of range '"..pos_range_str(plan[i].range).."'")
else
apply_execution_plan(desync, plan[i])
DLOG("orchestrator: executing '"..desync.func_instance.."'")
_G[plan[i].func](ctx, desync)
end
end
end
-- this function demonstrates how to stop execution of upcoming desync instances and take over their job
-- this can be used, for example, for orchestrating conditional processing without modifying of desync functions code
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-desync=desync_orchestrator_example --lua-desync=pass --lua-desync=pass
function desync_orchestrator_example(ctx, desync)
local plan = execution_plan(ctx)
if #plan>0 then
DLOG("orchestrator: taking over upcoming desync instances")
local desync_copy = deepcopy(desync)
execution_plan_cancel(ctx)
replay_execution_plan(desync_copy, plan)
end
end
-- these function duplicate range check logic from C code
-- mode must be n,d,b,s,x,a
-- pos is {mode,pos}
-- range is {from={mode,pos}, to={mode,pos}, upper_cutoff}
-- upper_cutoff = true means non-inclusive upper boundary
function pos_get(desync, mode)
if desync.track then
if mode=='n' then
return desync.outgoing and desync.track.pcounter_orig or desync.track.pcounter_reply
elseif mode=='d' then
return desync.outgoing and desync.track.pdcounter_orig or desync.track.pdcounter_reply
elseif mode=='b' then
return desync.outgoing and desync.track.pbcounter_orig or desync.track.pbcounter_reply
elseif mode=='s' and desync.track.tcp then
return desync.outgoing and (desync.track.tcp.seq - desync.track.tcp.seq0) or (desync.track.tcp.ack - desync.track.tcp.ack0)
end
end
return 0
end
function pos_check_from(desync, range)
if range.from.mode == 'x' then return false end
if range.from.mode ~= 'a' then
if desync.track then
return pos_get(desync, range.from.mode) >= range.from.pos
else
return false
end
end
return true;
end
function pos_check_to(desync, range)
local ps
if range.to.mode == 'x' then return false end
if range.to.mode ~= 'a' then
if desync.track then
ps = pos_get(desync, range.to.mode)
return (ps < range.to.pos) or not range.upper_cutoff and (ps == range.to.pos)
else
return false
end
end
return true;
end
function pos_check_range(desync, range)
return pos_check_from(desync,range) and pos_check_to(desync,range)
end
function pos_range_str(range)
return range.from.mode..range.from.pos..(range.upper_cutoff and '<' or '-')..range.to.mode..range.to.pos
end
function pos_str(desync, pos)
return pos.mode..pos_get(desync, pos.mode)
end
-- prepare standard rawsend options from desync
@@ -802,17 +930,21 @@ function direction_cutoff_opposite(ctx, desync, def)
instance_cutoff(ctx, true)
end
end
-- return true if l7payload matches filter l7payload_filter - comma separated list of payload types
function payload_match_filter(l7payload, l7payload_filter, def)
local argpl = l7payload_filter or def or "known"
local neg = string.sub(argpl,1,1)=="~"
local pl = neg and string.sub(argpl,2) or argpl
return neg ~= (in_list(pl, "all") or in_list(pl, l7payload) or in_list(pl, "known") and l7payload~="unknown" and l7payload~="empty")
end
-- check if desync payload type comply with payload type list in arg.payload
-- if arg.payload is not present - check for known payload - not empty and not unknown (nfqws1 behavior without "--desync-any-protocol" option)
-- if arg.payload is prefixed with '~' - it means negation
function payload_check(desync, def)
local b
local argpl = desync.arg.payload or def or "known"
local neg = string.sub(argpl,1,1)=="~"
local pl = neg and string.sub(argpl,2) or argpl
b = neg ~= (in_list(pl, "all") or in_list(pl, desync.l7payload) or in_list(pl, "known") and desync.l7payload~="unknown" and desync.l7payload~="empty")
if not b then
local b = payload_match_filter(desync.l7payload, desync.arg.payload, def)
if not b and b_debug then
local argpl = desync.arg.payload or def or "known"
DLOG("payload_check: payload '"..desync.l7payload.."' does not pass '"..argpl.."' filter")
end
return b
@@ -1031,4 +1163,3 @@ function ipfrag2(dis, ipfrag_options)
return {dis1,dis2}
end

View File

@@ -102,8 +102,8 @@ static void ConntrackInitTrack(t_ctrack *t)
{
memset(t, 0, sizeof(*t));
t->l7proto = L7_UNKNOWN;
t->scale_orig = t->scale_reply = SCALE_NONE;
time(&t->t_start);
t->pos.scale_orig = t->pos.scale_reply = SCALE_NONE;
time(&t->pos.t_start);
rawpacket_queue_init(&t->delayed);
lua_newtable(params.L);
t->lua_state = luaL_ref(params.L, LUA_REGISTRYINDEX);
@@ -136,86 +136,86 @@ static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr
if (bReverse)
{
t->pcounter_reply++;
t->pdcounter_reply += !!len_payload;
t->pbcounter_reply += len_payload;
t->pos.pcounter_reply++;
t->pos.pdcounter_reply += !!len_payload;
t->pos.pbcounter_reply += len_payload;
}
else
{
t->pcounter_orig++;
t->pdcounter_orig += !!len_payload;
t->pbcounter_orig += len_payload;
t->pos.pcounter_orig++;
t->pos.pdcounter_orig += !!len_payload;
t->pos.pbcounter_orig += len_payload;
}
if (tcphdr)
{
if (tcp_syn_segment(tcphdr))
{
if (t->state != SYN) ConntrackReInitTrack(t); // erase current entry
t->seq0 = ntohl(tcphdr->th_seq);
if (t->pos.state != SYN) ConntrackReInitTrack(t); // erase current entry
t->pos.seq0 = ntohl(tcphdr->th_seq);
}
else if (tcp_synack_segment(tcphdr))
{
// ignore SA dups
uint32_t seq0 = ntohl(tcphdr->th_ack) - 1;
if (t->state != SYN && t->seq0 != seq0)
if (t->pos.state != SYN && t->pos.seq0 != seq0)
ConntrackReInitTrack(t); // erase current entry
if (!t->seq0) t->seq0 = seq0;
t->ack0 = ntohl(tcphdr->th_seq);
if (!t->pos.seq0) t->pos.seq0 = seq0;
t->pos.ack0 = ntohl(tcphdr->th_seq);
}
else if (tcphdr->th_flags & (TH_FIN | TH_RST))
{
t->state = FIN;
t->pos.state = FIN;
}
else
{
if (t->state == SYN)
if (t->pos.state == SYN)
{
t->state = ESTABLISHED;
if (!bReverse && !t->ack0) t->ack0 = ntohl(tcphdr->th_ack) - 1;
t->pos.state = ESTABLISHED;
if (!bReverse && !t->pos.ack0) t->pos.ack0 = ntohl(tcphdr->th_ack) - 1;
}
}
scale = tcp_find_scale_factor(tcphdr);
mss = ntohs(tcp_find_mss(tcphdr));
if (bReverse)
{
t->pos_orig = t->seq_last = ntohl(tcphdr->th_ack);
t->ack_last = ntohl(tcphdr->th_seq);
t->pos_reply = t->ack_last + len_payload;
t->winsize_reply = ntohs(tcphdr->th_win);
t->winsize_reply_calc = t->winsize_reply;
if (t->scale_reply != SCALE_NONE) t->winsize_reply_calc <<= t->scale_reply;
if (mss && !t->mss_reply) t->mss_reply = mss;
if (scale != SCALE_NONE) t->scale_reply = scale;
t->pos.pos_orig = t->pos.seq_last = ntohl(tcphdr->th_ack);
t->pos.ack_last = ntohl(tcphdr->th_seq);
t->pos.pos_reply = t->pos.ack_last + len_payload;
t->pos.winsize_reply = ntohs(tcphdr->th_win);
t->pos.winsize_reply_calc = t->pos.winsize_reply;
if (t->pos.scale_reply != SCALE_NONE) t->pos.winsize_reply_calc <<= t->pos.scale_reply;
if (mss && !t->pos.mss_reply) t->pos.mss_reply = mss;
if (scale != SCALE_NONE) t->pos.scale_reply = scale;
}
else
{
t->seq_last = ntohl(tcphdr->th_seq);
t->pos_orig = t->seq_last + len_payload;
t->pos_reply = t->ack_last = ntohl(tcphdr->th_ack);
t->winsize_orig = ntohs(tcphdr->th_win);
t->winsize_orig_calc = t->winsize_orig;
if (t->scale_orig != SCALE_NONE) t->winsize_orig_calc <<= t->scale_orig;
if (mss && !t->mss_reply) t->mss_orig = mss;
if (scale != SCALE_NONE) t->scale_orig = scale;
t->pos.seq_last = ntohl(tcphdr->th_seq);
t->pos.pos_orig = t->pos.seq_last + len_payload;
t->pos.pos_reply = t->pos.ack_last = ntohl(tcphdr->th_ack);
t->pos.winsize_orig = ntohs(tcphdr->th_win);
t->pos.winsize_orig_calc = t->pos.winsize_orig;
if (t->pos.scale_orig != SCALE_NONE) t->pos.winsize_orig_calc <<= t->pos.scale_orig;
if (mss && !t->pos.mss_reply) t->pos.mss_orig = mss;
if (scale != SCALE_NONE) t->pos.scale_orig = scale;
}
}
else
{
if (bReverse)
{
t->ack_last = t->pos_reply;
t->pos_reply += len_payload;
t->pos.ack_last = t->pos.pos_reply;
t->pos.pos_reply += len_payload;
}
else
{
t->seq_last = t->pos_orig;
t->pos_orig += len_payload;
t->pos.seq_last = t->pos.pos_orig;
t->pos.pos_orig += len_payload;
}
}
time(&t->t_last);
time(&t->pos.t_last);
}
static bool ConntrackPoolDoubleSearchPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, t_ctrack **ctrack, bool *bReverse)
@@ -317,12 +317,12 @@ void ConntrackPoolPurge(t_conntrack *p)
if ((tnow - p->t_last_purge) >= p->t_purge_interval)
{
HASH_ITER(hh, p->pool, t, tmp) {
tidle = tnow - t->track.t_last;
tidle = tnow - t->track.pos.t_last;
if (t->track.b_cutoff ||
(t->conn.l4proto == IPPROTO_TCP && (
(t->track.state == SYN && tidle >= p->timeout_syn) ||
(t->track.state == ESTABLISHED && tidle >= p->timeout_established) ||
(t->track.state == FIN && tidle >= p->timeout_fin))
(t->track.pos.state == SYN && tidle >= p->timeout_syn) ||
(t->track.pos.state == ESTABLISHED && tidle >= p->timeout_established) ||
(t->track.pos.state == FIN && tidle >= p->timeout_fin))
) || (t->conn.l4proto == IPPROTO_UDP && tidle >= p->timeout_udp)
)
{
@@ -349,21 +349,21 @@ void ConntrackPoolDump(const t_conntrack *p)
printf("%s [%s]:%u => [%s]:%u : %s : t0=%llu last=t0+%llu now=last+%llu orig=d%llu/n%llu/b%llu reply=d%llu/n%llu/b%lld ",
proto_name(t->conn.l4proto),
sa1, t->conn.sport, sa2, t->conn.dport,
t->conn.l4proto == IPPROTO_TCP ? connstate_s[t->track.state] : "-",
(unsigned long long)t->track.t_start, (unsigned long long)(t->track.t_last - t->track.t_start), (unsigned long long)(tnow - t->track.t_last),
(unsigned long long)t->track.pdcounter_orig, (unsigned long long)t->track.pcounter_orig, (unsigned long long)t->track.pbcounter_orig,
(unsigned long long)t->track.pdcounter_reply, (unsigned long long)t->track.pcounter_reply, (unsigned long long)t->track.pbcounter_reply);
t->conn.l4proto == IPPROTO_TCP ? connstate_s[t->track.pos.state] : "-",
(unsigned long long)t->track.pos.t_start, (unsigned long long)(t->track.pos.t_last - t->track.pos.t_start), (unsigned long long)(tnow - t->track.pos.t_last),
(unsigned long long)t->track.pos.pdcounter_orig, (unsigned long long)t->track.pos.pcounter_orig, (unsigned long long)t->track.pos.pbcounter_orig,
(unsigned long long)t->track.pos.pdcounter_reply, (unsigned long long)t->track.pos.pcounter_reply, (unsigned long long)t->track.pos.pbcounter_reply);
if (t->conn.l4proto == IPPROTO_TCP)
printf("seq0=%u rseq=%u pos_orig=%u ack0=%u rack=%u pos_reply=%u mss_orig=%u mss_reply=%u wsize_orig=%u:%d wsize_reply=%u:%d",
t->track.seq0, t->track.seq_last - t->track.seq0, t->track.pos_orig - t->track.seq0,
t->track.ack0, t->track.ack_last - t->track.ack0, t->track.pos_reply - t->track.ack0,
t->track.mss_orig, t->track.mss_reply,
t->track.winsize_orig, t->track.scale_orig == SCALE_NONE ? -1 : t->track.scale_orig,
t->track.winsize_reply, t->track.scale_reply == SCALE_NONE ? -1 : t->track.scale_reply);
t->track.pos.seq0, t->track.pos.seq_last - t->track.pos.seq0, t->track.pos.pos_orig - t->track.pos.seq0,
t->track.pos.ack0, t->track.pos.ack_last - t->track.pos.ack0, t->track.pos.pos_reply - t->track.pos.ack0,
t->track.pos.mss_orig, t->track.pos.mss_reply,
t->track.pos.winsize_orig, t->track.pos.scale_orig == SCALE_NONE ? -1 : t->track.pos.scale_orig,
t->track.pos.winsize_reply, t->track.pos.scale_reply == SCALE_NONE ? -1 : t->track.pos.scale_reply);
else
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);
t->track.pos.seq_last, t->track.pos.pos_orig,
t->track.pos.ack_last, t->track.pos.pos_reply);
printf(" req_retrans=%u cutoff=%u lua_in_cutoff=%u lua_out_cutoff=%u hostname=%s l7proto=%s\n",
t->track.req_retrans_counter, t->track.b_cutoff, t->track.b_lua_in_cutoff, t->track.b_lua_out_cutoff, t->track.hostname, l7proto_str(t->track.l7proto));
};

View File

@@ -8,7 +8,6 @@
#include <stdint.h>
#include <ctype.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
#define __FAVOR_BSD
@@ -17,6 +16,7 @@
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include "conntrack_base.h"
#include "packet_queue.h"
#include "protocol.h"
@@ -48,35 +48,18 @@ typedef struct {
size_t size_present; // how many bytes already stored in 'packet'
} t_reassemble;
// SYN - SYN or SYN/ACK received
// ESTABLISHED - any except SYN or SYN/ACK received
// FIN - FIN or RST received
typedef enum {SYN=0, ESTABLISHED, FIN} t_connstate;
typedef struct
{
bool bCheckDone, bCheckResult, bCheckExcluded; // hostlist check result cache
uint8_t ipproto;
// this block of data can change between delayed (queued) packets. need to remeber this data for each packet for further replay
t_ctrack_position pos;
struct desync_profile *dp; // desync profile cache
bool dp_search_complete;
// common state
time_t t_start, t_last;
uint64_t pcounter_orig, pcounter_reply; // packet counter
uint64_t pdcounter_orig, pdcounter_reply; // data packet counter (with payload)
uint64_t pbcounter_orig, pbcounter_reply; // transferred byte counter. includes retransmissions. it's not the same as relative seq.
uint32_t pos_orig, pos_reply; // TCP: seq_last+payload, ack_last+payload UDP: sum of all seen payload lenghts including current
uint32_t seq_last, ack_last; // TCP: last seen seq and ack UDP: sum of all seen payload lenghts NOT including current
// tcp only state, not used in udp
t_connstate state;
uint32_t seq0, ack0; // starting seq and ack
uint16_t winsize_orig, winsize_reply; // last seen window size
uint8_t scale_orig, scale_reply; // last seen window scale factor. SCALE_NONE if none
uint32_t winsize_orig_calc, winsize_reply_calc; // calculated window size
uint16_t mss_orig, mss_reply;
uint8_t req_retrans_counter; // number of request retransmissions
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)

32
nfq2/conntrack_base.h Normal file
View File

@@ -0,0 +1,32 @@
#pragma once
#include <time.h>
#define CTRACK_T_SYN 60
#define CTRACK_T_FIN 60
#define CTRACK_T_EST 300
#define CTRACK_T_UDP 60
// SYN - SYN or SYN/ACK received
// ESTABLISHED - any except SYN or SYN/ACK received
// FIN - FIN or RST received
typedef enum {SYN=0, ESTABLISHED, FIN} t_connstate;
typedef struct
{
time_t t_last, t_start;
uint64_t pcounter_orig, pcounter_reply; // packet counter
uint64_t pdcounter_orig, pdcounter_reply; // data packet counter (with payload)
uint64_t pbcounter_orig, pbcounter_reply; // transferred byte counter. includes retransmissions. it's not the same as relative seq.
uint32_t pos_orig, pos_reply; // TCP: seq_last+payload, ack_last+payload UDP: sum of all seen payload lenghts including current
uint32_t seq_last, ack_last; // TCP: last seen seq and ack UDP: sum of all seen payload lenghts NOT including current
// tcp only state, not used in udp
t_connstate state;
uint32_t seq0, ack0; // starting seq and ack
uint16_t winsize_orig, winsize_reply; // last seen window size
uint8_t scale_orig, scale_reply; // last seen window scale factor. SCALE_NONE if none
uint32_t winsize_orig_calc, winsize_reply_calc; // calculated window size
uint16_t mss_orig, mss_reply;
} t_ctrack_position;

View File

@@ -209,7 +209,7 @@ static struct desync_profile *dp_find(
{
if (dp_match(&dpl->dp, l3proto, ip, ip6, port, hostname, bNoSubdom, l7proto, ssid, bCheckDone, bCheckResult, bExcluded))
{
DLOG("desync profile %u matches\n", dpl->dp.n);
DLOG("desync profile %u (%s) matches\n", dpl->dp.n, PROFILE_NAME(&dpl->dp));
return &dpl->dp;
}
}
@@ -234,8 +234,8 @@ static void auto_hostlist_reset_fail_counter(struct desync_profile *dp, const ch
if (fail_counter)
{
HostFailPoolDel(&dp->hostlist_auto_fail_counters, fail_counter);
DLOG("auto hostlist (profile %u) : %s : fail counter reset. website is working.\n", dp->n, hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u : client %s : proto %s : fail counter reset. website is working.", hostname, dp->n, client_ip_port, l7proto_str(l7proto));
DLOG("auto hostlist (profile %u (%s)) : %s : fail counter reset. website is working.\n", dp->n, PROFILE_NAME(dp), hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : fail counter reset. website is working.", hostname, dp->n, PROFILE_NAME(dp), client_ip_port, l7proto_str(l7proto));
}
}
}
@@ -249,9 +249,9 @@ static bool auto_hostlist_retrans(t_ctrack *ctrack, uint8_t l4proto, int thresho
{
if (!ctrack->req_seq_finalized || ctrack->req_seq_abandoned)
return false;
if (!seq_within(ctrack->seq_last, ctrack->req_seq_start, ctrack->req_seq_end))
if (!seq_within(ctrack->pos.seq_last, ctrack->req_seq_start, ctrack->req_seq_end))
{
DLOG("req retrans : tcp seq %u not within the req range %u-%u. stop tracking.\n", ctrack->seq_last, ctrack->req_seq_start, ctrack->req_seq_end);
DLOG("req retrans : tcp seq %u not within the req range %u-%u. stop tracking.\n", ctrack->pos.seq_last, ctrack->req_seq_start, ctrack->req_seq_end);
ctrack_stop_retrans_counter(ctrack);
auto_hostlist_reset_fail_counter(ctrack->dp, ctrack->hostname, client_ip_port, l7proto);
return false;
@@ -283,19 +283,19 @@ static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname
}
}
fail_counter->counter++;
DLOG("auto hostlist (profile %u) : %s : fail counter %d/%d\n", dp->n, hostname, fail_counter->counter, dp->hostlist_auto_fail_threshold);
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u : client %s : proto %s : fail counter %d/%d", hostname, dp->n, client_ip_port, l7proto_str(l7proto), fail_counter->counter, dp->hostlist_auto_fail_threshold);
DLOG("auto hostlist (profile %u (%s)) : %s : fail counter %d/%d\n", dp->n, PROFILE_NAME(dp), hostname, fail_counter->counter, dp->hostlist_auto_fail_threshold);
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : fail counter %d/%d", hostname, dp->n, PROFILE_NAME(dp), client_ip_port, l7proto_str(l7proto), fail_counter->counter, dp->hostlist_auto_fail_threshold);
if (fail_counter->counter >= dp->hostlist_auto_fail_threshold)
{
DLOG("auto hostlist (profile %u) : fail threshold reached. about to add %s to auto hostlist\n", dp->n, hostname);
DLOG("auto hostlist (profile %u (%s)) : fail threshold reached. about to add %s to auto hostlist\n", dp->n, PROFILE_NAME(dp), hostname);
HostFailPoolDel(&dp->hostlist_auto_fail_counters, fail_counter);
DLOG("auto hostlist (profile %u) : rechecking %s to avoid duplicates\n", dp->n, hostname);
DLOG("auto hostlist (profile %u (%s)) : rechecking %s to avoid duplicates\n", dp->n, PROFILE_NAME(dp), hostname);
bool bExcluded = false;
if (!HostlistCheck(dp, hostname, bNoSubdom, &bExcluded, false) && !bExcluded)
{
DLOG("auto hostlist (profile %u) : adding %s to %s\n", dp->n, hostname, dp->hostlist_auto->filename);
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u : client %s : proto %s : adding to %s", hostname, dp->n, client_ip_port, l7proto_str(l7proto), dp->hostlist_auto->filename);
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : adding to %s", hostname, dp->n, PROFILE_NAME(dp), client_ip_port, l7proto_str(l7proto), dp->hostlist_auto->filename);
if (!HostlistPoolAddStr(&dp->hostlist_auto->hostlist, hostname, 0))
{
DLOG_ERR("StrPoolAddStr out of memory\n");
@@ -312,7 +312,7 @@ static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname
else
{
DLOG("auto hostlist (profile %u) : NOT adding %s\n", dp->n, hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u : client %s : proto %s : NOT adding, duplicate detected", hostname, dp->n, client_ip_port, l7proto_str(l7proto));
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : NOT adding, duplicate detected", hostname, dp->n, PROFILE_NAME(dp), client_ip_port, l7proto_str(l7proto));
}
}
}
@@ -328,7 +328,7 @@ static void process_retrans_fail(t_ctrack *ctrack, uint8_t proto, const struct s
*client_ip_port = 0;
if (ctrack && ctrack->dp && ctrack->hostname && auto_hostlist_retrans(ctrack, proto, ctrack->dp->hostlist_auto_retrans_threshold, client_ip_port, ctrack->l7proto))
{
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u : client %s : proto %s : retrans threshold reached", ctrack->hostname, ctrack->dp->n, client_ip_port, l7proto_str(ctrack->l7proto));
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : retrans threshold reached", ctrack->hostname, ctrack->dp->n, PROFILE_NAME(ctrack->dp), client_ip_port, l7proto_str(ctrack->l7proto));
auto_hostlist_failed(ctrack->dp, ctrack->hostname, ctrack->hostname_is_ip, client_ip_port, ctrack->l7proto);
}
}
@@ -344,14 +344,14 @@ static bool send_delayed(t_ctrack *ctrack)
return true;
}
static bool rawpacket_queue_csum_fix(struct rawpacket_tailhead *q, const struct dissect *dis, const struct sockaddr_storage* dst, uint32_t fwmark, uint32_t desync_fwmark, const char *ifin, const char *ifout)
static bool rawpacket_queue_csum_fix(struct rawpacket_tailhead *q, const struct dissect *dis, const t_ctrack_position *pos, const struct sockaddr_storage* dst, uint32_t fwmark, uint32_t desync_fwmark, const char *ifin, const char *ifout)
{
// this breaks const pointer to l4 header
if (dis->tcp)
verdict_tcp_csum_fix(VERDICT_PASS, (struct tcphdr *)dis->tcp, dis->transport_len, dis->ip, dis->ip6);
else if (dis->udp)
verdict_udp_csum_fix(VERDICT_PASS, (struct udphdr *)dis->udp, dis->transport_len, dis->ip, dis->ip6);
return rawpacket_queue(q, dst, fwmark, desync_fwmark, ifin, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload);
return rawpacket_queue(q, dst, fwmark, desync_fwmark, ifin, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload, pos);
}
@@ -360,7 +360,7 @@ static bool reasm_start(t_ctrack *ctrack, t_reassemble *reasm, uint8_t proto, si
ReasmClear(reasm);
if (sz <= szMax)
{
uint32_t seq = (proto == IPPROTO_TCP) ? ctrack->seq_last : 0;
uint32_t seq = (proto == IPPROTO_TCP) ? ctrack->pos.seq_last : 0;
if (ReasmInit(reasm, sz, seq))
{
ReasmFeed(reasm, seq, data_payload, len_payload);
@@ -382,7 +382,7 @@ static bool reasm_feed(t_ctrack *ctrack, t_reassemble *reasm, uint8_t proto, con
{
if (ctrack && !ReasmIsEmpty(reasm))
{
uint32_t seq = (proto == IPPROTO_TCP) ? ctrack->seq_last : (uint32_t)reasm->size_present;
uint32_t seq = (proto == IPPROTO_TCP) ? ctrack->pos.seq_last : (uint32_t)reasm->size_present;
if (ReasmFeed(reasm, seq, data_payload, len_payload))
{
DLOG("reassemble : feeding data payload size=%zu. now we have %zu/%zu\n", len_payload, reasm->size_present, reasm->size);
@@ -429,7 +429,7 @@ static uint8_t ct_new_postnat_fix(const t_ctrack *ctrack, const struct dissect *
// if used in postnat chain, dropping initial packet will cause conntrack connection teardown
// so we need to workaround this.
// SYN and SYN,ACK checks are for conntrack-less mode
if (ctrack && (params.server ? ctrack->pcounter_reply : ctrack->pcounter_orig) == 1 || dis->tcp && (tcp_syn_segment(dis->tcp) || tcp_synack_segment(dis->tcp)))
if (ctrack && (params.server ? ctrack->pos.pcounter_reply : ctrack->pos.pcounter_orig) == 1 || dis->tcp && (tcp_syn_segment(dis->tcp) || tcp_synack_segment(dis->tcp)))
{
if (dis->len_pkt > *len_mod_pkt)
DLOG_ERR("linux postnat conntrack workaround cannot be applied\n");
@@ -458,60 +458,60 @@ static uint8_t ct_new_postnat_fix(const t_ctrack *ctrack, const struct dissect *
}
static uint64_t pos_get(const t_ctrack *ctrack, char mode, bool bReply)
static uint64_t pos_get(const t_ctrack_position *pos, char mode, bool bReply)
{
if (ctrack)
if (pos)
{
switch (mode)
{
case 'n': return bReply ? ctrack->pcounter_reply : ctrack->pcounter_orig;
case 'd': return bReply ? ctrack->pdcounter_reply : ctrack->pdcounter_orig;
case 's': return bReply ? (ctrack->ack_last - ctrack->ack0) : (ctrack->seq_last - ctrack->seq0);
case 'b': return bReply ? ctrack->pbcounter_reply : ctrack->pbcounter_orig;
case 'n': return bReply ? pos->pcounter_reply : pos->pcounter_orig;
case 'd': return bReply ? pos->pdcounter_reply : pos->pdcounter_orig;
case 's': return bReply ? (pos->ack_last - pos->ack0) : (pos->seq_last - pos->seq0);
case 'b': return bReply ? pos->pbcounter_reply : pos->pbcounter_orig;
}
}
return 0;
}
static bool check_pos_from(const t_ctrack *ctrack, bool bReply, const struct packet_range *range)
static bool check_pos_from(const t_ctrack_position *pos, bool bReply, const struct packet_range *range)
{
uint64_t pos;
uint64_t ps;
if (range->from.mode == 'x') return false;
if (range->from.mode != 'a')
{
if (ctrack)
if (pos)
{
pos = pos_get(ctrack, range->from.mode, bReply);
return pos >= range->from.pos;
ps = pos_get(pos, range->from.mode, bReply);
return ps >= range->from.pos;
}
else
return false;
}
return true;
}
static bool check_pos_to(const t_ctrack *ctrack, bool bReply, const struct packet_range *range)
static bool check_pos_to(const t_ctrack_position *pos, bool bReply, const struct packet_range *range)
{
uint64_t pos;
uint64_t ps;
if (range->to.mode == 'x') return false;
if (range->to.mode != 'a')
{
if (ctrack)
if (pos)
{
pos = pos_get(ctrack, range->to.mode, bReply);
return (pos < range->to.pos) || !range->upper_cutoff && (pos == range->to.pos);
ps = pos_get(pos, range->to.mode, bReply);
return (ps < range->to.pos) || !range->upper_cutoff && (ps == range->to.pos);
}
else
return false;
}
return true;
}
static bool check_pos_cutoff(const t_ctrack *ctrack, bool bReply, const struct packet_range *range)
static bool check_pos_cutoff(const t_ctrack_position *pos, bool bReply, const struct packet_range *range)
{
bool bto = check_pos_to(ctrack, bReply, range);
return ctrack ? !bto : (!bto || !check_pos_from(ctrack, bReply, range));
bool bto = check_pos_to(pos, bReply, range);
return pos ? !bto : (!bto || !check_pos_from(pos, bReply, range));
}
static bool check_pos_range(const t_ctrack *ctrack, bool bReply, const struct packet_range *range)
static bool check_pos_range(const t_ctrack_position *pos, bool bReply, const struct packet_range *range)
{
return check_pos_from(ctrack, bReply, range) && check_pos_to(ctrack, bReply, range);
return check_pos_from(pos, bReply, range) && check_pos_to(pos, bReply, range);
}
@@ -638,10 +638,6 @@ err:
lua_pop(params.L, rescount);
return false;
}
static void desync_instance(const char *func, unsigned int dp_n, unsigned int func_n, char *instance, size_t inst_size)
{
snprintf(instance, inst_size, "%s_%u_%u", func, dp_n, func_n);
}
static uint8_t desync(
struct desync_profile *dp,
uint32_t fwmark,
@@ -649,6 +645,7 @@ static uint8_t desync(
const char *ifout,
bool bIncoming,
t_ctrack *ctrack,
const t_ctrack_position *pos,
t_l7payload l7payload,
const struct dissect *dis,
uint8_t *mod_pkt, size_t *len_mod_pkt,
@@ -659,7 +656,7 @@ static uint8_t desync(
struct func_list *func;
int ref_arg = LUA_NOREF, status;
bool b, b_cutoff_all, b_unwanted_payload;
t_lua_desync_context ctx = { .dp = dp, .ctrack = ctrack, .dis = dis };
t_lua_desync_context ctx = { .dp = dp, .ctrack = ctrack, .dis = dis, .cancel = false, .incoming = bIncoming };
const char *sDirection = bIncoming ? "in" : "out";
struct packet_range *range;
size_t l;
@@ -678,17 +675,16 @@ static uint8_t desync(
DLOG("lua out cutoff\n");
return verdict;
}
if (!pos) pos = &ctrack->pos;
}
if (LIST_FIRST(&dp->lua_desync))
{
unsigned int func_n;
b_cutoff_all = b_unwanted_payload = true;
func_n = 1;
ctx.func_n = 1;
LIST_FOREACH(func, &dp->lua_desync, next)
{
ctx.func = func->func;
desync_instance(func->func, dp->n, func_n, instance, sizeof(instance));
desync_instance(func->func, dp->n, ctx.func_n, instance, sizeof(instance));
ctx.instance = instance;
range = bIncoming ? &func->range_in : &func->range_out;
@@ -699,12 +695,12 @@ static uint8_t desync(
{
if (lua_instance_cutoff_check(&ctx, bIncoming))
DLOG("* lua '%s' : voluntary cutoff\n", instance);
else if (check_pos_cutoff(ctrack, bIncoming, range))
else if (check_pos_cutoff(pos, bIncoming, range))
{
DLOG("* lua '%s' : %s pos %c%llu %c%llu is beyond range %c%u%c%c%u (ctrack %s)\n",
instance, sDirection,
range->from.mode, pos_get(ctrack, range->from.mode, bIncoming),
range->to.mode, pos_get(ctrack, range->to.mode, bIncoming),
range->from.mode, pos_get(pos, range->from.mode, bIncoming),
range->to.mode, pos_get(pos, range->to.mode, bIncoming),
range->from.mode, range->from.pos,
range->upper_cutoff ? '<' : '-',
range->to.mode, range->to.pos,
@@ -713,7 +709,7 @@ static uint8_t desync(
else
b_cutoff_all = false;
}
func_n++;
ctx.func_n++;
}
if (b_cutoff_all)
{
@@ -725,10 +721,14 @@ static uint8_t desync(
else
{
// create arg table that persists across multiple desync function calls
lua_createtable(params.L, 0, 12 + !!ctrack + !!dis->tcp + 3*!!replay_piece_count);
lua_newtable(params.L);
lua_pushf_dissect(dis);
lua_pushf_ctrack(ctrack);
lua_pushf_ctrack(ctrack, pos);
lua_pushf_int("profile_n", dp->n);
if (dp->name) lua_pushf_str("profile_name", dp->name);
if (dp->n_tpl) lua_pushf_int("template_n", dp->n_tpl);
if (dp->name_tpl) lua_pushf_str("template_name", dp->name_tpl);
if (dp->cookie) lua_pushf_str("cookie", dp->cookie);
lua_pushf_bool("outgoing", !bIncoming);
lua_pushf_str("ifin", (ifin && *ifin) ? ifin : NULL);
lua_pushf_str("ifout", (ifout && *ifout) ? ifout : NULL);
@@ -748,29 +748,29 @@ static uint8_t desync(
if (dis->tcp)
{
// recommended mss value for generated packets
if (ctrack && ctrack->mss_orig)
lua_pushf_int("tcp_mss", ctrack->mss_orig);
if (pos && pos->mss_orig)
lua_pushf_int("tcp_mss", pos->mss_orig);
else
lua_pushf_global("tcp_mss", "DEFAULT_MSS");
}
ref_arg = luaL_ref(params.L, LUA_REGISTRYINDEX);
func_n = 1;
ctx.func_n = 1;
LIST_FOREACH(func, &dp->lua_desync, next)
{
ctx.func = func->func;
desync_instance(func->func, dp->n, func_n, instance, sizeof(instance));
desync_instance(func->func, dp->n, ctx.func_n, instance, sizeof(instance));
ctx.instance = instance;
if (!lua_instance_cutoff_check(&ctx, bIncoming))
{
range = bIncoming ? &func->range_in : &func->range_out;
if (check_pos_range(ctrack, bIncoming, range))
if (check_pos_range(pos, bIncoming, range))
{
DLOG("* lua '%s' : %s pos %c%llu %c%llu in range %c%u%c%c%u\n",
instance, sDirection,
range->from.mode, pos_get(ctrack, range->from.mode, bIncoming),
range->to.mode, pos_get(ctrack, range->to.mode, bIncoming),
range->from.mode, pos_get(pos, range->from.mode, bIncoming),
range->to.mode, pos_get(pos, range->to.mode, bIncoming),
range->from.mode, range->from.pos,
range->upper_cutoff ? '<' : '-',
range->to.mode, range->to.pos);
@@ -789,7 +789,7 @@ static uint8_t desync(
lua_rawgeti(params.L, LUA_REGISTRYINDEX, ref_arg);
lua_pushf_args(&func->args, -1);
lua_pushf_str("func", func->func);
lua_pushf_int("func_n", func_n);
lua_pushf_int("func_n", ctx.func_n);
lua_pushf_str("func_instance", instance);
int initial_stack_top = lua_gettop(params.L);
status = lua_pcall(params.L, 2, LUA_MULTRET, 0);
@@ -808,37 +808,6 @@ static uint8_t desync(
case VERDICT_DROP:
verdict = VERDICT_DROP;
}
if (ctrack)
{
// lua cutoff
lua_rawgeti(params.L, LUA_REGISTRYINDEX, ref_arg);
lua_getfield(params.L, -1, "track");
if (lua_istable(params.L, -1))
{
if (!ctrack->b_lua_in_cutoff)
{
lua_getfield(params.L, -1, "lua_in_cutoff");
if (lua_toboolean(params.L, -1))
{
ctrack->b_lua_in_cutoff = true;
DLOG("* lua in cutoff set\n");
}
lua_pop(params.L, 1);
}
if (!ctrack->b_lua_out_cutoff)
{
lua_getfield(params.L, -1, "lua_out_cutoff");
if (lua_toboolean(params.L, -1))
{
ctrack->b_lua_out_cutoff = true;
DLOG("* lua out cutoff set\n");
}
lua_pop(params.L, 1);
}
}
lua_pop(params.L, 2);
}
}
else
DLOG("* lua '%s' : payload_type '%s' does not satisfy filter\n", instance, l7payload_str(l7payload));
@@ -846,13 +815,14 @@ static uint8_t desync(
else
DLOG("* lua '%s' : %s pos %c%llu %c%llu out of range %c%u%c%c%u\n",
instance, sDirection,
range->from.mode, pos_get(ctrack, range->from.mode, bIncoming),
range->to.mode, pos_get(ctrack, range->to.mode, bIncoming),
range->from.mode, pos_get(pos, range->from.mode, bIncoming),
range->to.mode, pos_get(pos, range->to.mode, bIncoming),
range->from.mode, range->from.pos,
range->upper_cutoff ? '<' : '-',
range->to.mode, range->to.pos);
}
func_n++;
if (ctx.cancel) break;
ctx.func_n++;
}
}
@@ -940,7 +910,13 @@ static void setup_direction(
}
}
static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset, uint32_t fwmark, const char *ifin, const char *ifout, const struct dissect *dis, uint8_t *mod_pkt, size_t *len_mod_pkt)
static uint8_t dpi_desync_tcp_packet_play(
unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset,
uint32_t fwmark,
const char *ifin, const char *ifout,
const t_ctrack_position *pos,
const struct dissect *dis,
uint8_t *mod_pkt, size_t *len_mod_pkt)
{
uint8_t verdict = VERDICT_PASS;
@@ -979,7 +955,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
l7proto = ctrack_replay->l7proto;
dp = ctrack_replay->dp;
if (dp)
DLOG("using cached desync profile %u\n", dp->n);
DLOG("using cached desync profile %u (%s)\n", dp->n, PROFILE_NAME(dp));
else if (!ctrack_replay->dp_search_complete)
{
dp = ctrack_replay->dp = dp_find(&params.desync_profiles, IPPROTO_TCP, sdip4, sdip6, sdport, ctrack_replay->hostname, ctrack_replay->hostname_is_ip, l7proto, ssid, NULL, NULL, NULL);
@@ -1007,7 +983,6 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
// in absence of conntrack guess direction by presence of interface names. won't work on BSD
bReverseFixed = ctrack ? (bReverse ^ params.server) : (bReverse = ifin && ifin && (!ifout || !*ifout));
setup_direction(dis, bReverseFixed, &src, &dst, &sdip4, &sdip6, &sdport);
ifname = bReverse ? ifin : ifout;
#ifdef HAS_FILTER_SSID
ssid = wlan_ssid_search_ifname(ifname);
@@ -1015,7 +990,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
#endif
if (ctrack) l7proto = ctrack->l7proto;
if (dp)
DLOG("using cached desync profile %u\n", dp->n);
DLOG("using cached desync profile %u (%s)\n", dp->n, PROFILE_NAME(dp));
else if (!ctrack || !ctrack->dp_search_complete)
{
const char *hostname = NULL;
@@ -1027,7 +1002,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
if (!hostname && !bReverse)
{
if (ipcache_get_hostname(sdip4, sdip6, host, sizeof(host), &hostname_is_ip) && *host)
if (!(hostname = ctrack_replay->hostname = strdup(host)))
if (!(hostname = ctrack->hostname = strdup(host)))
DLOG_ERR("strdup(host): out of memory\n");
}
}
@@ -1096,7 +1071,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
// process reply packets for auto hostlist mode
// by looking at RSTs or HTTP replies we decide whether original request looks like DPI blocked
// we only process first-sequence replies. do not react to subsequent redirects or RSTs
if (!params.server && ctrack && ctrack->hostname && ctrack->hostname_ah_check && (ctrack->ack_last - ctrack->ack0) == 1)
if (!params.server && ctrack && ctrack->hostname && ctrack->hostname_ah_check && (ctrack->pos.ack_last - ctrack->pos.ack0) == 1)
{
bool bFail = false;
@@ -1109,7 +1084,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
if (dis->tcp->th_flags & TH_RST)
{
DLOG("incoming RST detected for hostname %s\n", ctrack->hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u : client %s : proto %s : incoming RST", ctrack->hostname, ctrack->dp->n, client_ip_port, l7proto_str(l7proto));
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : incoming RST", ctrack->hostname, ctrack->dp->n, PROFILE_NAME(dp), client_ip_port, l7proto_str(l7proto));
bFail = true;
}
else if (dis->len_payload && l7proto == L7_HTTP)
@@ -1121,7 +1096,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
if (bFail)
{
DLOG("redirect to another domain detected. possibly DPI redirect.\n");
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u : client %s : proto %s : redirect to another domain", ctrack->hostname, ctrack->dp->n, client_ip_port, l7proto_str(l7proto));
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : redirect to another domain", ctrack->hostname, ctrack->dp->n, PROFILE_NAME(dp), client_ip_port, l7proto_str(l7proto));
}
else
DLOG("local or in-domain redirect detected. it's not a DPI redirect.\n");
@@ -1186,8 +1161,8 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
// we do not reassemble http
if (!ctrack->req_seq_present)
{
ctrack->req_seq_start = ctrack->seq_last;
ctrack->req_seq_end = ctrack->pos_orig - 1;
ctrack->req_seq_start = ctrack->pos.seq_last;
ctrack->req_seq_end = ctrack->pos.pos_orig - 1;
ctrack->req_seq_present = ctrack->req_seq_finalized = true;
DLOG("req retrans : tcp seq interval %u-%u\n", ctrack->req_seq_start, ctrack->req_seq_end);
}
@@ -1211,7 +1186,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
{
// do not reasm retransmissions
if (!bReqFull && ReasmIsEmpty(&ctrack->reasm_orig) && !ctrack->req_seq_abandoned &&
!(ctrack->req_seq_finalized && seq_within(ctrack->seq_last, ctrack->req_seq_start, ctrack->req_seq_end)))
!(ctrack->req_seq_finalized && seq_within(ctrack->pos.seq_last, ctrack->req_seq_start, ctrack->req_seq_end)))
{
// do not reconstruct unexpected large payload (they are feeding garbage ?)
if (!reasm_orig_start(ctrack, IPPROTO_TCP, TLSRecordLen(dis->data_payload), TCP_MAX_REASM, dis->data_payload, dis->len_payload))
@@ -1222,19 +1197,19 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
if (!ctrack->req_seq_present)
{
// lower bound of request seq interval
ctrack->req_seq_start = ctrack->seq_last;
ctrack->req_seq_start = ctrack->pos.seq_last;
ctrack->req_seq_present = true;
}
// upper bound of request seq interval
// it can grow on every packet until request is complete. then interval is finalized and never touched again.
ctrack->req_seq_end = ctrack->pos_orig - 1;
ctrack->req_seq_end = ctrack->pos.pos_orig - 1;
DLOG("req retrans : seq interval %u-%u\n", ctrack->req_seq_start, ctrack->req_seq_end);
ctrack->req_seq_finalized |= bReqFull;
}
if (!ReasmIsEmpty(&ctrack->reasm_orig))
{
if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &dst, fwmark, desync_fwmark, ifin, ifout))
if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &ctrack->pos, &dst, fwmark, desync_fwmark, ifin, ifout))
{
DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed));
}
@@ -1252,7 +1227,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
}
}
}
else if (ctrack && (ctrack->seq_last - ctrack->seq0)==1 && IsMTProto(dis->data_payload, dis->len_payload))
else if (ctrack && (ctrack->pos.seq_last - ctrack->pos.seq0)==1 && IsMTProto(dis->data_payload, dis->len_payload))
{
DLOG("packet contains telegram mtproto2 initial\n");
// mtproto detection requires aes. react only on the first tcp data packet. do not detect if ctrack unavailable.
@@ -1273,7 +1248,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
}
if (ctrack && ctrack->req_seq_finalized)
{
uint32_t dseq = ctrack->seq_last - ctrack->req_seq_end;
uint32_t dseq = ctrack->pos.seq_last - ctrack->req_seq_end;
// do not react to 32-bit overflowed sequence numbers. allow 16 Mb grace window then cutoff.
if (dseq >= 0x1000000 && !(dseq & 0x80000000)) ctrack->req_seq_abandoned = true;
}
@@ -1379,7 +1354,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
ntop46_port((struct sockaddr *)&dst, s2, sizeof(s2));
DLOG("dpi desync src=%s dst=%s track_direction=%s fixed_direction=%s connection_proto=%s payload_type=%s\n", s1, s2, bReverse ? "in" : "out", bReverseFixed ? "in" : "out", l7proto_str(l7proto), l7payload_str(l7payload));
}
verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, l7payload, dis, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, rdata_payload, rlen_payload, NULL, 0);
verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, pos, l7payload, dis, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, rdata_payload, rlen_payload, NULL, 0);
pass:
return (!bReverseFixed && (verdict & VERDICT_MASK) == VERDICT_DROP) ? ct_new_postnat_fix(ctrack, dis, mod_pkt, len_mod_pkt) : verdict;
@@ -1396,7 +1371,13 @@ static void quic_reasm_cancel(t_ctrack *ctrack, const char *reason)
}
static uint8_t dpi_desync_udp_packet_play(unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset, uint32_t fwmark, const char *ifin, const char *ifout, const struct dissect *dis, uint8_t *mod_pkt, size_t *len_mod_pkt)
static uint8_t dpi_desync_udp_packet_play(
unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset,
uint32_t fwmark,
const char *ifin, const char *ifout,
const t_ctrack_position *pos,
const struct dissect *dis,
uint8_t *mod_pkt, size_t *len_mod_pkt)
{
uint8_t verdict = VERDICT_PASS;
@@ -1452,7 +1433,7 @@ static uint8_t dpi_desync_udp_packet_play(unsigned int replay_piece, unsigned in
l7proto = ctrack_replay->l7proto;
dp = ctrack_replay->dp;
if (dp)
DLOG("using cached desync profile %u\n", dp->n);
DLOG("using cached desync profile %u (%s)\n", dp->n, PROFILE_NAME(dp));
else if (!ctrack_replay->dp_search_complete)
{
dp = ctrack_replay->dp = dp_find(&params.desync_profiles, IPPROTO_UDP, sdip4, sdip6, sdport, ctrack_replay->hostname, ctrack_replay->hostname_is_ip, l7proto, ssid, NULL, NULL, NULL);
@@ -1488,7 +1469,7 @@ static uint8_t dpi_desync_udp_packet_play(unsigned int replay_piece, unsigned in
#endif
if (ctrack) l7proto = ctrack->l7proto;
if (dp)
DLOG("using cached desync profile %u\n", dp->n);
DLOG("using cached desync profile %u (%s)\n", dp->n, PROFILE_NAME(dp));
else if (!ctrack || !ctrack->dp_search_complete)
{
const char *hostname = NULL;
@@ -1500,7 +1481,7 @@ static uint8_t dpi_desync_udp_packet_play(unsigned int replay_piece, unsigned in
if (!hostname && !bReverse)
{
if (ipcache_get_hostname(sdip4, sdip6, host, sizeof(host), &hostname_is_ip) && *host)
if (!(hostname = ctrack_replay->hostname = strdup(host)))
if (!(hostname = ctrack->hostname = strdup(host)))
DLOG_ERR("strdup(host): out of memory\n");
}
}
@@ -1553,6 +1534,7 @@ static uint8_t dpi_desync_udp_packet_play(unsigned int replay_piece, unsigned in
t_protocol_probe testers[] = {
{L7P_DNS_RESPONSE,L7_DNS,IsDNSResponse,false},
{L7P_DHT,L7_DHT,IsDht,false},
{L7P_STUN,L7_STUN,IsStunMessage,false},
{L7P_WIREGUARD_INITIATION,L7_WIREGUARD,IsWireguardHandshakeInitiation,false},
{L7P_WIREGUARD_RESPONSE,L7_WIREGUARD,IsWireguardHandshakeResponse,false},
{L7P_WIREGUARD_COOKIE,L7_WIREGUARD,IsWireguardHandshakeCookie,false},
@@ -1629,7 +1611,7 @@ static uint8_t dpi_desync_udp_packet_play(unsigned int replay_piece, unsigned in
}
if (!ReasmIsEmpty(&ctrack->reasm_orig))
{
if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &dst, fwmark, desync_fwmark, ifin, ifout))
if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &ctrack->pos, &dst, fwmark, desync_fwmark, ifin, ifout))
{
DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed));
}
@@ -1669,7 +1651,7 @@ static uint8_t dpi_desync_udp_packet_play(unsigned int replay_piece, unsigned in
if (!reasm_orig_start(ctrack, IPPROTO_UDP, UDP_MAX_REASM, UDP_MAX_REASM, clean, clean_len))
goto pass_reasm_cancel;
}
if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &dst, fwmark, desync_fwmark, ifin, ifout))
if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &ctrack->pos, &dst, fwmark, desync_fwmark, ifin, ifout))
{
DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed));
}
@@ -1704,7 +1686,7 @@ static uint8_t dpi_desync_udp_packet_play(unsigned int replay_piece, unsigned in
t_protocol_probe testers[] = {
{L7P_DISCORD_IP_DISCOVERY,L7_DISCORD,IsDiscordIpDiscoveryRequest,false},
{L7P_STUN_BINDING_REQ,L7_STUN,IsStunBindingRequest,false},
{L7P_STUN,L7_STUN,IsStunMessage,false},
{L7P_DNS_QUERY,L7_DNS,IsDNSQuery,false},
{L7P_DHT,L7_DHT,IsDht,false},
{L7P_WIREGUARD_INITIATION,L7_WIREGUARD,IsWireguardHandshakeInitiation,false},
@@ -1828,7 +1810,7 @@ static uint8_t dpi_desync_udp_packet_play(unsigned int replay_piece, unsigned in
ntop46_port((struct sockaddr *)&dst, s2, sizeof(s2));
DLOG("dpi desync src=%s dst=%s track_direction=%s fixed_direction=%s connection_proto=%s payload_type=%s\n", s1, s2, bReverse ? "in" : "out", bReverseFixed ? "in" : "out", l7proto_str(l7proto), l7payload_str(l7payload));
}
verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, l7payload, dis, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, NULL, 0, data_decrypt, len_decrypt);
verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, pos, l7payload, dis, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, NULL, 0, data_decrypt, len_decrypt);
pass:
return (!bReverse && (verdict & VERDICT_MASK) == VERDICT_DROP) ? ct_new_postnat_fix(ctrack, dis, mod_pkt, len_mod_pkt) : verdict;
@@ -1877,6 +1859,7 @@ static void packet_debug(bool replay, const struct dissect *dis)
static uint8_t dpi_desync_packet_play(
unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset, uint32_t fwmark, const char *ifin, const char *ifout,
const t_ctrack_position *pos,
const uint8_t *data_pkt, size_t len_pkt,
uint8_t *mod_pkt, size_t *len_mod_pkt)
{
@@ -1892,7 +1875,7 @@ static uint8_t dpi_desync_packet_play(
case IPPROTO_TCP:
if (dis.tcp)
{
verdict = dpi_desync_tcp_packet_play(replay_piece, replay_piece_count, reasm_offset, fwmark, ifin, ifout, &dis, mod_pkt, len_mod_pkt);
verdict = dpi_desync_tcp_packet_play(replay_piece, replay_piece_count, reasm_offset, fwmark, ifin, ifout, pos, &dis, mod_pkt, len_mod_pkt);
// we fix csum before pushing to replay queue
if (!replay_piece_count) verdict_tcp_csum_fix(verdict, (struct tcphdr *)dis.tcp, dis.transport_len, dis.ip, dis.ip6);
}
@@ -1900,7 +1883,7 @@ static uint8_t dpi_desync_packet_play(
case IPPROTO_UDP:
if (dis.udp)
{
verdict = dpi_desync_udp_packet_play(replay_piece, replay_piece_count, reasm_offset, fwmark, ifin, ifout, &dis, mod_pkt, len_mod_pkt);
verdict = dpi_desync_udp_packet_play(replay_piece, replay_piece_count, reasm_offset, fwmark, ifin, ifout, pos, &dis, mod_pkt, len_mod_pkt);
// we fix csum before pushing to replay queue
if (!replay_piece_count) verdict_udp_csum_fix(verdict, (struct udphdr *)dis.udp, dis.transport_len, dis.ip, dis.ip6);
}
@@ -1912,7 +1895,7 @@ static uint8_t dpi_desync_packet_play(
uint8_t dpi_desync_packet(uint32_t fwmark, const char *ifin, const char *ifout, const uint8_t *data_pkt, size_t len_pkt, uint8_t *mod_pkt, size_t *len_mod_pkt)
{
ipcachePurgeRateLimited(&params.ipcache, params.ipcache_lifetime);
return dpi_desync_packet_play(0, 0, 0, fwmark, ifin, ifout, data_pkt, len_pkt, mod_pkt, len_mod_pkt);
return dpi_desync_packet_play(0, 0, 0, fwmark, ifin, ifout, NULL, data_pkt, len_pkt, mod_pkt, len_mod_pkt);
}
@@ -1930,7 +1913,7 @@ static bool replay_queue(struct rawpacket_tailhead *q)
{
DLOG("REPLAYING delayed packet #%u offset %zu\n", i+1, offset);
modlen = sizeof(mod);
uint8_t verdict = dpi_desync_packet_play(i, count, offset, rp->fwmark_orig, rp->ifin, rp->ifout, rp->packet, rp->len, mod, &modlen);
uint8_t verdict = dpi_desync_packet_play(i, count, offset, rp->fwmark_orig, rp->ifin, rp->ifout, rp->pos_present ? &rp->pos : NULL, rp->packet, rp->len, mod, &modlen);
switch (verdict & VERDICT_MASK)
{
case VERDICT_MODIFY:

View File

@@ -258,7 +258,7 @@ static bool HostlistCheck_(const struct hostlist_collection_head *hostlists, con
// return : true = apply fooling, false = do not apply
bool HostlistCheck(const struct desync_profile *dp, const char *host, bool no_match_subdomains, bool *excluded, bool bSkipReloadCheck)
{
DLOG("* hostlist check for profile %u\n",dp->n);
DLOG("* hostlist check for profile %u (%s)\n",dp->n,PROFILE_NAME(dp));
return HostlistCheck_(&dp->hl_collection, &dp->hl_collection_exclude, host, no_match_subdomains, excluded, bSkipReloadCheck);
}
@@ -301,13 +301,34 @@ struct hostlist_file *RegisterHostlist(struct desync_profile *dp, bool bExclude,
filename);
}
static void HostlistsDebugProfile(const struct desync_profile *dp, const char *entity)
{
struct hostlist_item *hl_item;
LIST_FOREACH(hl_item, &dp->hl_collection, next)
if (hl_item->hfile!=dp->hostlist_auto)
{
if (hl_item->hfile->filename)
DLOG("%s %u (%s) include hostlist %s%s\n",entity, dp->n, PROFILE_NAME(dp), hl_item->hfile->filename,hl_item->hfile->hostlist ? "" : " (empty)");
else
DLOG("%s %u (%s) include fixed hostlist%s\n",entity, dp->n, PROFILE_NAME(dp), hl_item->hfile->hostlist ? "" : " (empty)");
}
LIST_FOREACH(hl_item, &dp->hl_collection_exclude, next)
{
if (hl_item->hfile->filename)
DLOG("%s %u (%s) exclude hostlist %s%s\n",entity, dp->n,PROFILE_NAME(dp),hl_item->hfile->filename,hl_item->hfile->hostlist ? "" : " (empty)");
else
DLOG("%s %u (%s) exclude fixed hostlist%s\n",entity, dp->n,PROFILE_NAME(dp),hl_item->hfile->hostlist ? "" : " (empty)");
}
if (dp->hostlist_auto)
DLOG("%s %u (%s) auto hostlist %s%s\n",entity, dp->n,PROFILE_NAME(dp),dp->hostlist_auto->filename,dp->hostlist_auto->hostlist ? "" : " (empty)");
}
void HostlistsDebug()
{
if (!params.debug) return;
struct hostlist_file *hfile;
struct desync_profile_list *dpl;
struct hostlist_item *hl_item;
LIST_FOREACH(hfile, &params.hostlists, next)
{
@@ -319,22 +340,10 @@ void HostlistsDebug()
LIST_FOREACH(dpl, &params.desync_profiles, next)
{
LIST_FOREACH(hl_item, &dpl->dp.hl_collection, next)
if (hl_item->hfile!=dpl->dp.hostlist_auto)
{
if (hl_item->hfile->filename)
DLOG("profile %u include hostlist %s%s\n",dpl->dp.n, hl_item->hfile->filename,hl_item->hfile->hostlist ? "" : " (empty)");
else
DLOG("profile %u include fixed hostlist%s\n",dpl->dp.n, hl_item->hfile->hostlist ? "" : " (empty)");
}
LIST_FOREACH(hl_item, &dpl->dp.hl_collection_exclude, next)
{
if (hl_item->hfile->filename)
DLOG("profile %u exclude hostlist %s%s\n",dpl->dp.n,hl_item->hfile->filename,hl_item->hfile->hostlist ? "" : " (empty)");
else
DLOG("profile %u exclude fixed hostlist%s\n",dpl->dp.n,hl_item->hfile->hostlist ? "" : " (empty)");
}
if (dpl->dp.hostlist_auto)
DLOG("profile %u auto hostlist %s%s\n",dpl->dp.n,dpl->dp.hostlist_auto->filename,dpl->dp.hostlist_auto->hostlist ? "" : " (empty)");
HostlistsDebugProfile(&dpl->dp, "profile");
}
LIST_FOREACH(dpl, &params.desync_templates, next)
{
HostlistsDebugProfile(&dpl->dp, "template");
}
}

View File

@@ -235,7 +235,7 @@ static bool IpsetCheck_(const struct ipset_collection_head *ips, const struct ip
bool IpsetCheck(const struct desync_profile *dp, const struct in_addr *ipv4, const struct in6_addr *ipv6)
{
if (PROFILE_IPSETS_ABSENT(dp)) return true;
DLOG("* ipset check for profile %u\n",dp->n);
DLOG("* ipset check for profile %u (%s)\n",dp->n,PROFILE_NAME(dp));
return IpsetCheck_(&dp->ips_collection,&dp->ips_collection_exclude,ipv4,ipv6);
}
@@ -287,13 +287,31 @@ static const char *dbg_ipset_fill(const ipset *ips)
else
return "empty";
}
void IpsetsDebugProfile(const struct desync_profile *dp, const char *entity)
{
struct ipset_item *ips_item;
LIST_FOREACH(ips_item, &dp->ips_collection, next)
{
if (ips_item->hfile->filename)
DLOG("%s %u (%s) include ipset %s (%s)\n",entity,dp->n,PROFILE_NAME(dp),ips_item->hfile->filename,dbg_ipset_fill(&ips_item->hfile->ipset));
else
DLOG("%s %u (%s) include fixed ipset (%s)\n",entity,dp->n,PROFILE_NAME(dp),dbg_ipset_fill(&ips_item->hfile->ipset));
}
LIST_FOREACH(ips_item, &dp->ips_collection_exclude, next)
{
if (ips_item->hfile->filename)
DLOG("%s %u (%s) exclude ipset %s (%s)\n",entity,dp->n,PROFILE_NAME(dp),ips_item->hfile->filename,dbg_ipset_fill(&ips_item->hfile->ipset));
else
DLOG("%s %u (%s) exclude fixed ipset (%s)\n",entity,dp->n,PROFILE_NAME(dp),dbg_ipset_fill(&ips_item->hfile->ipset));
}
}
void IpsetsDebug()
{
if (!params.debug) return;
struct ipset_file *hfile;
struct desync_profile_list *dpl;
struct ipset_item *ips_item;
LIST_FOREACH(hfile, &params.ipsets, next)
{
@@ -305,15 +323,10 @@ void IpsetsDebug()
LIST_FOREACH(dpl, &params.desync_profiles, next)
{
LIST_FOREACH(ips_item, &dpl->dp.ips_collection, next)
if (ips_item->hfile->filename)
DLOG("profile %u include ipset %s (%s)\n",dpl->dp.n,ips_item->hfile->filename,dbg_ipset_fill(&ips_item->hfile->ipset));
else
DLOG("profile %u include fixed ipset (%s)\n",dpl->dp.n,dbg_ipset_fill(&ips_item->hfile->ipset));
LIST_FOREACH(ips_item, &dpl->dp.ips_collection_exclude, next)
if (ips_item->hfile->filename)
DLOG("profile %u exclude ipset %s (%s)\n",dpl->dp.n,ips_item->hfile->filename,dbg_ipset_fill(&ips_item->hfile->ipset));
else
DLOG("profile %u exclude fixed ipset (%s)\n",dpl->dp.n,dbg_ipset_fill(&ips_item->hfile->ipset));
IpsetsDebugProfile(&dpl->dp, "profile");
}
LIST_FOREACH(dpl, &params.desync_templates, next)
{
IpsetsDebugProfile(&dpl->dp, "template");
}
}

View File

@@ -19,6 +19,11 @@
#include "crypto/aes-ctr.h"
void desync_instance(const char *func, unsigned int dp_n, unsigned int func_n, char *instance, size_t inst_size)
{
snprintf(instance, inst_size, "%s_%u_%u", func, dp_n, func_n);
}
static void lua_check_argc(lua_State *L, const char *where, int argc)
{
int num_args = lua_gettop(L);
@@ -605,7 +610,7 @@ static int luacall_uname(lua_State *L)
}
static int luacall_clock_gettime(lua_State *L)
{
lua_check_argc(L,"uname", 0);
lua_check_argc(L,"clock_gettime", 0);
LUA_STACK_GUARD_ENTER(L)
@@ -639,7 +644,7 @@ static int luacall_instance_cutoff(lua_State *L)
int argc=lua_gettop(L);
bool bIn,bOut;
if (argc>=2)
if (argc>=2 && lua_type(L,2)!=LUA_TNIL)
{
luaL_checktype(L,2,LUA_TBOOLEAN);
bOut = lua_toboolean(L,2);
@@ -705,6 +710,102 @@ bool lua_instance_cutoff_check(const t_lua_desync_context *ctx, bool bIn)
return b;
}
static int luacall_lua_cutoff(lua_State *L)
{
lua_check_argc_range(L,"lua_cutoff",1,2);
LUA_STACK_GUARD_ENTER(L)
t_lua_desync_context *ctx;
if (!lua_islightuserdata(L,1))
luaL_error(L, "lua_cutoff expect desync context in the first argument");
ctx = lua_touserdata(L,1);
int argc=lua_gettop(L);
bool bIn,bOut;
if (argc>=2 && lua_type(L,2)!=LUA_TNIL)
{
luaL_checktype(L,2,LUA_TBOOLEAN);
bOut = lua_toboolean(L,2);
bIn = !bOut;
}
else
bIn = bOut = true;
if (ctx->ctrack)
{
DLOG("lua cutoff from '%s' in=%u out=%u\n",ctx->instance,bIn,bOut);
// lua cutoff is one way transition
if (bIn) ctx->ctrack->b_lua_in_cutoff = true;
if (bOut) ctx->ctrack->b_lua_out_cutoff = true;
}
else
DLOG("lua cutoff requested from '%s' in=%u out=%u but not possible without conntrack\n",ctx->instance,bIn,bOut);
LUA_STACK_GUARD_RETURN(L,0)
}
static int luacall_execution_plan(lua_State *L)
{
lua_check_argc(L,"execution_plan",1);
LUA_STACK_GUARD_ENTER(L)
const t_lua_desync_context *ctx;
if (!lua_islightuserdata(L,1))
luaL_error(L, "execution_plan expect desync context in the first argument");
ctx = lua_touserdata(L,1);
lua_newtable(L);
struct func_list *func;
char instance[256], pls[2048];
struct packet_range *range;
unsigned int n=1;
LIST_FOREACH(func, &ctx->dp->lua_desync, next)
{
if (n > ctx->func_n)
{
desync_instance(func->func, ctx->dp->n, n, instance, sizeof(instance));
range = ctx->incoming ? &func->range_in : &func->range_out;
lua_pushinteger(params.L, n - ctx->func_n);
lua_createtable(params.L, 0, 6);
lua_pushf_args(&func->args, -1);
lua_pushf_str("func", func->func);
lua_pushf_int("func_n", ctx->func_n);
lua_pushf_str("func_instance", instance);
lua_pushf_range("range", range);
if (l7_payload_str_list(func->payload_type, pls, sizeof(pls)))
lua_pushf_str("payload_filter", pls);
else
lua_pushf_nil("payload_filter");
lua_rawset(params.L,-3);
}
n++;
}
LUA_STACK_GUARD_RETURN(L,1)
}
static int luacall_execution_plan_cancel(lua_State *L)
{
lua_check_argc(L,"execution_plan_cancel",1);
t_lua_desync_context *ctx;
if (!lua_islightuserdata(L,1))
luaL_error(L, "execution_plan_cancel expect desync context in the first argument");
ctx = lua_touserdata(L,1);
DLOG("execution plan cancel from '%s'\n",ctx->instance);
ctx->cancel = true;
return 0;
}
static int luacall_raw_packet(lua_State *L)
{
lua_check_argc(L,"raw_packet",1);
@@ -1066,21 +1167,23 @@ void lua_pushf_dissect(const struct dissect *dis)
lua_rawset(params.L,-3);
}
void lua_pushf_ctrack(const t_ctrack *ctrack)
void lua_pushf_ctrack(const t_ctrack *ctrack, const t_ctrack_position *pos)
{
LUA_STACK_GUARD_ENTER(params.L)
if (!pos) pos = &ctrack->pos;
lua_pushliteral(params.L, "track");
if (ctrack)
{
lua_createtable(params.L, 0, 13 + (ctrack->ipproto == IPPROTO_TCP));
lua_pushf_int("pcounter_orig", ctrack->pcounter_orig);
lua_pushf_int("pdcounter_orig", ctrack->pdcounter_orig);
lua_pushf_int("pbcounter_orig", ctrack->pbcounter_orig);
lua_pushf_int("pcounter_reply", ctrack->pcounter_reply);
lua_pushf_int("pdcounter_reply", ctrack->pdcounter_reply);
lua_pushf_int("pbcounter_reply", ctrack->pbcounter_reply);
lua_pushf_int("pcounter_orig", pos->pcounter_orig);
lua_pushf_int("pdcounter_orig", pos->pdcounter_orig);
lua_pushf_int("pbcounter_orig", pos->pbcounter_orig);
lua_pushf_int("pcounter_reply", pos->pcounter_reply);
lua_pushf_int("pdcounter_reply", pos->pdcounter_reply);
lua_pushf_int("pbcounter_reply", pos->pbcounter_reply);
if (ctrack->incoming_ttl)
lua_pushf_int("incoming_ttl", ctrack->incoming_ttl);
else
@@ -1096,20 +1199,20 @@ void lua_pushf_ctrack(const t_ctrack *ctrack)
{
lua_pushliteral(params.L, "tcp");
lua_createtable(params.L, 0, 14);
lua_pushf_int("seq0", ctrack->seq0);
lua_pushf_int("seq", ctrack->seq_last);
lua_pushf_int("ack0", ctrack->ack0);
lua_pushf_int("ack", ctrack->ack_last);
lua_pushf_int("pos_orig", ctrack->pos_orig - ctrack->seq0);
lua_pushf_int("winsize_orig", ctrack->winsize_orig);
lua_pushf_int("winsize_orig_calc", ctrack->winsize_orig_calc);
lua_pushf_int("scale_orig", ctrack->scale_orig);
lua_pushf_int("mss_orig", ctrack->mss_orig);
lua_pushf_int("pos_reply", ctrack->pos_reply - ctrack->ack0);
lua_pushf_int("winsize_reply", ctrack->winsize_reply);
lua_pushf_int("winsize_reply_calc", ctrack->winsize_reply_calc);
lua_pushf_int("scale_reply", ctrack->scale_reply);
lua_pushf_int("mss_reply", ctrack->mss_reply);
lua_pushf_int("seq0", pos->seq0);
lua_pushf_int("seq", pos->seq_last);
lua_pushf_int("ack0", pos->ack0);
lua_pushf_int("ack", pos->ack_last);
lua_pushf_int("pos_orig", pos->pos_orig - pos->seq0);
lua_pushf_int("winsize_orig", pos->winsize_orig);
lua_pushf_int("winsize_orig_calc", pos->winsize_orig_calc);
lua_pushf_int("scale_orig", pos->scale_orig);
lua_pushf_int("mss_orig", pos->mss_orig);
lua_pushf_int("pos_reply", pos->pos_reply - pos->ack0);
lua_pushf_int("winsize_reply", pos->winsize_reply);
lua_pushf_int("winsize_reply_calc", pos->winsize_reply_calc);
lua_pushf_int("scale_reply", pos->scale_reply);
lua_pushf_int("mss_reply", pos->mss_reply);
lua_rawset(params.L,-3);
}
}
@@ -1120,7 +1223,7 @@ void lua_pushf_ctrack(const t_ctrack *ctrack)
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
void lua_pushf_args(const struct ptr_list_head *args, int idx_desync)
void lua_pushf_args(const struct str2_list_head *args, int idx_desync)
{
// var=val - pass val string
// var=%val - subst 'val' blob
@@ -1130,7 +1233,7 @@ void lua_pushf_args(const struct ptr_list_head *args, int idx_desync)
LUA_STACK_GUARD_ENTER(params.L)
struct ptr_list *arg;
struct str2_list *arg;
const char *var, *val;
idx_desync = lua_absindex(params.L, idx_desync);
@@ -1139,8 +1242,8 @@ void lua_pushf_args(const struct ptr_list_head *args, int idx_desync)
lua_newtable(params.L);
LIST_FOREACH(arg, args, next)
{
var = (char*)arg->ptr1;
val = arg->ptr2 ? (char*)arg->ptr2 : "";
var = arg->str1;
val = arg->str2 ? arg->str2 : "";
if (val[0]=='\\' && (val[1]=='%' || val[1]=='#'))
// escape char
lua_pushf_str(var, val+1);
@@ -1160,7 +1263,33 @@ void lua_pushf_args(const struct ptr_list_head *args, int idx_desync)
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
void lua_pushf_pos(const char *name, const struct packet_pos *pos)
{
LUA_STACK_GUARD_ENTER(params.L)
char smode[2]="?";
lua_pushf_table(name);
lua_getfield(params.L,-1,name);
*smode=pos->mode;
lua_pushf_str("mode",smode);
lua_pushf_int("pos",pos->pos);
lua_pop(params.L,1);
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
void lua_pushf_range(const char *name, const struct packet_range *range)
{
LUA_STACK_GUARD_ENTER(params.L)
lua_pushf_table(name);
lua_getfield(params.L,-1,"range");
lua_pushf_bool("upper_cutoff",range->upper_cutoff);
lua_pushf_pos("from", &range->from);
lua_pushf_pos("to", &range->to);
lua_pop(params.L,1);
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
static void lua_reconstruct_extract_options(lua_State *L, int idx, bool *badsum, bool *ip6_preserve_next, uint8_t *ip6_last_proto)
@@ -2663,6 +2792,21 @@ static void lua_init_const(void)
{
LUA_STACK_GUARD_ENTER(params.L)
const struct
{
const char *name, *v;
} cstr[] = {
{"NFQWS2_VER",params.verstr}
};
DLOG("LUA STR:");
for (int i=0;i<sizeof(cstr)/sizeof(*cstr);i++)
{
lua_pushstring(params.L, cstr[i].v);
lua_setglobal(params.L, cstr[i].name);
DLOG(" %s", cstr[i].name);
}
const struct
{
const char *name;
@@ -2674,6 +2818,7 @@ static void lua_init_const(void)
{"divert_port",params.port},
#endif
{"desync_fwmark",params.desync_fwmark},
{"NFQWS2_COMPAT_VER",LUA_COMPAT_VER},
{"VERDICT_PASS",VERDICT_PASS},
{"VERDICT_MODIFY",VERDICT_MODIFY},
@@ -2735,7 +2880,7 @@ static void lua_init_const(void)
{"IPPROTO_SHIM6",IPPROTO_SHIM6},
{"IPPROTO_NONE",IPPROTO_NONE}
};
DLOG("LUA NUMERIC:");
DLOG("\nLUA NUMERIC:");
for (int i=0;i<sizeof(cuint)/sizeof(*cuint);i++)
{
lua_pushinteger(params.L, (lua_Integer)cuint[i].v);
@@ -2836,6 +2981,12 @@ static void lua_init_functions(void)
// voluntarily stop receiving packets
{"instance_cutoff",luacall_instance_cutoff},
// voluntarily stop receiving packets of the current connection for all instances
{"lua_cutoff",luacall_lua_cutoff},
// get info about upcoming desync instances and their arguments
{"execution_plan",luacall_execution_plan},
// cancel execution of upcoming desync instances and their arguments
{"execution_plan_cancel",luacall_execution_plan_cancel},
// get raw packet data
{"raw_packet",luacall_raw_packet},

View File

@@ -28,6 +28,9 @@
#define LUA_STACK_GUARD_RETURN(L,N) LUA_STACK_GUARD_LEAVE(L,N); return N;
void desync_instance(const char *func, unsigned int dp_n, unsigned int func_n, char *instance, size_t inst_size);
bool lua_test_init_script_files(void);
bool lua_init(void);
void lua_shutdown(void);
@@ -68,8 +71,10 @@ void lua_pushf_iphdr(const struct ip *ip, size_t len);
void lua_pushf_ip6hdr(const struct ip6_hdr *ip6, size_t len);
void lua_push_dissect(const struct dissect *dis);
void lua_pushf_dissect(const struct dissect *dis);
void lua_pushf_ctrack(const t_ctrack *ctrack);
void lua_pushf_args(const struct ptr_list_head *args, int idx_desync);
void lua_pushf_ctrack(const t_ctrack *ctrack, const t_ctrack_position *pos);
void lua_pushf_args(const struct str2_list_head *args, int idx_desync);
void lua_pushf_pos(const char *name, const struct packet_pos *pos);
void lua_pushf_range(const char *name, const struct packet_range *range);
void lua_pushf_global(const char *field, const char *global);
bool lua_reconstruct_ip6hdr(int idx, struct ip6_hdr *ip6, size_t *len, uint8_t last_proto, bool preserve_next);
@@ -79,10 +84,12 @@ bool lua_reconstruct_udphdr(int idx, struct udphdr *udp);
bool lua_reconstruct_dissect(int idx, uint8_t *buf, size_t *len, bool badsum, bool ip6_preserve_next);
typedef struct {
unsigned int func_n;
const char *func, *instance;
const struct desync_profile *dp;
const t_ctrack *ctrack;
const struct dissect *dis;
t_ctrack *ctrack;
bool incoming,cancel;
} t_lua_desync_context;
bool lua_instance_cutoff_check(const t_lua_desync_context *ctx, bool bIn);

View File

@@ -48,11 +48,6 @@
#define NF_ACCEPT 1
#endif
#define CTRACK_T_SYN 60
#define CTRACK_T_FIN 60
#define CTRACK_T_EST 300
#define CTRACK_T_UDP 60
#define MAX_CONFIG_FILE_SIZE 16384
struct params_s params;
@@ -99,7 +94,7 @@ static void onusr2(int sig)
struct desync_profile_list *dpl;
LIST_FOREACH(dpl, &params.desync_profiles, next)
{
printf("\nDESYNC profile %u\n", dpl->dp.n);
printf("\nDESYNC profile %u (%s)\n", dpl->dp.n, PROFILE_NAME(&dpl->dp));
HostFailPoolDump(dpl->dp.hostlist_auto_fail_counters);
}
printf("\nIPCACHE\n");
@@ -1007,27 +1002,28 @@ static bool parse_pf_list(char *opt, struct port_filters_head *pfl)
return true;
}
bool lua_call_param_add(char *opt, struct ptr_list_head *args)
bool lua_call_param_add(char *opt, struct str2_list_head *args)
{
char c,*p;
struct ptr_list *arg;
struct str2_list *arg;
if ((p = strchr(opt,'=')))
{
c = *p; *p = 0;
}
if (!is_identifier(opt) || !(arg=ptrlist_add(args)))
if (!is_identifier(opt) || !(arg=str2list_add(args)))
{
if (p) *p = c;
return false;
}
arg->ptr1 = strdup(opt);
arg->str1 = strdup(opt);
if (p)
{
arg->ptr2 = strdup(p+1);
arg->str2 = strdup(p+1);
*p = c;
if (!arg->str2) return false;
}
return arg->ptr1;
return !!arg->str1;
}
struct func_list *parse_lua_call(char *opt, struct func_list_head *flist)
@@ -1053,7 +1049,6 @@ struct func_list *parse_lua_call(char *opt, struct func_list_head *flist)
last = !*e;
c = *e;
*e = 0;
b = lua_call_param_add(p, &f->args);
if (!last) *e++ = c;
if (!b) goto err;
@@ -1156,21 +1151,21 @@ static void BlobDebug()
}
}
static void LuaDesyncDebug(struct desync_profile *dp)
static void LuaDesyncDebug(struct desync_profile *dp, const char *entity)
{
if (params.debug)
{
struct func_list *func;
struct ptr_list *arg;
struct str2_list *arg;
int n,i;
LIST_FOREACH(func, &dp->lua_desync, next)
{
DLOG("profile %u lua %s(",dp->n,func->func);
DLOG("%s %u (%s) lua %s(",entity,dp->n,PROFILE_NAME(dp),func->func);
n=0;
LIST_FOREACH(arg, &func->args, next)
{
if (n) DLOG(",");
DLOG(arg->ptr2 ? "%s=\"%s\"" : "%s=\"\"", (char*)arg->ptr1, (char*)arg->ptr2);
DLOG(arg->str2 ? "%s=\"%s\"" : "%s=\"\"", arg->str1, arg->str2);
n++;
}
DLOG(" range_in=%c%u%c%c%u range_out=%c%u%c%c%u payload_type=",
@@ -1412,8 +1407,12 @@ static void exithelp(void)
" --lua-init=@<filename>|<lua_text>\t\t\t; load LUA program from a file or string. if multiple parameters present order of execution is preserved.\n"
" --lua-gc=<int>\t\t\t\t\t\t; forced garbage collection every N sec. default %u sec. triggers only when a packet arrives. 0 = disable.\n"
"\nMULTI-STRATEGY:\n"
" --new\t\t\t\t\t\t\t; begin new strategy\n"
" --skip\t\t\t\t\t\t\t; do not use this strategy\n"
" --new\t\t\t\t\t\t\t; begin new profile\n"
" --skip\t\t\t\t\t\t\t; do not use this profile\n"
" --name=<name>\t\t\t\t\t\t; set profile name\n"
" --template[=<name>]\t\t\t\t\t; use this profile as template (must be named or will be useless)\n"
" --cookie[=<string>]\t\t\t\t\t; pass this profile-bound string to LUA\n"
" --import=<name>\t\t\t\t\t; populate current profile with template data\n"
" --filter-l3=ipv4|ipv6\t\t\t\t\t; L3 protocol filter. multiple comma separated values allowed.\n"
" --filter-tcp=[~]port1[-port2]|*\t\t\t; TCP port filter. ~ means negation. setting tcp and not setting udp filter denies udp. comma separated list allowed.\n"
" --filter-udp=[~]port1[-port2]|*\t\t\t; UDP port filter. ~ means negation. setting udp and not setting tcp filter denies tcp. comma separated list allowed.\n"
@@ -1500,22 +1499,6 @@ static void ApplyDefaultBlobs(struct blob_collection_head *blobs)
load_const_blob_to_collection("fake_default_quic",buf,620,blobs,0);
}
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#if defined(ZAPRET_GH_VER) || defined (ZAPRET_GH_HASH)
#ifdef __ANDROID__
#define PRINT_VER printf("github android version %s (%s)\n\n", TOSTRING(ZAPRET_GH_VER), TOSTRING(ZAPRET_GH_HASH))
#else
#define PRINT_VER printf("github version %s (%s)\n\n", TOSTRING(ZAPRET_GH_VER), TOSTRING(ZAPRET_GH_HASH))
#endif
#else
#ifdef __ANDROID__
#define PRINT_VER printf("self-built android version %s %s\n\n", __DATE__, __TIME__)
#else
#define PRINT_VER printf("self-built version %s %s\n\n", __DATE__, __TIME__)
#endif
#endif
enum opt_indices {
IDX_DEBUG,
IDX_DRY_RUN,
@@ -1563,6 +1546,10 @@ enum opt_indices {
IDX_HOSTLIST_AUTO_DEBUG,
IDX_NEW,
IDX_SKIP,
IDX_NAME,
IDX_TEMPLATE,
IDX_IMPORT,
IDX_COOKIE,
IDX_FILTER_L3,
IDX_FILTER_TCP,
IDX_FILTER_UDP,
@@ -1643,6 +1630,10 @@ static const struct option long_options[] = {
[IDX_HOSTLIST_AUTO_DEBUG] = {"hostlist-auto-debug", required_argument, 0, 0},
[IDX_NEW] = {"new", no_argument, 0, 0},
[IDX_SKIP] = {"skip", no_argument, 0, 0},
[IDX_NAME] = {"name", required_argument, 0, 0},
[IDX_TEMPLATE] = {"template", optional_argument, 0, 0},
[IDX_IMPORT] = {"import", required_argument, 0, 0},
[IDX_COOKIE] = {"cookie", required_argument, 0, 0},
[IDX_FILTER_L3] = {"filter-l3", required_argument, 0, 0},
[IDX_FILTER_TCP] = {"filter-tcp", required_argument, 0, 0},
[IDX_FILTER_UDP] = {"filter-udp", required_argument, 0, 0},
@@ -1679,16 +1670,26 @@ static const struct option long_options[] = {
[IDX_LAST] = {NULL, 0, NULL, 0},
};
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#if defined(ZAPRET_GH_VER) || defined (ZAPRET_GH_HASH)
#ifdef __ANDROID__
#define MAKE_VER(s,size) snprintf(s,size,"github android version %s (%s) lua_compat_ver %u", TOSTRING(ZAPRET_GH_VER), TOSTRING(ZAPRET_GH_HASH), LUA_COMPAT_VER)
#else
#define MAKE_VER(s,size) snprintf(s,size,"github version %s (%s) lua_compat_ver %u", TOSTRING(ZAPRET_GH_VER), TOSTRING(ZAPRET_GH_HASH), LUA_COMPAT_VER)
#endif
#else
#ifdef __ANDROID__
#define MAKE_VER(s,size) snprintf(s,size,"self-built android version %s %s lua_compat_ver %u", __DATE__, __TIME__, LUA_COMPAT_VER)
#else
#define MAKE_VER(s,size) snprintf(s,size,"self-built version %s %s lua_compat_ver %u", __DATE__, __TIME__, LUA_COMPAT_VER)
#endif
#endif
int main(int argc, char **argv)
{
if (argc < 2) exithelp();
aes_init_keygen_tables(); // required for aes
set_console_io_buffering();
set_env_exedir(argv[0]);
#ifdef __CYGWIN__
prepare_low_appdata();
if (service_run(argc, argv))
{
// we were running as service. now exit.
@@ -1697,7 +1698,7 @@ int main(int argc, char **argv)
#endif
int result, v;
int option_index = 0;
bool bSkip = false, bDry = false;
bool bSkip = false, bDry = false, bTemplate;
struct hostlist_file *anon_hl = NULL, *anon_hl_exclude = NULL;
struct ipset_file *anon_ips = NULL, *anon_ips_exclude = NULL;
uint64_t payload_type=0;
@@ -1709,17 +1710,29 @@ int main(int argc, char **argv)
unsigned int hash_wf_tcp_in = 0, hash_wf_udp_in = 0, hash_wf_tcp_out = 0, hash_wf_udp_out = 0, hash_wf_raw = 0, hash_wf_raw_part = 0, hash_ssid_filter = 0, hash_nlm_filter = 0;
#endif
if (argc < 2) exithelp();
srandom(time(NULL));
aes_init_keygen_tables(); // required for aes
mask_from_preflen6_prepare();
set_env_exedir(argv[0]);
set_console_io_buffering();
#ifdef __CYGWIN__
prepare_low_appdata();
#endif
PRINT_VER;
init_params(&params);
memset(&params, 0, sizeof(params));
MAKE_VER(params.verstr, sizeof(params.verstr));
printf("%s\n\n",params.verstr);
ApplyDefaultBlobs(&params.blobs);
struct desync_profile_list *dpl;
struct desync_profile *dp;
unsigned int desync_profile_count = 0;
unsigned int desync_profile_count = 0, desync_template_count = 0;
bTemplate = false;
if (!(dpl = dp_list_add(&params.desync_profiles)))
{
DLOG_ERR("desync_profile_add: out of memory\n");
@@ -1728,39 +1741,6 @@ int main(int argc, char **argv)
dp = &dpl->dp;
dp->n = ++desync_profile_count;
#ifdef __linux__
params.qnum = -1;
#elif defined(BSD)
params.port = 0;
#endif
params.desync_fwmark = DPI_DESYNC_FWMARK_DEFAULT;
params.ctrack_t_syn = CTRACK_T_SYN;
params.ctrack_t_est = CTRACK_T_EST;
params.ctrack_t_fin = CTRACK_T_FIN;
params.ctrack_t_udp = CTRACK_T_UDP;
params.ipcache_lifetime = IPCACHE_LIFETIME;
params.lua_gc = LUA_GC_INTERVAL;
LIST_INIT(&params.hostlists);
LIST_INIT(&params.ipsets);
LIST_INIT(&params.blobs);
LIST_INIT(&params.lua_init_scripts);
ApplyDefaultBlobs(&params.blobs);
#ifdef __CYGWIN__
LIST_INIT(&params.ssid_filter);
LIST_INIT(&params.nlm_filter);
LIST_INIT(&params.wf_raw_part);
#else
if (can_drop_root())
{
params.uid = params.gid[0] = 0x7FFFFFFF; // default uid:gid
params.gid_count = 1;
params.droproot = true;
}
#endif
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
if (argc >= 2 && (argv[1][0] == '@' || argv[1][0] == '$'))
{
@@ -2120,23 +2100,83 @@ int main(int argc, char **argv)
else
{
check_dp(dp);
if (bTemplate)
{
if (dp->name && dp_list_search_name(&params.desync_templates, dp->name))
{
DLOG_ERR("template '%s' already present\n", dp->name);
exit_clean(1);
}
dpl->dp.n = ++desync_template_count;
dp_list_move(&params.desync_templates, dpl);
}
else
{
desync_profile_count++;
}
if (!(dpl = dp_list_add(&params.desync_profiles)))
{
DLOG_ERR("desync_profile_add: out of memory\n");
exit_clean(1);
}
dp = &dpl->dp;
dp->n = ++desync_profile_count;
dp->n = desync_profile_count;
}
anon_hl = anon_hl_exclude = NULL;
anon_ips = anon_ips_exclude = NULL;
payload_type = 0;
range_in = PACKET_RANGE_NEVER;
range_out = PACKET_RANGE_ALWAYS;
bTemplate = false;
break;
case IDX_SKIP:
bSkip = true;
break;
case IDX_TEMPLATE:
bTemplate = true;
case IDX_NAME:
if (optarg)
{
free(dp->name);
if (!(dp->name = strdup(optarg)))
{
DLOG_ERR("out of memory\n");
exit_clean(1);
}
}
break;
case IDX_COOKIE:
free(dp->cookie);
if (!(dp->cookie = strdup(optarg)))
{
DLOG_ERR("out of memory\n");
exit_clean(1);
}
break;
case IDX_IMPORT:
{
struct desync_profile_list *tpl = dp_list_search_name(&params.desync_templates, optarg);
if (!tpl)
{
DLOG_ERR("template '%s' not found\n", optarg);
exit_clean(1);
}
if (!dp_list_copy(dp, &tpl->dp))
{
DLOG_ERR("could not copy template\n");
exit_clean(1);
}
dp->n = desync_profile_count;
free(dp->name_tpl);
if (tpl->dp.name && !(dp->name_tpl = strdup(tpl->dp.name)))
{
DLOG_ERR("out of memory\n");
exit_clean(1);
}
dp->n_tpl = tpl->dp.n;
}
break;
case IDX_FILTER_L3:
if (!wf_make_l3(optarg, &dp->filter_ipv4, &dp->filter_ipv6))
@@ -2399,7 +2439,20 @@ int main(int argc, char **argv)
desync_profile_count--;
}
else
{
check_dp(dp);
if (bTemplate)
{
if (dp->name && dp_list_search_name(&params.desync_templates, dp->name))
{
DLOG_ERR("template '%s' already present\n", dp->name);
exit_clean(1);
}
dpl->dp.n = ++desync_template_count;
dp_list_move(&params.desync_templates, dpl);
desync_profile_count--;
}
}
// do not need args from file anymore
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
@@ -2423,13 +2476,14 @@ int main(int argc, char **argv)
DLOG("adding low-priority default empty desync profile\n");
// add default empty profile
if (!(dpl = dp_list_add(&params.desync_profiles)))
if (!(dpl = dp_list_add(&params.desync_profiles)) || !(dpl->dp.name=strdup("no_action")))
{
DLOG_ERR("desync_profile_add: out of memory\n");
exit_clean(1);
}
DLOG_CONDUP("we have %d user defined desync profile(s) and default low priority profile 0\n", desync_profile_count);
DLOG_CONDUP("we have %u user defined desync profile(s) and default low priority profile 0\n", desync_profile_count);
DLOG_CONDUP("we have %u user defined desync template(s)\n", desync_template_count);
if (params.writeable_dir_enable)
{
@@ -2463,11 +2517,16 @@ int main(int argc, char **argv)
if (params.droproot)
#endif
{
if (dp->hostlist_auto && ensure_file_access(dp->hostlist_auto->filename))
if (dp->hostlist_auto && !ensure_file_access(dp->hostlist_auto->filename))
DLOG_ERR("could not make '%s' accessible. auto hostlist file may not be writable after privilege drop\n", dp->hostlist_auto->filename);
}
LuaDesyncDebug(dp);
LuaDesyncDebug(dp,"profile");
}
LIST_FOREACH(dpl, &params.desync_templates, next)
{
dp = &dpl->dp;
LuaDesyncDebug(dp,"template");
}
if (!test_list_files())
@@ -2493,6 +2552,9 @@ int main(int argc, char **argv)
BlobDebug();
DLOG("\n");
// not required anymore. free memory
dp_list_destroy(&params.desync_templates);
#ifdef __CYGWIN__
if (!*params.windivert_filter)
{
@@ -2566,7 +2628,7 @@ int main(int argc, char **argv)
exit_clean(1);
#endif
print_id();
if (!test_list_files())
if (!test_list_files() || !lua_test_init_script_files())
exit_clean(1);
}
#endif

View File

@@ -10,3 +10,6 @@
extern bool bQuit;
#endif
int main(int argc, char *argv[]);
// when something changes that can break LUA compatibility this version should be increased
#define LUA_COMPAT_VER 2

View File

@@ -26,7 +26,7 @@ void rawpacket_queue_destroy(struct rawpacket_tailhead *q)
while((rp = rawpacket_dequeue(q))) rawpacket_free(rp);
}
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload)
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload,const t_ctrack_position *pos)
{
struct rawpacket *rp = malloc(sizeof(struct rawpacket));
if (!rp) return NULL;
@@ -52,6 +52,15 @@ struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sock
memcpy(rp->packet,data,len);
rp->len=len;
rp->len_payload=len_payload;
// make a copy for replay
if (pos)
{
rp->pos = *pos;
rp->pos_present = true;
}
else
rp->pos_present = false;
TAILQ_INSERT_TAIL(q, rp, next);

View File

@@ -6,6 +6,8 @@
#include <net/if.h>
#include <sys/socket.h>
#include "conntrack_base.h"
struct rawpacket
{
struct sockaddr_storage dst;
@@ -14,6 +16,8 @@ struct rawpacket
uint32_t fwmark;
size_t len, len_payload;
uint8_t *packet;
t_ctrack_position pos;
bool pos_present;
TAILQ_ENTRY(rawpacket) next;
};
TAILQ_HEAD(rawpacket_tailhead, rawpacket);
@@ -22,6 +26,6 @@ void rawpacket_queue_init(struct rawpacket_tailhead *q);
void rawpacket_queue_destroy(struct rawpacket_tailhead *q);
bool rawpacket_queue_empty(const struct rawpacket_tailhead *q);
unsigned int rawpacket_queue_count(const struct rawpacket_tailhead *q);
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload);
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload,const t_ctrack_position *pos);
struct rawpacket *rawpacket_dequeue(struct rawpacket_tailhead *q);
void rawpacket_free(struct rawpacket *rp);

View File

@@ -322,7 +322,7 @@ void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit)
}
}
void dp_init(struct desync_profile *dp)
void dp_init_dynamic(struct desync_profile *dp)
{
LIST_INIT(&dp->hl_collection);
LIST_INIT(&dp->hl_collection_exclude);
@@ -331,33 +331,25 @@ void dp_init(struct desync_profile *dp)
LIST_INIT(&dp->pf_tcp);
LIST_INIT(&dp->pf_udp);
LIST_INIT(&dp->lua_desync);
#ifdef HAS_FILTER_SSID
LIST_INIT(&dp->filter_ssid);
#endif
}
void dp_init(struct desync_profile *dp)
{
dp_init_dynamic(dp);
dp->hostlist_auto_fail_threshold = HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT;
dp->hostlist_auto_fail_time = HOSTLIST_AUTO_FAIL_TIME_DEFAULT;
dp->hostlist_auto_retrans_threshold = HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT;
dp->filter_ipv4 = dp->filter_ipv6 = true;
}
struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head)
{
struct desync_profile_list *entry = calloc(1,sizeof(struct desync_profile_list));
if (!entry) return NULL;
dp_init(&entry->dp);
// add to the tail
struct desync_profile_list *dpn,*dpl=LIST_FIRST(&params.desync_profiles);
if (dpl)
{
while ((dpn=LIST_NEXT(dpl,next))) dpl = dpn;
LIST_INSERT_AFTER(dpl, entry, next);
}
else
LIST_INSERT_HEAD(&params.desync_profiles, entry, next);
return entry;
}
static void dp_clear_dynamic(struct desync_profile *dp)
{
free(dp->name);
free(dp->name_tpl);
free(dp->cookie);
hostlist_collection_destroy(&dp->hl_collection);
hostlist_collection_destroy(&dp->hl_collection_exclude);
ipset_collection_destroy(&dp->ips_collection);
@@ -389,6 +381,67 @@ void dp_list_destroy(struct desync_profile_list_head *head)
dp_entry_destroy(entry);
}
}
static struct desync_profile_list *desync_profile_entry_alloc()
{
struct desync_profile_list *entry = calloc(1,sizeof(struct desync_profile_list));
if (entry) dp_init(&entry->dp);
return entry;
}
struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head)
{
struct desync_profile_list *entry = desync_profile_entry_alloc();
if (!entry) return false;
struct desync_profile_list *tail, *item;
LIST_TAIL(head, tail, item);
LIST_INSERT_TAIL(head, tail, entry, next);
return entry;
}
bool dp_list_copy(struct desync_profile *to, const struct desync_profile *from)
{
// clear everything in target
dp_clear(to);
// first copy all simple type values
*to = *from;
// prepare empty dynamic structures
dp_init_dynamic(to);
// copy dynamic structures
if (from->name && !(to->name = strdup(from->name))) return false;
if (from->name_tpl && !(to->name_tpl = strdup(from->name_tpl))) return false;
if (from->cookie && !(to->cookie = strdup(from->cookie))) return false;
if (
#ifdef HAS_FILTER_SSID
!strlist_copy(&to->filter_ssid, &from->filter_ssid) ||
#endif
!funclist_copy(&to->lua_desync, &from->lua_desync) ||
!ipset_collection_copy(&to->ips_collection, &from->ips_collection) ||
!ipset_collection_copy(&to->ips_collection_exclude, &from->ips_collection_exclude) ||
!hostlist_collection_copy(&to->hl_collection, &from->hl_collection) ||
!hostlist_collection_copy(&to->hl_collection_exclude, &from->hl_collection_exclude))
{
return false;
}
return true;
}
void dp_list_move(struct desync_profile_list_head *target, struct desync_profile_list *dpl)
{
struct desync_profile_list *tail, *item;
LIST_TAIL(target, tail, item);
LIST_REMOVE(dpl, next);
LIST_INSERT_TAIL(target, tail, dpl, next);
}
struct desync_profile_list *dp_list_search_name(struct desync_profile_list_head *head, const char *name)
{
struct desync_profile_list *dpl;
if (name)
LIST_FOREACH(dpl, head, next)
if (dpl->dp.name && !strcmp(dpl->dp.name, name))
return dpl;
return NULL;
}
bool dp_list_have_autohostlist(struct desync_profile_list_head *head)
{
struct desync_profile_list *dpl;
@@ -427,6 +480,7 @@ void cleanup_params(struct params_s *params)
ConntrackPoolDestroy(&params->conntrack);
dp_list_destroy(&params->desync_profiles);
dp_list_destroy(&params->desync_templates);
hostlist_files_destroy(&params->hostlists);
ipset_files_destroy(&params->ipsets);
ipcacheDestroy(&params->ipcache);
@@ -440,3 +494,40 @@ void cleanup_params(struct params_s *params)
free(params->user); params->user=NULL;
#endif
}
void init_params(struct params_s *params)
{
memset(params, 0, sizeof(*params));
#ifdef __linux__
params->qnum = -1;
#elif defined(BSD)
params->port = 0;
#endif
params->desync_fwmark = DPI_DESYNC_FWMARK_DEFAULT;
params->ctrack_t_syn = CTRACK_T_SYN;
params->ctrack_t_est = CTRACK_T_EST;
params->ctrack_t_fin = CTRACK_T_FIN;
params->ctrack_t_udp = CTRACK_T_UDP;
params->ipcache_lifetime = IPCACHE_LIFETIME;
params->lua_gc = LUA_GC_INTERVAL;
LIST_INIT(&params->hostlists);
LIST_INIT(&params->ipsets);
LIST_INIT(&params->blobs);
LIST_INIT(&params->lua_init_scripts);
#ifdef __CYGWIN__
LIST_INIT(&params->ssid_filter);
LIST_INIT(&params->nlm_filter);
LIST_INIT(&params->wf_raw_part);
#else
if (can_drop_root())
{
params->uid = params->gid[0] = 0x7FFFFFFF; // default uid:gid
params->gid_count = 1;
params->droproot = true;
}
#endif
}

View File

@@ -6,6 +6,7 @@
#include "desync.h"
#include "protocol.h"
#include "helpers.h"
#include "sec.h"
#include <sys/param.h>
#include <sys/types.h>
@@ -53,6 +54,10 @@ enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG, LOG_
struct desync_profile
{
unsigned int n; // number of the profile
char *name; // optional malloced name string
unsigned int n_tpl; // number of imported template
char *name_tpl; // imported template name
char *cookie; // optional malloced string
bool filter_ipv4,filter_ipv6;
struct port_filters_head pf_tcp,pf_udp;
@@ -78,6 +83,7 @@ struct desync_profile
struct func_list_head lua_desync;
};
#define PROFILE_NAME(dp) ((dp)->name ? (dp)->name : "noname")
#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))
@@ -89,6 +95,9 @@ struct desync_profile_list {
};
LIST_HEAD(desync_profile_list_head, desync_profile_list);
struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head);
void dp_list_move(struct desync_profile_list_head *target, struct desync_profile_list *dpl);
bool dp_list_copy(struct desync_profile *to, const struct desync_profile *from);
struct desync_profile_list *dp_list_search_name(struct desync_profile_list_head *head, const char *name);
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);
@@ -104,6 +113,7 @@ struct params_s
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
wordexp_t wexp; // for file based config
#endif
char verstr[128];
enum log_target debug_target;
char debug_logfile[PATH_MAX];
@@ -119,7 +129,7 @@ struct params_s
bool bind_fix4,bind_fix6;
uint32_t desync_fwmark; // unused in BSD
struct desync_profile_list_head desync_profiles;
struct desync_profile_list_head desync_profiles, desync_templates;
#ifdef __CYGWIN__
struct str_list_head ssid_filter,nlm_filter;
@@ -170,6 +180,8 @@ struct params_s
extern struct params_s params;
extern const char *progname;
void init_params(struct params_s *params);
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
void cleanup_args(struct params_s *params);
#endif

View File

@@ -155,20 +155,33 @@ bool strlist_add(struct str_list_head *head, const char *str)
LIST_INSERT_HEAD(head, entry, next);
return true;
}
static struct str_list *strlist_entry_copy(const struct str_list *entry)
{
return strlist_entry_alloc(entry->str);
}
bool strlist_copy(struct str_list_head *to, const struct str_list_head *from)
{
struct str_list *tail, *item, *entry;
LIST_TAIL(to, tail, item);
LIST_FOREACH(item, from, next)
{
if (!(entry = strlist_entry_copy(item))) return false;
LIST_INSERT_TAIL(to, tail, entry, next);
tail = tail ? LIST_NEXT(tail, next) : LIST_FIRST(to);
}
return true;
}
bool strlist_add_tail(struct str_list_head *head, const char *str)
{
struct str_list *entry = strlist_entry_alloc(str);
if (!entry) return false;
// add to the tail
struct str_list *strn,*strl=LIST_FIRST(head);
if (strl)
{
while ((strn=LIST_NEXT(strl,next))) strl = strn;
LIST_INSERT_AFTER(strl, entry, next);
}
else
LIST_INSERT_HEAD(head, entry, next);
struct str_list *tail, *item;
LIST_TAIL(head, tail, item);
LIST_INSERT_TAIL(head, tail, entry, next);
return true;
}
static void strlist_entry_destroy(struct str_list *entry)
@@ -200,35 +213,77 @@ bool strlist_search(const struct str_list_head *head, const char *str)
}
static struct ptr_list *ptrlist_entry_alloc()
static void str2list_entry_destroy(struct str2_list *entry)
{
return (struct ptr_list*)calloc(1,sizeof(struct ptr_list));
free(entry->str1);
free(entry->str2);
free(entry);
}
void str2list_destroy(struct str2_list_head *head)
{
struct str2_list *entry;
while ((entry = LIST_FIRST(head)))
{
LIST_REMOVE(entry, next);
str2list_entry_destroy(entry);
}
}
static struct str2_list *str2list_entry_alloc()
{
return (struct str2_list*)calloc(1,sizeof(struct str2_list));
}
struct ptr_list *ptrlist_add(struct ptr_list_head *head)
struct str2_list *str2list_add(struct str2_list_head *head)
{
struct ptr_list *entry = ptrlist_entry_alloc();
struct str2_list *entry = str2list_entry_alloc();
if (!entry) return NULL;
LIST_INSERT_HEAD(head, entry, next);
return entry;
}
static void ptrlist_entry_destroy(struct ptr_list *entry)
static struct str2_list *str2list_entry_copy(const struct str2_list *entry)
{
free(entry->ptr1);
free(entry->ptr2);
struct str2_list *e2 = str2list_entry_alloc();
if (!e2) return NULL;
e2->str1 = strdup(entry->str1);
e2->str2 = strdup(entry->str2);
if (!e2->str1 || !e2->str2)
{
str2list_entry_destroy(e2);
return false;
}
return e2;
}
bool str2list_copy(struct str2_list_head *to, const struct str2_list_head *from)
{
struct str2_list *tail, *item, *entry;
LIST_TAIL(to, tail, item);
LIST_FOREACH(item, from, next)
{
if (!(entry = str2list_entry_copy(item))) return false;
LIST_INSERT_TAIL(to, tail, entry, next);
tail = tail ? LIST_NEXT(tail, next) : LIST_FIRST(to);
}
return true;
}
static void funclist_entry_destroy(struct func_list *entry)
{
free(entry->func);
str2list_destroy(&entry->args);
free(entry);
}
void ptrlist_destroy(struct ptr_list_head *head)
void funclist_destroy(struct func_list_head *head)
{
struct ptr_list *entry;
struct func_list *entry;
while ((entry = LIST_FIRST(head)))
{
LIST_REMOVE(entry, next);
ptrlist_entry_destroy(entry);
funclist_entry_destroy(entry);
}
}
static struct func_list *funclist_entry_alloc(const char *func)
{
struct func_list *entry = malloc(sizeof(struct func_list));
@@ -250,31 +305,38 @@ struct func_list *funclist_add_tail(struct func_list_head *head, const char *fun
struct func_list *entry = funclist_entry_alloc(func);
if (!entry) return NULL;
// add to the tail
struct func_list *funcn,*funcl=LIST_FIRST(head);
if (funcl)
{
while ((funcn=LIST_NEXT(funcl,next))) funcl = funcn;
LIST_INSERT_AFTER(funcl, entry, next);
}
else
LIST_INSERT_HEAD(head, entry, next);
struct func_list *tail, *item;
LIST_TAIL(head, tail, item);
LIST_INSERT_TAIL(head, tail, entry, next);
return entry;
}
static void funclist_entry_destroy(struct func_list *entry)
static struct func_list *funclist_entry_copy(const struct func_list *entry)
{
free(entry->func);
ptrlist_destroy(&entry->args);
free(entry);
}
void funclist_destroy(struct func_list_head *head)
{
struct func_list *entry;
while ((entry = LIST_FIRST(head)))
struct func_list *e2 = funclist_entry_alloc(entry->func);
if (!e2) return NULL;
e2->payload_type = entry->payload_type;
e2->range_in = entry->range_in;
e2->range_out = entry->range_out;
if (!str2list_copy(&e2->args, &entry->args))
{
LIST_REMOVE(entry, next);
funclist_entry_destroy(entry);
funclist_entry_destroy(e2);
return false;
}
return e2;
}
bool funclist_copy(struct func_list_head *to, const struct func_list_head *from)
{
struct func_list *tail, *item, *entry;
LIST_TAIL(to, tail, item);
LIST_FOREACH(item, from, next)
{
if (!(entry = funclist_entry_copy(item))) return false;
LIST_INSERT_TAIL(to, tail, entry, next);
tail = tail ? LIST_NEXT(tail, next) : LIST_FIRST(to);
}
return true;
}
@@ -333,16 +395,36 @@ void hostlist_files_reset_modtime(struct hostlist_files_head *list)
FILE_MOD_RESET(&hfile->mod_sig);
}
struct hostlist_item *hostlist_collection_add(struct hostlist_collection_head *head, struct hostlist_file *hfile)
static struct hostlist_item *hostlist_collection_entry_alloc(struct hostlist_file *hfile)
{
struct hostlist_item *entry = malloc(sizeof(struct hostlist_item));
if (entry)
{
entry->hfile = hfile;
LIST_INSERT_HEAD(head, entry, next);
}
if (entry) entry->hfile = hfile;
return entry;
}
struct hostlist_item *hostlist_collection_add(struct hostlist_collection_head *head, struct hostlist_file *hfile)
{
struct hostlist_item *entry = hostlist_collection_entry_alloc(hfile);
if (entry) LIST_INSERT_HEAD(head, entry, next);
return entry;
}
static struct hostlist_item *hostlist_collection_entry_copy(const struct hostlist_item *entry)
{
return hostlist_collection_entry_alloc(entry->hfile);
}
bool hostlist_collection_copy(struct hostlist_collection_head *to, const struct hostlist_collection_head *from)
{
struct hostlist_item *tail, *item, *entry;
LIST_TAIL(to, tail, item);
LIST_FOREACH(item, from, next)
{
if (!(entry = hostlist_collection_entry_copy(item))) return false;
LIST_INSERT_TAIL(to, tail, entry, next);
tail = tail ? LIST_NEXT(tail, next) : LIST_FIRST(to);
}
return true;
}
void hostlist_collection_destroy(struct hostlist_collection_head *head)
{
struct hostlist_item *entry;
@@ -579,16 +661,36 @@ void ipset_files_reset_modtime(struct ipset_files_head *list)
FILE_MOD_RESET(&hfile->mod_sig);
}
struct ipset_item *ipset_collection_add(struct ipset_collection_head *head, struct ipset_file *hfile)
static struct ipset_item *ipset_collection_entry_alloc(struct ipset_file *hfile)
{
struct ipset_item *entry = malloc(sizeof(struct ipset_item));
if (entry)
{
entry->hfile = hfile;
LIST_INSERT_HEAD(head, entry, next);
}
if (entry) entry->hfile = hfile;
return entry;
}
struct ipset_item *ipset_collection_add(struct ipset_collection_head *head, struct ipset_file *hfile)
{
struct ipset_item *entry = ipset_collection_entry_alloc(hfile);
if (entry) LIST_INSERT_HEAD(head, entry, next);
return entry;
}
static struct ipset_item *ipset_collection_entry_copy(const struct ipset_item *entry)
{
return ipset_collection_entry_alloc(entry->hfile);
}
bool ipset_collection_copy(struct ipset_collection_head *to, const struct ipset_collection_head *from)
{
struct ipset_item *tail, *item, *entry;
LIST_TAIL(to, tail, item);
LIST_FOREACH(item, from, next)
{
if (!(entry = ipset_collection_entry_copy(item))) return false;
LIST_INSERT_TAIL(to, tail, entry, next);
tail = tail ? LIST_NEXT(tail, next) : LIST_FIRST(to);
}
return true;
}
void ipset_collection_destroy(struct ipset_collection_head *head)
{
struct ipset_item *entry;
@@ -645,7 +747,7 @@ bool port_filters_in_range(const struct port_filters_head *head, uint16_t port)
{
const struct port_filter_item *item;
if (!LIST_FIRST(head)) return true;
if (LIST_EMPTY(head)) return true;
LIST_FOREACH(item, head, next)
{
if (pf_in_range(port, &item->pf))
@@ -656,7 +758,7 @@ 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)
{
port_filter pf;
if (LIST_FIRST(head)) return true;
if (!LIST_EMPTY(head)) return true;
return pf_parse("0",&pf) && port_filter_add(head,&pf);
}
@@ -667,15 +769,9 @@ 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);
struct blob_item *tail, *item;
LIST_TAIL(head, tail, item);
LIST_INSERT_TAIL(head, tail, entry, next);
}
return entry;
}
@@ -693,14 +789,9 @@ struct blob_item *blob_collection_add_blob(struct blob_collection_head *head, co
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);
struct blob_item *tail, *item;
LIST_TAIL(head, tail, item);
LIST_INSERT_TAIL(head, tail, entry, next);
return entry;
}
@@ -725,7 +816,7 @@ void blob_collection_destroy(struct blob_collection_head *head)
}
bool blob_collection_empty(const struct blob_collection_head *head)
{
return !LIST_FIRST(head);
return LIST_EMPTY(head);
}
struct blob_item *blob_collection_search_name(struct blob_collection_head *head, const char *name)
{

View File

@@ -17,6 +17,17 @@
#define HOSTLIST_POOL_FLAG_STRICT_MATCH 1
#define LIST_TAIL(head, tail, temp) {\
tail=LIST_FIRST(head); \
if (tail) while ((temp=LIST_NEXT(tail,next))) tail = temp; }
#define LIST_INSERT_TAIL(head, tail, elm, field) { \
if (LIST_FIRST(head)) \
LIST_INSERT_AFTER(tail, elm, field); \
else \
LIST_INSERT_HEAD(head, elm, field); }
typedef struct hostlist_pool {
char *str; /* key */
uint32_t flags; /* custom data */
@@ -38,25 +49,28 @@ bool strlist_add(struct str_list_head *head, const char *str);
bool strlist_add_tail(struct str_list_head *head, const char *str);
void strlist_destroy(struct str_list_head *head);
bool strlist_search(const struct str_list_head *head, const char *str);
bool strlist_copy(struct str_list_head *to, const struct str_list_head *from);
struct ptr_list {
void *ptr1,*ptr2;
LIST_ENTRY(ptr_list) next;
struct str2_list {
char *str1,*str2;
LIST_ENTRY(str2_list) next;
};
LIST_HEAD(ptr_list_head, ptr_list);
LIST_HEAD(str2_list_head, str2_list);
struct ptr_list *ptrlist_add(struct ptr_list_head *head);
void ptrlist_destroy(struct ptr_list_head *head);
struct str2_list *str2list_add(struct str2_list_head *head);
bool str2list_copy(struct str2_list_head *to, const struct str2_list_head *from);
void str2list_destroy(struct str2_list_head *head);
struct func_list {
char *func;
uint64_t payload_type;
struct packet_range range_in, range_out;
struct ptr_list_head args;
struct str2_list_head args;
LIST_ENTRY(func_list) next;
};
LIST_HEAD(func_list_head, func_list);
struct func_list *funclist_add_tail(struct func_list_head *head, const char *func);
bool funclist_copy(struct func_list_head *to, const struct func_list_head *from);
void funclist_destroy(struct func_list_head *head);
@@ -96,6 +110,7 @@ struct hostlist_item {
LIST_HEAD(hostlist_collection_head, hostlist_item);
struct hostlist_item *hostlist_collection_add(struct hostlist_collection_head *head, struct hostlist_file *hfile);
void hostlist_collection_destroy(struct hostlist_collection_head *head);
bool hostlist_collection_copy(struct hostlist_collection_head *to, const struct hostlist_collection_head *from);
struct hostlist_item *hostlist_collection_search(struct hostlist_collection_head *head, const char *filename);
bool hostlist_collection_is_empty(const struct hostlist_collection_head *head);
@@ -158,6 +173,7 @@ struct ipset_item {
};
LIST_HEAD(ipset_collection_head, ipset_item);
struct ipset_item * ipset_collection_add(struct ipset_collection_head *head, struct ipset_file *hfile);
bool ipset_collection_copy(struct ipset_collection_head *to, const struct ipset_collection_head *from);
void ipset_collection_destroy(struct ipset_collection_head *head);
struct ipset_item *ipset_collection_search(struct ipset_collection_head *head, const char *filename);
bool ipset_collection_is_empty(const struct ipset_collection_head *head);

View File

@@ -48,7 +48,7 @@ bool l7_proto_match(t_l7proto l7proto, uint64_t filter_l7)
static const char *l7payload_name[] = {
"all","unknown","empty","known","http_req","http_reply","tls_client_hello","tls_server_hello","quic_initial",
"wireguard_initiation","wireguard_response","wireguard_cookie","wireguard_keepalive","wireguard_data",
"dht","discord_ip_discovery","stun_binding_req",
"dht","discord_ip_discovery","stun",
"xmpp_stream", "xmpp_starttls", "xmpp_proceed", "xmpp_features",
"dns_query", "dns_response",
"mtproto_initial"};
@@ -66,6 +66,35 @@ bool l7_payload_match(t_l7payload l7payload, uint64_t filter_l7p)
{
return filter_l7p==L7P_ALL || (filter_l7p & (1<<l7payload)) || (filter_l7p & (1<<L7P_KNOWN)) && l7payload>L7P_KNOWN && l7payload<L7P_LAST;
}
bool l7_payload_str_list(uint64_t l7p, char *buf, size_t size)
{
char *p;
const char *pstr;
size_t lstr;
t_l7payload pl;
if (!size) return false;
if (l7p==L7P_ALL)
{
if (size<4) return false;
memcpy(buf,"all",4);
return true;
}
for(pl=0, p=buf, *buf=0 ; pl<L7P_LAST ; pl++)
{
if (l7p & (1<<pl))
{
pstr = l7payload_str(pl);
lstr = strlen(pstr);
if (size < ((p!=buf) + lstr + 1)) return false;
if (p!=buf) *p++=','; // not first
memcpy(p,pstr,lstr);
p[lstr]=0;
p+=lstr;
}
}
return true;
}
static const char *posmarker_names[] = {"abs","host","endhost","sld","midsld","endsld","method","extlen","sniext"};
@@ -1380,11 +1409,11 @@ bool IsDiscordIpDiscoveryRequest(const uint8_t *data, size_t len)
!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 IsStunBindingRequest(const uint8_t *data, size_t len)
bool IsStunMessage(const uint8_t *data, size_t len)
{
return len>=20 && // header size
data[0]==0 && data[1]==1 &&
(data[3]&0b11)==0 && // length must be a multiple of 4
(data[0]&0xC0)==0 && // 2 most significant bits must be zeroes
(data[3]&3)==0 && // length must be a multiple of 4
ntohl(*(uint32_t*)(&data[4]))==0x2112A442 && // magic cookie
ntohs(*(uint16_t*)(&data[2]))==len-20;
}

View File

@@ -42,7 +42,7 @@ typedef enum {
L7P_WIREGUARD_DATA,
L7P_DHT,
L7P_DISCORD_IP_DISCOVERY,
L7P_STUN_BINDING_REQ,
L7P_STUN,
L7P_XMPP_STREAM,
L7P_XMPP_STARTTLS,
L7P_XMPP_PROCEED,
@@ -55,6 +55,7 @@ typedef enum {
t_l7payload l7payload_from_name(const char *name);
const char *l7payload_str(t_l7payload l7);
bool l7_payload_match(t_l7payload l7payload, uint64_t filter_l7p);
bool l7_payload_str_list(uint64_t l7p, char *buf, size_t size);
typedef enum {
PM_ABS=0,
@@ -152,7 +153,7 @@ bool IsWireguardKeepalive(const uint8_t *data, size_t len);
bool IsWireguardData(const uint8_t *data, size_t len);
bool IsDht(const uint8_t *data, size_t len);
bool IsDiscordIpDiscoveryRequest(const uint8_t *data, size_t len);
bool IsStunBindingRequest(const uint8_t *data, size_t len);
bool IsStunMessage(const uint8_t *data, size_t len);
bool IsMTProto(const uint8_t *data, size_t len);
#define QUIC_MAX_CID_LENGTH 20

99
uninstall_easy.sh Executable file
View File

@@ -0,0 +1,99 @@
#!/bin/sh
# automated script for easy uninstalling zapret
EXEDIR="$(dirname "$0")"
EXEDIR="$(cd "$EXEDIR"; pwd)"
ZAPRET_BASE=${ZAPRET_BASE:-"$EXEDIR"}
ZAPRET_RW=${ZAPRET_RW:-"$ZAPRET_BASE"}
ZAPRET_CONFIG=${ZAPRET_CONFIG:-"$ZAPRET_RW/config"}
ZAPRET_CONFIG_DEFAULT="$ZAPRET_BASE/config.default"
IPSET_DIR="$ZAPRET_BASE/ipset"
[ -f "$ZAPRET_CONFIG" ] || {
ZAPRET_CONFIG_DIR="$(dirname "$ZAPRET_CONFIG")"
[ -d "$ZAPRET_CONFIG_DIR" ] || mkdir -p "$ZAPRET_CONFIG_DIR"
cp "$ZAPRET_CONFIG_DEFAULT" "$ZAPRET_CONFIG"
}
. "$ZAPRET_CONFIG"
. "$ZAPRET_BASE/common/base.sh"
. "$ZAPRET_BASE/common/elevate.sh"
. "$ZAPRET_BASE/common/fwtype.sh"
. "$ZAPRET_BASE/common/dialog.sh"
. "$ZAPRET_BASE/common/ipt.sh"
. "$ZAPRET_BASE/common/nft.sh"
. "$ZAPRET_BASE/common/installer.sh"
remove_systemd()
{
clear_ipset
service_stop_systemd
service_remove_systemd
timer_remove_systemd
nft_del_table
crontab_del
}
remove_openrc()
{
clear_ipset
service_remove_openrc
nft_del_table
crontab_del
}
remove_linux()
{
INIT_SCRIPT_SRC="$EXEDIR/init.d/sysv/zapret2"
clear_ipset
echo \* executing sysv init stop
"$INIT_SCRIPT_SRC" stop
nft_del_table
crontab_del
echo
echo '!!! WARNING. YOUR UNINSTALL IS INCOMPLETE !!!'
echo 'you must manually remove zapret auto start from your system'
}
remove_openwrt()
{
OPENWRT_FW_INCLUDE=/etc/firewall.zapret2
clear_ipset
service_remove_sysv
remove_openwrt_firewall
remove_openwrt_iface_hook
nft_del_table
restart_openwrt_firewall
crontab_del
remove_extra_pkgs_openwrt
echo
echo to fully remove zapret : rm -r \"$ZAPRET_BASE\"
}
fix_sbin_path
check_system
require_root
case $SYSTEM in
systemd)
remove_systemd
;;
openrc)
remove_openrc
;;
linux)
remove_linux
;;
openwrt)
remove_openwrt
;;
esac
exitp 0