nftables support

This commit is contained in:
bol-van 2022-02-15 17:15:36 +03:00
parent b7aa3a878c
commit 95667733a6
35 changed files with 3099 additions and 2000 deletions

View File

@ -4,6 +4,12 @@ EXEDIR="$(dirname "$0")"
EXEDIR="$(cd "$EXEDIR"; pwd)"
ZAPRET_BASE="$EXEDIR"
[ -f "$ZAPRET_BASE/config" ] && . "$ZAPRET_BASE/config"
. "$ZAPRET_BASE/common/base.sh"
. "$ZAPRET_BASE/common/dialog.sh"
. "$ZAPRET_BASE/common/elevate.sh"
. "$ZAPRET_BASE/common/fwtype.sh"
[ -n "$QNUM" ] || QNUM=59780
[ -n "$TPPORT" ] || TPPORT=993
[ -n "$TPWS_UID" ] || TPWS_UID=1
@ -32,14 +38,6 @@ DNSCHECK_DIG2=/tmp/dig2.txt
DNSCHECK_DIGS=/tmp/digs.txt
exists()
{
which $1 >/dev/null 2>/dev/null
}
whichq()
{
which $1 2>/dev/null
}
killwait()
{
# $1 - signal (-9, -2, ...)
@ -59,53 +57,6 @@ exitp()
exit $1
}
read_yes_no()
{
# $1 - default (Y/N)
local A
read A
[ -z "$A" ] || ([ "$A" != "Y" ] && [ "$A" != "y" ] && [ "$A" != "N" ] && [ "$A" != "n" ]) && A=$1
[ "$A" = "Y" ] || [ "$A" = "y" ] || [ "$A" = "1" ]
}
ask_yes_no()
{
# $1 - default (Y/N or 0/1)
# $2 - text
local DEFAULT=$1
[ "$1" = "1" ] && DEFAULT=Y
[ "$1" = "0" ] && DEFAULT=N
[ -z "$DEFAULT" ] && DEFAULT=N
$ECHON "$2 (default : $DEFAULT) (Y/N) ? "
read_yes_no $DEFAULT
}
ask_yes_no_var()
{
# $1 - variable name for answer : 0/1
# $2 - text
local DEFAULT
eval DEFAULT="\$$1"
if ask_yes_no "$DEFAULT" "$2"; then
eval $1=1
else
eval $1=0
fi
}
require_root()
{
local exe
echo \* checking privileges
[ $(id -u) -ne "0" ] && {
echo root is required
exe="$EXEDIR/$(basename "$0")"
exists sudo && exec sudo "$exe"
exists su && exec su root -c "$exe"
echo su or sudo not found
exitp 2
}
}
IPT()
{
$IPTABLES -C "$@" >/dev/null 2>/dev/null || $IPTABLES -I "$@"
@ -165,29 +116,11 @@ check_system()
Linux)
PKTWS="$NFQWS"
PKTWSD=nfqws
local INIT=$(sed 's/\x0/\n/g' /proc/1/cmdline | head -n 1)
[ -L "$INIT" ] && INIT=$(readlink "$INIT")
INIT=$(basename "$INIT")
if [ -f "/etc/openwrt_release" ] && exists opkg && exists uci && [ "$INIT" = "procd" ] ; then
SUBSYS=openwrt
# new openwrt version abandon iptables and use nftables instead
# no iptables/ip6tables/ipt-kmods are installed by default
# fw4 firewall is used, fw3 is symbolic link to fw4
# no more firewall includes
# make sure nft was not just installed by user but all the system is based on fw4
if [ -x /sbin/fw4 ] && exists nft && [ "$FWTYPE" != "iptables" ] ; then
FWTYPE=nftables
else
FWTYPE=iptables
fi
else
# generic linux
if exists nft && [ "$FWTYPE" != "iptables" ]; then
FWTYPE=nftables
else
FWTYPE=iptables
fi
fi
linux_fwtype
[ "$FWTYPE" = iptables -o "$FWTYPE" = nftables ] || {
echo firewall type $FWTYPE not supported in $UNAME
exitp 5
}
;;
FreeBSD)
PKTWS="$DVTWS"
@ -403,10 +336,10 @@ pktws_ipt_prepare()
nftables)
nft add table inet $NFT_TABLE
[ "$IPV" = 6 -a -n "$IP6_DEFRAG_DISABLE" ] && {
nft "add chain inet $NFT_TABLE predefrag { type filter hook output priority -401; }"
nft "add chain inet $NFT_TABLE predefrag { type filter hook output priority -402; }"
nft "add rule inet $NFT_TABLE predefrag meta nfproto ipv${IPV} exthdr frag exists notrack"
}
nft "add chain inet $NFT_TABLE premangle { type filter hook output priority -151; }"
nft "add chain inet $NFT_TABLE premangle { type filter hook output priority -152; }"
nft "add rule inet $NFT_TABLE premangle meta nfproto ipv${IPV} tcp dport $1 mark and 0x40000000 != 0x40000000 queue num $QNUM bypass"
;;
ipfw)
@ -447,7 +380,7 @@ tpws_ipt_prepare()
nftables)
nft add table inet $NFT_TABLE
# -101 = pre dstnat
nft "add chain inet $NFT_TABLE output { type nat hook output priority -101; }"
nft "add chain inet $NFT_TABLE output { type nat hook output priority -102; }"
nft "add rule inet $NFT_TABLE output tcp dport $1 skuid != $TPWS_UID dnat ip${IPVV} to $LOCALHOST_IPT:$TPPORT"
;;
ipfw)
@ -801,9 +734,7 @@ configure_curl_opt()
linux_ipv6_defrag_can_be_disabled()
{
local V1=$(sed -nre 's/^Linux version ([0-9]+)\.[0-9]+.*$/\1/p' /proc/version)
local V2=$(sed -nre 's/^Linux version [0-9]+\.([0-9]+).*$/\1/p' /proc/version)
[ -n "$V1" -a -n "$V2" ] && [ "$V1" -gt 4 -o "$V1" = 4 -a "$V2" -ge 16 ]
linux_min_version 4 16
}
configure_defrag()
@ -836,7 +767,7 @@ configure_defrag()
fi
[ -n "$IP6_DEFRAG_DISABLE" ] && {
local ipexe="$(readlink -f $(whichq ip6tables))"
if [ "${ipexe#*nft}" != "$ipexe" ]; then
if contains "$ipexe" nft; then
echo "WARNING ! ipv6 ipfrag tests may have no effect if ip6tables-nft is used. current ip6tables point to : $ipexe"
else
echo "WARNING ! ipv6 ipfrag tests may have no effect if ip6table_raw kernel module is not loaded with parameter : raw_before_defrag=1"

135
common/base.sh Normal file
View File

@ -0,0 +1,135 @@
exists()
{
which "$1" >/dev/null 2>/dev/null
}
existf()
{
type "$1" >/dev/null 2>/dev/null
}
whichq()
{
which $1 2>/dev/null
}
exist_all()
{
while [ -n "$1" ]; do
exists "$1" || return 1
shift
done
return 0
}
on_off_function()
{
# $1 : function name on
# $2 : function name off
# $3 : 0 - off, 1 - on
local F="$1"
[ "$3" = "1" ] || F="$2"
shift
shift
shift
"$F" "$@"
}
contains()
{
# check if substring $2 contains in $1
[ "${1#*$2}" != "$1" ]
}
find_str_in_list()
{
[ -n "$1" ] && {
for v in $2; do
[ "$v" = "$1" ] && return 0
done
}
return 1
}
end_with_newline()
{
local c=$(tail -c 1)
[ "$c" = "" ]
}
make_separator_list()
{
# $1 - var name to receive result
# $2 - separator
# $3,$4,... - elements
local var="$1" sep="$2" i
shift; shift
while [ -n "$1" ]; do
if [ -n "$i" ] ; then
i=$i$sep$1
else
i=$1
fi
shift
done
eval $var=$i
}
make_comma_list()
{
# $1 - var name to receive result
# $2,$3,... - elements
local var="$1"
shift
make_separator_list $var , "$@"
}
is_linked_to_busybox()
{
local IFS F P
IFS=:
for path in $PATH; do
F=$path/$1
P="$(readlink $F)"
if [ -z "$P" ] && [ -x $F ] && [ ! -L $F ]; then return 1; fi
[ "${P%busybox*}" != "$P" ] && return
done
}
get_dir_inode()
{
local dir="$1"
[ -L "$dir" ] && dir=$(readlink "$dir")
ls -id "$dir" | awk '{print $1}'
}
linux_min_version()
{
# $1 - major ver
# $2 - minor ver
local V1=$(sed -nre 's/^Linux version ([0-9]+)\.[0-9]+.*$/\1/p' /proc/version)
local V2=$(sed -nre 's/^Linux version [0-9]+\.([0-9]+).*$/\1/p' /proc/version)
[ -n "$V1" -a -n "$V2" ] && [ "$V1" -gt "$1" -o "$V1" -eq "$1" -a "$V2" -ge "$2" ]
}
linux_get_subsys()
{
local INIT=$(sed 's/\x0/\n/g' /proc/1/cmdline | head -n 1)
[ -L "$INIT" ] && INIT=$(readlink "$INIT")
INIT=$(basename "$INIT")
if [ -f "/etc/openwrt_release" ] && [ "$INIT" = "procd" ] ; then
SUBSYS=openwrt
else
# generic linux
SUBSYS=
fi
}
openwrt_fw3()
{
[ ! -x /sbin/fw4 -a -x /sbin/fw3 ]
}
openwrt_fw4()
{
[ -x /sbin/fw4 ]
}
openwrt_fw3_integration()
{
[ "$FWTYPE" = iptables ] && openwrt_fw3
}
create_dev_stdin()
{
[ -e /dev/stdin ] || ln -s /proc/self/fd/0 /dev/stdin
}

58
common/dialog.sh Normal file
View File

@ -0,0 +1,58 @@
read_yes_no()
{
# $1 - default (Y/N)
local A
read A
[ -z "$A" ] || ([ "$A" != "Y" ] && [ "$A" != "y" ] && [ "$A" != "N" ] && [ "$A" != "n" ]) && A=$1
[ "$A" = "Y" ] || [ "$A" = "y" ] || [ "$A" = "1" ]
}
ask_yes_no()
{
# $1 - default (Y/N or 0/1)
# $2 - text
local DEFAULT=$1
[ "$1" = "1" ] && DEFAULT=Y
[ "$1" = "0" ] && DEFAULT=N
[ -z "$DEFAULT" ] && DEFAULT=N
printf "$2 (default : $DEFAULT) (Y/N) ? "
read_yes_no $DEFAULT
}
ask_yes_no_var()
{
# $1 - variable name for answer : 0/1
# $2 - text
local DEFAULT
eval DEFAULT="\$$1"
if ask_yes_no "$DEFAULT" "$2"; then
eval $1=1
else
eval $1=0
fi
}
ask_list()
{
# $1 - mode var
# $2 - space separated value list
# $3 - (optional) default value
local M_DEFAULT
eval M_DEFAULT="\$$1"
local M_ALL=$M_DEFAULT
local M=""
local m
[ -n "$3" ] && { find_str_in_list "$M_DEFAULT" "$2" || M_DEFAULT="$3" ;}
n=1
for m in $2; do
echo $n : $m
n=$(($n+1))
done
printf "your choice (default : $M_DEFAULT) : "
read m
[ -n "$m" ] && M=$(echo $2 | cut -d ' ' -f$m 2>/dev/null)
[ -z "$M" ] && M="$M_DEFAULT"
echo selected : $M
eval $1="\"$M\""
[ "$M" != "$M_OLD" ]
}

13
common/elevate.sh Normal file
View File

@ -0,0 +1,13 @@
require_root()
{
local exe
echo \* checking privileges
[ $(id -u) -ne "0" ] && {
echo root is required
exe="$EXEDIR/$(basename "$0")"
exists sudo && exec sudo "$exe"
exists su && exec su root -c "$exe"
echo su or sudo not found
exitp 2
}
}

64
common/fwtype.sh Normal file
View File

@ -0,0 +1,64 @@
linux_ipt_avail()
{
exists iptables && exists ip6tables
}
linux_maybe_iptables_fwtype()
{
linux_ipt_avail && FWTYPE=iptables
}
linux_nft_avail()
{
exists nft
}
linux_fwtype()
{
[ -n "$FWTYPE" ] && return
FWTYPE=unsupported
linux_get_subsys
if [ "$SUBSYS" = openwrt ] ; then
# linux kernel is new enough if fw4 is there
if [ -x /sbin/fw4 ] && linux_nft_avail ; then
FWTYPE=nftables
else
linux_maybe_iptables_fwtype
fi
else
SUBSYS=
# generic linux
# flowtable is implemented since kernel 4.16
if linux_nft_avail && linux_min_version 4 16; then
FWTYPE=nftables
else
linux_maybe_iptables_fwtype
fi
fi
export FWTYPE
}
get_fwtype()
{
[ -n "$FWTYPE" ] && return
local UNAME="$(uname)"
case "$UNAME" in
Linux)
linux_fwtype
;;
FreeBSD)
if exists ipfw ; then
FWTYPE=ipfw
else
FWTYPE=unsupported
fi
;;
*)
FWTYPE=unsupported
;;
esac
export FWTYPE
}

462
common/installer.sh Normal file
View File

@ -0,0 +1,462 @@
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/zapret
exitp()
{
echo
echo press enter to continue
read A
exit $1
}
parse_var_checked()
{
# $1 - file name
# $2 - var name
local sed="sed -nre s/^[[:space:]]*$2=[\\\"|\']?([^\\\"|\']*)[\\\"|\']?/\1/p"
local v="$($sed <"$1" | tail -n 1)"
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 vi; do
exists "$e" && {
ed="$e"
break
}
done
}
[ -n "$ed" ] && "$ed" "$1"
}
edit_vars()
{
# $1,$2,... - var names
local n=1 var v tmp="/tmp/zvars"
rm -f "$tmp"
while [ 1=1 ]; do
eval var="\$$n"
[ -n "$var" ] || break
eval v="\$$var"
echo $var=\"$v\" >>"$tmp"
n=$(($n+1))
done
edit_file "$tmp" && parse_vars_checked "$tmp" "$@"
rm -f "$tmp"
}
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()
{
echo \* checking system
SYSTEM=""
SYSTEMCTL=$(whichq systemctl)
get_fwtype
OPENWRT_FW3=
local info
local 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
elif [ -f "/etc/openwrt_release" ] && exists opkg && exists uci && [ "$INIT" = "procd" ] ; then
{
SYSTEM=openwrt
if openwrt_fw3 ; then
OPENWRT_FW3=1
info="openwrt firewall uses 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
info="openwrt firewall uses fw4. flow offloading requires nftables."
fi
}
elif openrc_test; then
SYSTEM=openrc
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.txt for manual setup info.
if ask_yes_no N "do you want to continue"; then
SYSTEM=linux
else
exitp 5
fi
fi
elif [ "$UNAME" = "Darwin" ]; then
SYSTEM=macos
else
echo easy installer only supports Linux and MacOS. check readme.txt for supported systems and manual setup info.
exitp 5
fi
echo system is based on $SYSTEM
[ -n "$info" ] && echo $info
}
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 zapret service
"$SYSTEMCTL" start zapret || {
echo could not start zapret service
exitp 30
}
}
service_stop_systemd()
{
echo \* stopping zapret service
"$SYSTEMCTL" daemon-reload
"$SYSTEMCTL" disable zapret
"$SYSTEMCTL" stop zapret
}
service_remove_systemd()
{
echo \* removing zapret service
rm -f "$SYSTEMD_SYSTEM_DIR/zapret.service"
"$SYSTEMCTL" daemon-reload
}
timer_remove_systemd()
{
echo \* removing zapret-list-update timer
"$SYSTEMCTL" daemon-reload
"$SYSTEMCTL" disable zapret-list-update.timer
"$SYSTEMCTL" stop zapret-list-update.timer
rm -f "$SYSTEMD_SYSTEM_DIR/zapret-list-update.service" "$SYSTEMD_SYSTEM_DIR/zapret-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 zapret
}
ln -fs "$INIT_SCRIPT_SRC" "$INIT_SCRIPT"
[ "$1" != "0" ] && rc-update add zapret
}
service_remove_openrc()
{
echo \* removing zapret service
[ -x "$INIT_SCRIPT" ] && {
rc-update del zapret
"$INIT_SCRIPT" stop
}
rm -f "$INIT_SCRIPT"
}
service_start_sysv()
{
[ -x "$INIT_SCRIPT" ] && {
echo \* starting zapret service
"$INIT_SCRIPT" start || {
echo could not start zapret service
exitp 30
}
}
}
service_stop_sysv()
{
[ -x "$INIT_SCRIPT" ] && {
echo \* stopping zapret service
"$INIT_SCRIPT" stop
}
}
service_remove_sysv()
{
echo \* removing zapret 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 "$(opkg list $1)" ]
}
check_package_openwrt()
{
[ -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)" ]
}
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/??-zapret
}
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
fw3 -q restart || {
echo could not restart firewall
exitp 30
}
}
remove_openwrt_firewall()
{
echo \* removing firewall script
openwrt_fw_section_del
# from old zapret versions. now we use single include
openwrt_fw_section_del 6
}
clear_ipset()
{
echo "* clearing ipset(s)"
# free some RAM
"$IPSET_DIR/create_ipset.sh" clear
}
service_install_macos()
{
echo \* installing zapret service
ln -fs "$ZAPRET_BASE/init.d/macos/zapret.plist" /Library/LaunchDaemons
}
service_start_macos()
{
echo \* starting zapret service
"$INIT_SCRIPT_SRC" start
}
service_stop_macos()
{
echo \* stopping zapret service
"$INIT_SCRIPT_SRC" stop
}
service_remove_macos()
{
echo \* removing zapret service
rm -f /Library/LaunchDaemons/zapret.plist
zapret_stop_daemons
}
remove_macos_firewall()
{
echo \* removing zapret PF hooks
pf_anchors_clear
pf_anchors_del
pf_anchor_root_del
pf_anchor_root_reload
}

172
common/ipt.sh Normal file
View File

@ -0,0 +1,172 @@
ipt()
{
iptables -C "$@" >/dev/null 2>/dev/null || iptables -I "$@"
}
ipta()
{
iptables -C "$@" >/dev/null 2>/dev/null || iptables -A "$@"
}
ipt_del()
{
iptables -C "$@" >/dev/null 2>/dev/null && iptables -D "$@"
}
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_port_target()
{
# $1 - var name of iptables filter
local f
if [ "$MODE_HTTP" = "1" ] && [ "$MODE_HTTPS" = "1" ]; then
f="-m multiport --dports 80,443"
elif [ "$MODE_HTTPS" = "1" ]; then
f="--dport 443"
elif [ "$MODE_HTTP" = "1" ]; then
f="--dport 80"
else
echo WARNING !!! HTTP and HTTPS are both disabled
fi
eval $1="\"\$$1 $f\""
}
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
}
zapret_do_firewall_ipt()
{
# $1 - 1 - add, 0 - del
if [ "$1" = 1 ]; then
echo Applying iptables
else
echo Clearing iptables
fi
local mode="${MODE_OVERRIDE:-$MODE}"
[ "$mode" = "tpws-socks" ] && return 0
local first_packet_only="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:4"
local desync="-m mark ! --mark $DESYNC_MARK/$DESYNC_MARK"
local f4 f6 qn qns qn6 qns6
# always create ipsets. ip_exclude ipset is required
[ "$1" = 1 ] && create_ipset no-update
case "$mode" in
tpws)
if [ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ]; then
echo both http and https are disabled. not applying redirection.
else
filter_apply_port_target f4
f6=$f4
filter_apply_ipset_target f4 f6
fw_tpws $1 "$f4" "$f6" $TPPORT
fi
;;
nfqws)
# quite complex but we need to minimize nfqws processes to save RAM
get_nfqws_qnums qn qns qn6 qns6
if [ "$MODE_HTTP_KEEPALIVE" != "1" ] && [ -n "$qn" ] && [ "$qn" = "$qns" ]; then
filter_apply_port_target f4
f4="$f4 $first_packet_only"
filter_apply_ipset_target4 f4
fw_nfqws_post4 $1 "$f4 $desync" $qn
else
if [ -n "$qn" ]; then
f4="--dport 80"
[ "$MODE_HTTP_KEEPALIVE" = "1" ] || f4="$f4 $first_packet_only"
filter_apply_ipset_target4 f4
fw_nfqws_post4 $1 "$f4 $desync" $qn
fi
if [ -n "$qns" ]; then
f4="--dport 443 $first_packet_only"
filter_apply_ipset_target4 f4
fw_nfqws_post4 $1 "$f4 $desync" $qns
fi
fi
if [ "$MODE_HTTP_KEEPALIVE" != "1" ] && [ -n "$qn6" ] && [ "$qn6" = "$qns6" ]; then
filter_apply_port_target f6
f6="$f6 $first_packet_only"
filter_apply_ipset_target6 f6
fw_nfqws_post6 $1 "$f6 $desync" $qn6
else
if [ -n "$qn6" ]; then
f6="--dport 80"
[ "$MODE_HTTP_KEEPALIVE" = "1" ] || f6="$f6 $first_packet_only"
filter_apply_ipset_target6 f6
fw_nfqws_post6 $1 "$f6 $desync" $qn6
fi
if [ -n "$qns6" ]; then
f6="--dport 443 $first_packet_only"
filter_apply_ipset_target6 f6
fw_nfqws_post6 $1 "$f6 $desync" $qns6
fi
fi
;;
custom)
existf zapret_custom_firewall && zapret_custom_firewall $1
;;
esac
if [ "$1" = 1 ] ; then
existf flow_offloading_exempt && flow_offloading_exempt
else
existf flow_offloading_unexempt && flow_offloading_unexempt
unprepare_tpws_fw
fi
return 0
}

23
common/linux_fw.sh Normal file
View File

@ -0,0 +1,23 @@
zapret_do_firewall()
{
linux_fwtype
case "$FWTYPE" in
iptables)
zapret_do_firewall_ipt "$@"
;;
nftables)
zapret_do_firewall_nft "$@"
;;
esac
return 0
}
zapret_apply_firewall()
{
zapret_do_firewall 1 "$@"
}
zapret_unapply_firewall()
{
zapret_do_firewall 0 "$@"
}

51
common/linux_iphelper.sh Normal file
View File

@ -0,0 +1,51 @@
# there's no route_localnet for ipv6
# the best we can is to route to link local of the incoming interface
# OUTPUT - can DNAT to ::1
# PREROUTING - can't DNAT to ::1. can DNAT to link local of -i interface or to any global addr
# not a good idea to expose tpws to the world (bind to ::)
get_ipv6_linklocal()
{
# $1 - interface name. if empty - any interface
if exists ip ; then
local dev
[ -n "$1" ] && dev="dev $1"
ip addr show $dev | sed -e 's/^.*inet6 \([^ ]*\)\/[0-9]* scope link.*$/\1/;t;d' | head -n 1
else
ifconfig $1 | sed -re 's/^.*inet6 addr: ([^ ]*)\/[0-9]* Scope:Link.*$/\1/;t;d' | head -n 1
fi
}
get_ipv6_global()
{
# $1 - interface name. if empty - any interface
if exists ip ; then
local dev
[ -n "$1" ] && dev="dev $1"
ip addr show $dev | sed -e 's/^.*inet6 \([^ ]*\)\/[0-9]* scope global.*$/\1/;t;d' | head -n 1
else
ifconfig $1 | sed -re 's/^.*inet6 addr: ([^ ]*)\/[0-9]* Scope:Global.*$/\1/;t;d' | head -n 1
fi
}
iface_is_up()
{
# $1 - interface name
[ -f /sys/class/net/$1/operstate ] || return
local state
read state </sys/class/net/$1/operstate
[ "$state" != "down" ]
}
wait_ifup()
{
# $1 - interface name
local ct=0
while
iface_is_up $1 && return
[ "$ct" -ge "$IFUP_WAIT_SEC" ] && break
echo waiting for ifup of $1 for another $(($IFUP_WAIT_SEC - $ct)) seconds ...
ct=$(($ct+1))
sleep 1
do :; done
false
}

348
common/nft.sh Normal file
View File

@ -0,0 +1,348 @@
[ -n "$ZAPRET_NFT_TABLE" ] || ZAPRET_NFT_TABLE=zapret
# required for : nft -f -
create_dev_stdin
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_set_exists()
{
# $1 - set name
nft -t list set inet $ZAPRET_NFT_TABLE $1 2>/dev/null >/dev/null
}
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
}
nft_del_chains()
{
nft_del_all_chains_from_table "inet $ZAPRET_NFT_TABLE"
}
nft_create_chains()
{
cat << EOF | nft -f -
add chain inet $ZAPRET_NFT_TABLE dnat_output { type nat hook output priority -101; }
flush chain inet $ZAPRET_NFT_TABLE dnat_output
add chain inet $ZAPRET_NFT_TABLE dnat_pre { type nat hook prerouting priority -101; }
flush chain inet $ZAPRET_NFT_TABLE dnat_pre
add chain inet $ZAPRET_NFT_TABLE forward { type filter hook forward priority -1; }
flush chain inet $ZAPRET_NFT_TABLE forward
add chain inet $ZAPRET_NFT_TABLE input { type filter hook input priority -1; }
flush chain inet $ZAPRET_NFT_TABLE input
add chain inet $ZAPRET_NFT_TABLE flow_offload
flush chain inet $ZAPRET_NFT_TABLE flow_offload
add chain inet $ZAPRET_NFT_TABLE localnet_protect
flush chain inet $ZAPRET_NFT_TABLE localnet_protect
add rule inet $ZAPRET_NFT_TABLE localnet_protect ip daddr $TPWS_LOCALHOST4 return comment "route_localnet allow access to tpws"
add rule inet $ZAPRET_NFT_TABLE localnet_protect ip daddr 127.0.0.0/8 drop comment "route_localnet remote access protection"
add rule inet $ZAPRET_NFT_TABLE input iifname != lo jump localnet_protect
add chain inet $ZAPRET_NFT_TABLE postrouting { type filter hook postrouting priority -151; }
flush chain inet $ZAPRET_NFT_TABLE postrouting
add set inet $ZAPRET_NFT_TABLE lanif { type ifname; }
add set inet $ZAPRET_NFT_TABLE wanif { type ifname; }
add set inet $ZAPRET_NFT_TABLE wanif6 { type ifname; }
EOF
}
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
shift
make_comma_list devices "$@"
[ -n "$devices" ] && devices="devices={$devices};"
[ -n "$flags" ] && flags="flags $flags;"
nft add flowtable inet $ZAPRET_NFT_TABLE ft "{ hook ingress priority -1; $flags $devices }"
}
nft_flush_ifsets()
{
cat << EOF | nft -f - 2>/dev/null
flush set inet $ZAPRET_NFT_TABLE lanif
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 lanif
nft list set inet $ZAPRET_NFT_TABLE wanif
nft list set inet $ZAPRET_NFT_TABLE wanif6
nft list flowtable inet $ZAPRET_NFT_TABLE ft
}
nft_create_firewall()
{
nft_create_table
nft_del_flowtable
nft_create_chains
}
nft_del_firewall()
{
nft_del_chains
nft_del_flowtable
nft_flush_ifsets
}
nft_add_rule()
{
# $1 - chain
# $2,$3,... - rule(s)
local chain="$1"
shift
nft add rule inet $ZAPRET_NFT_TABLE $chain "$@"
}
nft_add_set_elements()
{
# $1 - set name
# $2,$3,... - element(s)
local set="$1" elements
shift
make_comma_list elements "$@"
[ -z "$elements" ] || nft add element inet $ZAPRET_NFT_TABLE $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_clean_nfqws_rule()
{
echo "$@" | sed -e "s/mark and $DESYNC_MARK == 0//g"
}
nft_add_nfqws_flow_exempt_rule()
{
# $1 - rule (must be all filters in one var)
nft_add_rule flow_offload $(nft_clean_nfqws_rule $1) return comment \"direct flow offloading exemption\"
nft_add_rule flow_offload $(nft_reverse_nfqws_rule $1) return comment \"reverse flow offloading exemption\"
}
nft_hw_offload_supported()
{
# $1,$2,... - interface names
local devices res=1
make_comma_list devices "$@"
[ -n "$devices" ] && devices="devices={$devices};"
nft add table ${ZAPRET_NFT_TABLE}_test && nft add flowtable ${ZAPRET_NFT_TABLE}_test ft "{ flags offload; $devices }" 2>/dev/null && res=0
nft delete table ${ZAPRET_NFT_TABLE}_test 2>/dev/null
return $res
}
nft_apply_flow_offloading()
{
# ft can be absent
nft_add_rule flow_offload meta l4proto "{ tcp, udp }" flow add @ft 2>/dev/null && nft_add_rule forward jump flow_offload
}
nft_filter_apply_port_target()
{
# $1 - var name of nftables filter
local f
if [ "$MODE_HTTP" = "1" ] && [ "$MODE_HTTPS" = "1" ]; then
f="tcp dport {80,443}"
elif [ "$MODE_HTTPS" = "1" ]; then
f="tcp dport 443"
elif [ "$MODE_HTTP" = "1" ]; then
f="tcp dport 80"
else
echo WARNING !!! HTTP and HTTPS are both disabled
fi
eval $1="\"\$$1 $f\""
}
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_only()
{
linux_fwtype
case "$FWTYPE" in
nftables)
"$@"
;;
esac
}
zapret_reload_ifsets()
{
nft_only nft_create_table ; nft_fill_ifsets
return 0
}
zapret_list_ifsets()
{
nft_only nft_list_ifsets
return 0
}
zapret_list_table()
{
nft_only nft_list_table
return 0
}
zapret_apply_firewall_nft()
{
echo Applying nftables
local mode="${MODE_OVERRIDE:-$MODE}"
[ "$mode" = "tpws-socks" ] && return 0
local first_packet_only="ct original packets 1-4"
local desync="mark and $DESYNC_MARK == 0"
local f4 f6 qn qns qn6 qns6
create_ipset no-update
nft_create_firewall
nft_fill_ifsets
case "$mode" in
tpws)
if [ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ]; then
echo both http and https are disabled. not applying redirection.
else
nft_filter_apply_port_target f4
f6=$f4
nft_filter_apply_ipset_target f4 f6
nft_fw_tpws "$f4" "$f6" $TPPORT
fi
;;
nfqws)
# quite complex but we need to minimize nfqws processes to save RAM
get_nfqws_qnums qn qns qn6 qns6
if [ "$MODE_HTTP_KEEPALIVE" != "1" ] && [ -n "$qn" ] && [ "$qn" = "$qns" ]; then
nft_filter_apply_port_target f4
f4="$f4 $first_packet_only"
nft_filter_apply_ipset_target4 f4
nft_fw_nfqws_post4 "$f4 $desync" $qn
else
if [ -n "$qn" ]; then
f4="tcp dport 80"
[ "$MODE_HTTP_KEEPALIVE" = "1" ] || f4="$f4 $first_packet_only"
nft_filter_apply_ipset_target4 f4
nft_fw_nfqws_post4 "$f4 $desync" $qn
fi
if [ -n "$qns" ]; then
f4="tcp dport 443 $first_packet_only"
nft_filter_apply_ipset_target4 f4
nft_fw_nfqws_post4 "$f4 $desync" $qns
fi
fi
if [ "$MODE_HTTP_KEEPALIVE" != "1" ] && [ -n "$qn6" ] && [ "$qn6" = "$qns6" ]; then
nft_filter_apply_port_target f6
f6="$f6 $first_packet_only"
nft_filter_apply_ipset_target6 f6
nft_fw_nfqws_post6 "$f6 $desync" $qn6
else
if [ -n "$qn6" ]; then
f6="tcp dport 80"
[ "$MODE_HTTP_KEEPALIVE" = "1" ] || f6="$f6 $first_packet_only"
nft_filter_apply_ipset_target6 f6
nft_fw_nfqws_post6 "$f6 $desync" $qn6
fi
if [ -n "$qns6" ]; then
f6="tcp dport 443 $first_packet_only"
nft_filter_apply_ipset_target6 f6
nft_fw_nfqws_post6 "$f6 $desync" $qns6
fi
fi
;;
custom)
existf zapret_custom_firewall_nft && zapret_custom_firewall_nft
;;
esac
[ "$FLOWOFFLOAD" = 'software' -o "$FLOWOFFLOAD" = 'hardware' ] && nft_apply_flow_offloading
return 0
}
zapret_unapply_firewall_nft()
{
echo Clearing nftables
unprepare_route_localnet
nft_del_firewall
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
}

262
common/pf.sh Normal file
View File

@ -0,0 +1,262 @@
PF_MAIN="/etc/pf.conf"
PF_ANCHOR_DIR=/etc/pf.anchors
PF_ANCHOR_ZAPRET="$PF_ANCHOR_DIR/zapret"
PF_ANCHOR_ZAPRET_V4="$PF_ANCHOR_DIR/zapret-v4"
PF_ANCHOR_ZAPRET_V6="$PF_ANCHOR_DIR/zapret-v6"
pf_anchor_root_reload()
{
echo reloading PF root anchor
pfctl -qf "$PF_MAIN"
}
pf_anchor_root()
{
local patch
[ -f "$PF_MAIN" ] && {
grep -q '^rdr-anchor "zapret"$' "$PF_MAIN" || {
echo patching rdr-anchor in $PF_MAIN
patch=1
sed -i '' -e '/^rdr-anchor "com\.apple\/\*"$/i \
rdr-anchor "zapret"
' $PF_MAIN
}
grep -q '^anchor "zapret"$' "$PF_MAIN" || {
echo patching anchor in $PF_MAIN
patch=1
sed -i '' -e '/^anchor "com\.apple\/\*"$/i \
anchor "zapret"
' $PF_MAIN
}
grep -q "^set limit table-entries" "$PF_MAIN" || {
echo patching table-entries limit
patch=1
sed -i '' -e '/^scrub-anchor "com\.apple\/\*"$/i \
set limit table-entries 5000000
' $PF_MAIN
}
grep -q '^anchor "zapret"$' "$PF_MAIN" &&
grep -q '^rdr-anchor "zapret"$' "$PF_MAIN" &&
grep -q '^set limit table-entries' "$PF_MAIN" && {
if [ -n "$patch" ]; then
echo successfully patched $PF_MAIN
pf_anchor_root_reload
else
echo successfully checked zapret anchors in $PF_MAIN
fi
return 0
}
}
echo ----------------------------------
echo Automatic $PF_MAIN patching failed. You must apply root anchors manually in your PF config.
echo rdr-anchor \"zapret\"
echo anchor \"zapret\"
echo ----------------------------------
return 1
}
pf_anchor_root_del()
{
sed -i '' -e '/^anchor "zapret"$/d' -e '/^rdr-anchor "zapret"$/d' -e '/^set limit table-entries/d' "$PF_MAIN"
}
pf_anchor_zapret()
{
[ "$DISABLE_IPV4" = "1" ] || {
if [ -f "$ZIPLIST_EXCLUDE" ]; then
echo "table <nozapret> persist file \"$ZIPLIST_EXCLUDE\""
else
echo "table <nozapret> persist"
fi
}
[ "$DISABLE_IPV6" = "1" ] || {
if [ -f "$ZIPLIST_EXCLUDE6" ]; then
echo "table <nozapret6> persist file \"$ZIPLIST_EXCLUDE6\""
else
echo "table <nozapret6> persist"
fi
}
[ "$DISABLE_IPV4" = "1" ] || echo "rdr-anchor \"/zapret-v4\" inet to !<nozapret>"
[ "$DISABLE_IPV6" = "1" ] || echo "rdr-anchor \"/zapret-v6\" inet6 to !<nozapret6>"
[ "$DISABLE_IPV4" = "1" ] || echo "anchor \"/zapret-v4\" inet to !<nozapret>"
[ "$DISABLE_IPV6" = "1" ] || echo "anchor \"/zapret-v6\" inet6 to !<nozapret6>"
}
pf_anchor_zapret_tables()
{
# $1 - variable to receive applied table names
# $2/$3 $4/$5 ... table_name/table_file
local tblv=$1
local _tbl
shift
[ "$MODE_FILTER" = "ipset" ] &&
{
while [ -n "$1" ] && [ -n "$2" ] ; do
[ -f "$2" ] && {
echo "table <$1> file \"$2\""
_tbl="$_tbl<$1> "
}
shift
shift
done
}
[ -n "$_tbl" ] || _tbl="any"
eval $tblv="\"\$_tbl\""
}
pf_anchor_port_target()
{
if [ "$MODE_HTTP" = "1" ] && [ "$MODE_HTTPS" = "1" ]; then
echo "{80,443}"
elif [ "$MODE_HTTPS" = "1" ]; then
echo "443"
elif [ "$MODE_HTTP" = "1" ]; then
echo "80"
fi
}
pf_anchor_zapret_v4_tpws()
{
# $1 - port
local rule port=$(pf_anchor_port_target)
for lan in $IFACE_LAN; do
for t in $tbl; do
echo "rdr on $lan inet proto tcp from any to $t port $port -> 127.0.0.1 port $1"
done
done
echo "rdr on lo0 inet proto tcp from !127.0.0.0/8 to any port $port -> 127.0.0.1 port $1"
for t in $tbl; do
rule="route-to (lo0 127.0.0.1) inet proto tcp from !127.0.0.0/8 to $t port $port user { >root }"
if [ -n "$IFACE_WAN" ] ; then
for wan in $IFACE_WAN; do
echo "pass out on $wan $rule"
done
else
echo "pass out $rule"
fi
done
}
pf_anchor_zapret_v4()
{
local tbl port
[ "$DISABLE_IPV4" = "1" ] || {
case $MODE in
tpws)
[ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ] && return
pf_anchor_zapret_tables tbl zapret-user "$ZIPLIST_USER" zapret "$ZIPLIST"
pf_anchor_zapret_v4_tpws $TPPORT
;;
custom)
pf_anchor_zapret_tables tbl zapret-user "$ZIPLIST_USER" zapret "$ZIPLIST"
existf zapret_custom_firewall_v4 && zapret_custom_firewall_v4
;;
esac
}
}
pf_anchor_zapret_v6_tpws()
{
# $1 - port
local LL_LAN rule port=$(pf_anchor_port_target)
# LAN link local is only for router
for lan in $IFACE_LAN; do
LL_LAN=$(get_ipv6_linklocal $lan)
[ -n "$LL_LAN" ] && {
for t in $tbl; do
echo "rdr on $lan inet6 proto tcp from any to $t port $port -> $LL_LAN port $1"
done
}
done
echo "rdr on lo0 inet6 proto tcp from !::1 to any port $port -> fe80::1 port $1"
for t in $tbl; do
rule="route-to (lo0 fe80::1) inet6 proto tcp from !::1 to $t port $port user { >root }"
if [ -n "$IFACE_WAN" ] ; then
for wan in $IFACE_WAN; do
echo "pass out on $wan $rule"
done
else
echo "pass out $rule"
fi
done
}
pf_anchor_zapret_v6()
{
local tbl port
[ "$DISABLE_IPV6" = "1" ] || {
case $MODE in
tpws)
[ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ] && return
pf_anchor_zapret_tables tbl zapret6-user "$ZIPLIST_USER6" zapret6 "$ZIPLIST6"
pf_anchor_zapret_v6_tpws $TPPORT
;;
custom)
pf_anchor_zapret_tables tbl zapret6-user "$ZIPLIST_USER6" zapret6 "$ZIPLIST6"
existf zapret_custom_firewall_v6 && zapret_custom_firewall_v6
;;
esac
}
}
pf_anchors_create()
{
wait_lan_ll
pf_anchor_zapret >"$PF_ANCHOR_ZAPRET"
pf_anchor_zapret_v4 >"$PF_ANCHOR_ZAPRET_V4"
pf_anchor_zapret_v6 >"$PF_ANCHOR_ZAPRET_V6"
}
pf_anchors_del()
{
rm -f "$PF_ANCHOR_ZAPRET" "$PF_ANCHOR_ZAPRET_V4" "$PF_ANCHOR_ZAPRET_V6"
}
pf_anchors_load()
{
echo loading zapret anchor from "$PF_ANCHOR_ZAPRET"
pfctl -qa zapret -f "$PF_ANCHOR_ZAPRET" || {
echo error loading zapret anchor
return 1
}
if [ "$DISABLE_IPV4" = "1" ]; then
echo clearing zapret-v4 anchor
pfctl -qa zapret-v4 -F all 2>/dev/null
else
echo loading zapret-v4 anchor from "$PF_ANCHOR_ZAPRET_V4"
pfctl -qa zapret-v4 -f "$PF_ANCHOR_ZAPRET_V4" || {
echo error loading zapret-v4 anchor
return 1
}
fi
if [ "$DISABLE_IPV6" = "1" ]; then
echo clearing zapret-v6 anchor
pfctl -qa zapret-v6 -F all 2>/dev/null
else
echo loading zapret-v6 anchor from "$PF_ANCHOR_ZAPRET_V6"
pfctl -qa zapret-v6 -f "$PF_ANCHOR_ZAPRET_V6" || {
echo error loading zapret-v6 anchor
return 1
}
fi
echo successfully loaded PF anchors
return 0
}
pf_anchors_clear()
{
echo clearing zapret anchors
pfctl -qa zapret-v4 -F all 2>/dev/null
pfctl -qa zapret-v6 -F all 2>/dev/null
pfctl -qa zapret -F all 2>/dev/null
}
pf_enable()
{
echo enabling PF
pfctl -qe
}
pf_table_reload()
{
echo reloading zapret tables
[ "$DISABLE_IPV4" = "1" ] || pfctl -qTl -a zapret-v4 -f "$PF_ANCHOR_ZAPRET_V4"
[ "$DISABLE_IPV6" = "1" ] || pfctl -qTl -a zapret-v6 -f "$PF_ANCHOR_ZAPRET_V6"
pfctl -qTl -a zapret -f "$PF_ANCHOR_ZAPRET"
}

45
common/queue.sh Normal file
View File

@ -0,0 +1,45 @@
get_nfqws_qnums()
{
# $1 - var name for ipv4 http
# $2 - var name for ipv4 https
# $3 - var name for ipv6 http
# $4 - var name for ipv6 https
local _qn _qns _qn6 _qns6
[ "$DISABLE_IPV4" = "1" ] || {
_qn=$QNUM
_qns=$_qn
[ "$NFQWS_OPT_DESYNC_HTTP" = "$NFQWS_OPT_DESYNC_HTTPS" ] || _qns=$(($QNUM+1))
}
[ "$DISABLE_IPV6" = "1" ] || {
_qn6=$(($QNUM+2))
_qns6=$(($QNUM+3))
[ "$DISABLE_IPV4" = "1" ] || {
if [ "$NFQWS_OPT_DESYNC_HTTP6" = "$NFQWS_OPT_DESYNC_HTTP" ]; then
_qn6=$_qn;
elif [ "$NFQWS_OPT_DESYNC_HTTP6" = "$NFQWS_OPT_DESYNC_HTTPS" ]; then
_qn6=$_qns;
fi
if [ "$NFQWS_OPT_DESYNC_HTTPS6" = "$NFQWS_OPT_DESYNC_HTTP" ]; then
_qns6=$_qn;
elif [ "$NFQWS_OPT_DESYNC_HTTPS6" = "$NFQWS_OPT_DESYNC_HTTPS" ]; then
_qns6=$_qns;
fi
}
[ "$NFQWS_OPT_DESYNC_HTTPS6" = "$NFQWS_OPT_DESYNC_HTTP6" ] && _qns6=$_qn6;
}
if [ "$MODE_HTTP" = 1 ]; then
eval $1=$_qn
eval $3=$_qn6
else
eval $1=
eval $3=
fi
if [ "$MODE_HTTPS" = 1 ]; then
eval $2=$_qns
eval $4=$_qns6
else
eval $2=
eval $4=
fi
}

12
config
View File

@ -4,10 +4,15 @@
# can help in case /tmp has not enough space
#TMPDIR=/opt/zapret/tmp
# override firewall type : iptables,nftables,ipfw
#FWTYPE=iptables
# options for ipsets
# maximum number of elements in sets. also used for nft sets
SET_MAXELEM=262144
# too low hashsize can cause memory allocation errors on low RAM systems , even if RAM is enough
# too large hashsize will waste lots of RAM
IPSET_OPT="hashsize 262144 maxelem 2097152"
IPSET_OPT="hashsize 262144 maxelem $SET_MAXELEM"
# options for ip2net. "-4" or "-6" auto added by ipset create script
IP2NET_OPT4="--prefix-length=22-30 --v4-threshold=3/4"
@ -65,9 +70,8 @@ FLOWOFFLOAD=donttouch
#IFACE_LAN=eth0
#IFACE_WAN=eth1
# should init scripts apply firewall rules ?
# set to 0 if firewall control system is present
# openwrt uses fw3 firewall , init never touch fw
# should start/stop command of init scripts apply firewall rules ?
# not applicable to older openwrt with fw3 firewall
INIT_APPLY_FW=1
# do not work with ipv4

View File

@ -218,3 +218,7 @@ nfqws: ipfrag
v45
nfqws: hop-by-hop ipv6 desync and fooling
v46
big startup script refactoring to support nftables and new openwrt snapshot builds with firewall4

92
docs/nftables_notes.txt Normal file
View File

@ -0,0 +1,92 @@
nftables - это технология, пришедшая на замену iptables.
В ней собрали все, что относилось к различным iptables. А их немало. iptables, ip6tables, ebtables, arptables, ipset.
Весь код из разрозненных, но похожих компонент, собрали в одно целое с единым синтаксисом.
Добавили различные конструкции языка, позволяющие писать правила более лаконично, не повторяя одни и те же команды с небольшими различиями.
На nftables можно сделать почти все, что можно было сделать на iptables. Есть то, что можно сделать на nftables, но нельзя на iptables.
Удобно, красиво.
К сожалению, не обошлось и без боли. 10 лет развития nftables казалось бы должны были вылизать все. Но не тут то было.
Главная боль N1. Очень серьезная, актуальная для openwrt, и решения не видно.
ipset-ы позволяли загонять пересекающиеся интервалы ip адресов или подсетей.
nftables sets это не позволяют. Любое пересечение вызывает ошибку.
Есть auto-merge, но это работает только в user mode в процессе nft, при условии, что весь блок адресов загоняется одной командой
и нет пересечений с уже имеющимся контентом в set.
Это не было бы критической проблемой, поскольку скрипты zapret и так загоняют ipset целиком.
Проблема в катастрофическом расходе памяти при операции загона больших интервальных листов, то есть с подсетями и диапазонами.
Чтобы загнать 100000 ipv4 записей, едва хватает 300 Mb памяти устройства.
При успехе операции в ядре список столько не занимает, но суть дела это не меняет.
Для традиционных linux систем это не проблема, но почти все роутеры загнутся.
Приемлемого решения не просматривается.
Сделать записи непересекающимися в листах - задача непростая. Потребуется переписать алгоритм auto-merge из nft,
но с пониженным расходом памяти.
Загонять записи по одному отдельными вызовами nft, игнорируя ошибки, займет вечность.
Загонять блоком отдельных команд, игнорируя ошибки, - nft такого не умеет. Похоже, при любой ошибке происходит откат всего скрипта.
К тому же при таком подходе будут неточности в итоговом результате.
Swap позволяет немного сгладить проблему, но лишь незначительно.
Скажем, если вдруг list загоняется без ошибок с 300 Mb памяти и с падением на 256 Mb, то swap спасает.
Если памяти становится 200 Mb, то swap уже не спасет. Все равно вызывается OOM killer, заодно убивая и другие процессы, кроме nft,
а это уже совсем плохо. Может быть убито что-то важное.
Боль N2, но не такая смертельная.
10 лет вылизывания кода, но при загоне больших листов в set-ы то и дело при вызовах nft list происходят seg faults.
Например, падать может nft -t list ruleset, но nft -t list table inet zapret может не падать.
Вроде это не влияет на функционал, но все равно создается неудобство.
Боль N3, не смертельная, но тоже не айс.
Какие-то нерациональные алгоритмы разбора таблиц в nft.
Например, есть 1 большой set на 100000 элементов и 1 маленький на 2 элемента.
Чтобы просто пролистать мелкий set или добавить туда еще что-то nft будет мусолить несколько секунд.
Что он делает за это время ? Тащит из ядра огромный блоб, в котором все в куче, и разбирает его, чтобы выделить искомую мелочь ?
В какой-то мере удается это сгладить, обьединяя несколько команд в единый скрипт.
Плюс N1, главный
iptables хороши, когда ими пользуется кто-то один. Иначе это проходной двор.
Когда есть система управления фаерволом, то приходится как-то к ней прикручиваться, чтобы не нарушить ее работу
и управлять правилами синхронно. Нужно уметь внести и удалить отдельные правила когда это нужно, не трогая все остальное.
Некоторые системы управления фаерволом вообще не предполагают, чтобы кто-то еще лез в iptables, и это очень сильно портит жизнь.
У iptables есть предопределенный набор хуков netfilter с фиксированным приоритетом.
В nftables хуков можно создать неограниченное количество с выбранным приоритетом, управляя ими независимо в отдельных таблицах.
Система управления фаерволом может работать в одной таблице (fw4 в случае openwrt) и не трогать все остальное.
zapret может работать в другой таблице и не трогать систему управления фаерволом. Они друг другу не мешают.
Это снимает множество боли.
Плюс N2
Возможность выбора приоритета хука позволяет легко решить проблему хаотической и принудительной дефрагментацией L3 ipv6,
без танцев с загрузкой модулей ядра со специальными параметрами или перекомпиляцией nftables-nft.
Плюс N3
Наличие множеств (anonymous/named sets) позволяет не писать кучу однообразных правил там, где в iptables их пришлось бы написать.
Плюс N4
Если у вас есть nftables, то там наверняка есть уже все или почти все.
Нет кучи разных модулей ядра и .so плагинов для iptables user-mode процесса.
Отдельные модули ядра есть, но их меньше, чем в iptables, и openwrt их делит на меньшее число пакетов, большинство из которых
и так ставятся по умолчанию. user-mode процесс nft и вовсе неделим. EXE-шник + lib.
Плюс N5
Пишут, что nftables работают быстрее.
Выводы
Честно говоря, лучше бы openwrt оставался на iptables.
Пусть они и старые, c недостатками, но как говорится ложка дегтя портит цистерну меда.
nftables - именно тот случай. Все хорошо, но все плохо из-за досадной особенности.
Без больших листов все почти прекрасно. Но большие ip листы убивают все. Не для домашних это роутеров.
А ipset-ы к nftables не прикрутить.
Делать нечего. Openwrt отошел от iptables. С этим придется как-то жить.
Поэтому пришлось сделать для openwrt поддержку и iptables, и nftables (только с версии openwrt 21.xx, в более старых будут проблемы).
iptables можно задействовать на любой openwrt версии.
Если используется fw3, применяется старый механизм интеграции в fw3.
Если он не используется, то правилами iptables управляем как в традиционных linux системах - то есть с возможностью
запуска и остановки, а скрипт запуска вносит в том числе и правила iptables.

View File

@ -49,7 +49,7 @@ deal with its consequences.
3. Modification of TCP connection at the packet level. Implemented through the NFQUEUE handler and raw sockets.
For options 2 and 3, tpws and nfqws programs are implemented, respectively.
You need to run them with the necessary parameters and redirect certain traffic with iptables.
You need to run them with the necessary parameters and redirect certain traffic with iptables or nftables.
To redirect a TCP connection to a transparent proxy, the following commands are used:
@ -110,6 +110,17 @@ In the PREROUTING DNAT chain, it is possible to any global address or to the lin
the packet came from.
NFQUEUE works without changes.
## nftables
nftables are fine except one very big problem.
nft requires tons of RAM to load large nf sets (ip lists) with subnets/intervals. Most of the home routers can't afford that.
For example, even a 256 Mb system can't load a 100K ip list. nft process will OOM.
nf sets do not support overlapping intervals and that's why nft process applies very RAM consuming algorithm to merge intervals so they don't overlap.
There're equivalents to iptables for all other functions. Interface and protocol anonymous sets allow not to write multiple similar rules.
Flow offloading is built-in into new linux kernels and nft versions.
## When it will not work
* If DNS server returns false responses. ISP can return false IP addresses or not return anything
@ -564,6 +575,9 @@ if remote resolving causes trouble configure clients to use local name resolutio
## Ways to get a list of blocked IP
nftables can't work with ipsets. Native nf sets require lots of RAM to load large ip lists with subnets and intervals.
In case you're on a low RAM system and need large lists it may be required to fall back to iptables+ipset.
1. Enter the blocked domains to `ipset/zapret-hosts-user.txt` and run `ipset/get_user.sh`
At the output, you get `ipset/zapret-ip-user.txt` with IP addresses.
@ -643,6 +657,12 @@ When using large regulator lists estimate the amount of RAM on the router !
The file `/opt/zapret/config` is used by various components of the system and contains basic settings.
It needs to be viewed and edited if necessary.
Which firewall type use on linux systems : `nftables` or `iptables`.
On traditional systems `nftables` is selected by default if nft is installed.
On openwrt by default `nftables` is selected on `firewall4` based systems.
`FWTYPE=iptables`
Main mode :
```
@ -739,8 +759,9 @@ temp directory. Used by ipset/*.sh scripts for large lists processing.
/tmp by default. Can be reassigned if /tmp is tmpfs and RAM is low.
TMPDIR=/opt/zapret/tmp
ipset options :
ipset and nfset options :
`SET_MAXELEM=262144`
`IPSET_OPT="hashsize 262144 maxelem 2097152`
Kernel automatically increases hashsize if ipset is too large for the current hashsize.
@ -770,6 +791,11 @@ In openwrt there's default network 'lan'. Only traffic coming from this network
To override this behaviour set the following variable :
`OPENWRT_LAN="lan lan2 lan3"`
The `INIT_APPLY_FW=1` parameter enables the init script to independently apply iptables rules.
With other values or if the parameter is commented out, the rules will not be applied.
This is useful if you have a firewall management system, in the settings of which you should tie the rules.
Not applicable to `OpenWRT` if used with `firewall3+iptables`.
The following settings are not relevant for openwrt :
If your system works as a router, then you need to enter the names of the internal and external interfaces:
@ -781,10 +807,6 @@ IMPORTANT: configuring routing, masquerade, etc. not a zapret task.
Only modes that intercept transit traffic are enabled.
It's possible to specify multiple interfaces like this : `IFACE_LAN="eth0 eth1 eth2"`
The `INIT_APPLY_FW=1` parameter enables the init script to independently apply iptables rules.
With other values or if the parameter is commented out, the rules will not be applied.
This is useful if you have a firewall management system, in the settings of which you should tie the rules.
## Screwing to the firewall control system or your launch system
@ -795,17 +817,41 @@ In this case, the rules for iptables should be screwed to your firewall separate
The following calls allow you to apply or remove iptables rules separately:
```
/opt/zapret/init.d/sysv/zapret start-fw
/opt/zapret/init.d/sysv/zapret stop-fw
/opt/zapret/init.d/sysv/zapret start_fw
/opt/zapret/init.d/sysv/zapret stop_fw
/opt/zapret/init.d/sysv/zapret restart_fw
```
And you can start or stop the demons separately from the firewall:
```
/opt/zapret/init.d/sysv/zapret start-daemons
/opt/zapret/init.d/sysv/zapret stop-daemons
/opt/zapret/init.d/sysv/zapret start_daemons
/opt/zapret/init.d/sysv/zapret stop_daemons
/opt/zapret/init.d/sysv/zapret restart_daemons
```
nftables nearly eliminate conflicts betweeen firewall control systems because they allow
separate tables and netfilter hooks. `zapret` nf table is used for zapret purposes.
If your system does not touch it everything will likely be OK.
Some additional nftables-only calls exist :
Lookup `lanif`, `wanif`, `wanif6` and `flow table` interface sets.
```
/opt/zapret/init.d/sysv/zapret list_ifsets
```
Renew `lanif`, `wanif`, `wanif6` and `flow table` interface sets.
Taken from `IFACE_LAN`, `IFACE_WAN` config variables on traditional Linux systems.
Autoselected on `OpenWRT`. `lanif` can be extended using `OPENWRT_LAN` config variable.
/opt/zapret/init.d/sysv/zapret reload_ifsets
Calls `nft -t list table inet zapret`.
```
/opt/zapret/init.d/sysv/zapret list_table
```
## Installation
### Checking ISP

View File

@ -1,4 +1,4 @@
zapret v.45
zapret v.46
English
-------
@ -63,7 +63,7 @@ For english version refer to docs/readme.eng.txt
Для вариантов 2 и 3 реализованы программы tpws и nfqws соответственно.
Чтобы они работали, необходимо их запустить с нужными параметрами и перенаправить на них определенный трафик
средствами iptables.
средствами iptables или nftables.
Для перенаправления tcp соединения на transparent proxy используются команды следующего вида :
@ -149,6 +149,14 @@ DNAT на localhost (::1) возможен только в цепочке OUTPUT
NFQUEUE работает без изменений.
Особенности применения nftables
-------------------------------
Более подробно преимущества и недостатки nftables применительно к данной системе описаны в docs/nftables_notes.txt
Если коротко, то в nftables невозможно работать с большими ip листами на системах с малым количеством RAM.
Остальные рассматриваемые здесь функции могут быть перенесены на nftables.
Когда это работать не будет
---------------------------
@ -490,6 +498,9 @@ options ip6table_raw raw_before_defrag=1
Это нужно сделать вручную, никакой автоматики в blockcheck.sh нет.
Либо можно раз и навсегда избавиться от этой проблемы, используя nftables. Там можно создать netfilter hook
с любым приоритетом. Используйте приоритет -401 и ниже.
tpws
-----
@ -642,6 +653,10 @@ tpws полностью работает на асинхронных сокет
Способы получения списка заблокированных IP
-------------------------------------------
!!! nftables не могут работать с ipset-ами. Собственный аналогичный механизм требует огромного количество RAM
!!! для загрузки больших листов. Например, для загона 100K записей в nfset не хватает даже 256 Mb.
!!! Если вам нужны большие листы на домашних роутерах, откатывайтесь на iptables+ipset.
1) Внесите заблокированные домены в ipset/zapret-hosts-user.txt и запустите ipset/get_user.sh
На выходе получите ipset/zapret-ip-user.txt с IP адресами.
@ -829,6 +844,11 @@ tpws и nfqws решают нужно ли применять дурение в
Файл /opt/zapret/config используется различными компонентами системы и содержит основные настройки.
Его нужно просмотреть и при необходимости отредактировать.
На linux системах можно выбрать использовать iptables или nftables.
По умолчанию на традиционных linux выбирается nftables, если установлен nft.
На openwrt по умолчанию выбирается nftables на новых версиях с firewall4.
FWTYPE=iptables
Основной режим :
tpws - tpws в режиме transparent
@ -910,8 +930,11 @@ MDIG_THREADS=30
указать место на флэшке или диске.
TMPDIR=/opt/zapret/tmp
Опции для создания ipset-ов
Опции для создания ipset-ов и nfset-ов
SET_MAXELEM=262144
IPSET_OPT="hashsize 262144 maxelem 2097152"
ПРО РУГАНЬ в dmesg по поводу нехватки памяти.
Может так случиться, что памяти в системе достаточно, но при попытке заполнить огромный ipset
ядро начинает громко ругаться, ipset заполняется не полностью.
@ -939,6 +962,11 @@ GZIP_LISTS=1
Но возможно задать другие сети или список сетей :
OPENWRT_LAN="lan lan2 lan3"
Параметр INIT_APPLY_FW=1 разрешает init скрипту самостоятельно применять правила iptables.
При иных значениях или если параметр закомментирован, правила применены не будут.
Это полезно, если у вас есть система управления фаерволом, в настройки которой и следует прикрутить правила.
На openwrt неприменимо при использовании firewall3+iptables.
Следующие настройки не актуальны для openwrt :
Если ваша система работает как роутер, то нужно вписать названия внутреннего и внешнего интерфейсов :
@ -948,10 +976,6 @@ IFACE_WAN=eth1
Включаются только режимы, обеспечивающие перехват транзитного трафика.
Возможно определить несколько интерфейсов следующим образом : IFACE_LAN="eth0 eth1 eth2"
Параметр INIT_APPLY_FW=1 разрешает init скрипту самостоятельно применять правила iptables.
При иных значениях или если параметр закомментирован, правила применены не будут.
Это полезно, если у вас есть система управления фаерволом, в настройки которой и следует прикрутить правила.
Прикручивание к системе управления фаерволом или своей системе запуска
----------------------------------------------------------------------
@ -961,13 +985,34 @@ IFACE_WAN=eth1
Следующие вызовы позволяют применить или убрать правила iptables отдельно :
/opt/zapret/init.d/sysv/zapret start-fw
/opt/zapret/init.d/sysv/zapret stop-fw
/opt/zapret/init.d/sysv/zapret start_fw
/opt/zapret/init.d/sysv/zapret stop_fw
/opt/zapret/init.d/sysv/zapret restart_fw
А так можно запустить или остановить демоны отдельно от фаервола :
/opt/zapret/init.d/sysv/zapret start-daemons
/opt/zapret/init.d/sysv/zapret stop-daemons
/opt/zapret/init.d/sysv/zapret start_daemons
/opt/zapret/init.d/sysv/zapret stop_daemons
/opt/zapret/init.d/sysv/zapret restart_daemons
nftables сводят практически на нет конфликты между разными системами управления, поскольку позволяют
использовать независимые таблицы и хуки. Используется отдельная nf-таблица "zapret".
Если ваша система ее не будет трогать, скорее всего все будет нормально.
Для nftables предусмотрено несколько дополнительных вызовов :
Посмотреть set-ы интерфейсов, относящихся к lan, wan и wan6. По ним идет завертывание трафика.
А так же таблицу flow table с именами интерфейсов ingress hook.
/opt/zapret/init.d/sysv/zapret list_ifsets
Обновить set-ы интерфейсов, относящихся к lan, wan и wan6.
Для традиционных linux список интерфейсов берется из переменных конфига IFACE_LAN, IFACE_WAN.
Для openwrt определяется автоматически. Множество lanif может быть расширено параметром OPENWRT_LAN.
Все интерфейсы lan и wan так же добавляются в ingress hook от flow table.
/opt/zapret/init.d/sysv/zapret reload_ifsets
Просмотр таблицы без содержимого set-ов. Вызывает nft -t list table inet zapret
/opt/zapret/init.d/sysv/zapret list_table
Вариант custom
--------------
@ -980,20 +1025,22 @@ custom код вынесен в отдельный shell include
Нужно свой код вписать в функции :
zapret_custom_daemons
zapret_custom_firewall
zapret_custom_firewall_nft
В файле custom пишите ваш код, пользуясь хелперами из "functions" или "zapret".
Смотрите как там сделано добавление iptables или запуск демонов.
Используя хелпер функции, вы избавитесь от необходимости учитывать все возможные случаи
типа наличия/отсутствия ipv6, является ли система роутером, имена интерфейсов, ...
Хелперы это учитывают , вам нужно сосредоточиться лишь на фильтрах iptables и
Хелперы это учитывают , вам нужно сосредоточиться лишь на фильтрах {ip,nf}tables и
параметрах демонов.
Код для openwrt и sysv немного отличается. В sysv нужно обрабатывать и запуск, и остановку.
Код для openwrt и sysv немного отличается. В sysv нужно обрабатывать и запуск, и остановку демонов.
Запуск это или остановка передается в параметре $1 (0 или 1).
В openwrt за остановку демонов отвечает procd, а firewall вычищается при "fw3 restart",
потому нет необходимости реализовывать логику останова.
В openwrt за остановку отвечает procd.
При апгрейде нужно сохранить лишь custom, другие файлы править не надо.
Для фаервола кастом пишется отдельно для iptables и nftables. Все очень похоже, но отличается
написание фильтров и названия процедур хелперов. Если вам не нужны iptables или nftables -
можете не писать соответствующую функцию.
Готовый custom скрипт custom-tpws4http-nfqws4https позволяет применить дурение
tpws к http и nfqws к https. При этом поддерживаются установки из config.
@ -1011,6 +1058,8 @@ tpws к http и nfqws к https. При этом поддерживаются у
apt-get update
apt-get install ipset curl dnsutils git
Если хотите использовать nftables, то нужен пакет nftables, а ipset не обязателен.
Скопировать директорию zapret в /opt или скачать через git :
cd /opt
git clone --depth 1 https://github.com/bol-van/zapret
@ -1124,7 +1173,7 @@ git и curl по умолчанию могут присутствовать, ips
Подключить init скрипт :
ln -fs /opt/zapret/init.d/sysv/zapret /etc/init.d
ln -fs /opt/zapret/init.d/openrc/zapret /etc/init.d
rc-update add zapret
Запустить службу :
@ -1174,8 +1223,11 @@ install_easy.sh автоматизирует описанные выше руч
Деинсталяция выполняется через uninstall_easy.sh
Ручная установка на openwrt/LEDE
--------------------------------
Ручная установка на openwrt/LEDE 15.xx-21.xx
--------------------------------------------
!!! Данная инструкция написана для систем, основанных на iptables+firewall3
!!! В новых версиях openwrt переходит на nftables+firewall4, инструкция неприменима. Пользуйтесь install_easy.sh
Установить дополнительные пакеты :
opkg update
@ -1283,10 +1335,10 @@ Cкрипт из /etc/hotplug.d/iface перезапустит демоны по
Посмотреть через iptables -nL, ip6tables -nL или через luci вкладку "firewall" появились ли нужные правила.
ЭКОНОМИЯ МЕСТА : если его мало, то можно оставить в директории zapret лишь подкаталог ipset, файл config и init.d/openwrt.
ЭКОНОМИЯ МЕСТА : если его мало, то можно оставить в директории zapret лишь подкаталоги
ipset, common, файл config, init.d/openwrt.
Далее нужно создать подкаталоги с реально используемыми бинариками (ip2net, mdig, tpws, nfq)
и скопировать туда из binaries рабочие executables.
Рекомендуется оставить ip2net и mdig. Из tpws и nfq оставить лишь тот, что был выбран в config.
ЕСЛИ ВСЕ ПЛОХО С МЕСТОМ : откажитесь от работы со списком РКН. используйте только get_user.sh

View File

@ -1,4 +1,9 @@
# init script functions library for macos
[ -n "$ZAPRET_BASE" ] || ZAPRET_BASE=/opt/zapret
. "$ZAPRET_BASE/config"
. "$ZAPRET_BASE/common/base.sh"
. "$ZAPRET_BASE/common/pf.sh"
IPSET_DIR=$ZAPRET_BASE/ipset
. "$IPSET_DIR/def.sh"
@ -14,33 +19,9 @@ TPWS_WAIT="--bind-wait-ifup=30 --bind-wait-ip=30"
TPWS_WAIT_SOCKS6="$TPWS_WAIT --bind-wait-ip-linklocal=30"
[ -n "$TPWS" ] || TPWS="$ZAPRET_BASE/tpws/tpws"
PF_MAIN="/etc/pf.conf"
PF_ANCHOR_DIR=/etc/pf.anchors
PF_ANCHOR_ZAPRET="$PF_ANCHOR_DIR/zapret"
PF_ANCHOR_ZAPRET_V4="$PF_ANCHOR_DIR/zapret-v4"
PF_ANCHOR_ZAPRET_V6="$PF_ANCHOR_DIR/zapret-v6"
CUSTOM_SCRIPT="$ZAPRET_BASE/init.d/macos/custom"
[ -f "$CUSTOM_SCRIPT" ] && . "$CUSTOM_SCRIPT"
existf()
{
type "$1" >/dev/null 2>/dev/null
}
on_off_function()
{
# $1 : function name on
# $2 : function name off
# $3 : 0 - off, 1 - on
local F="$1"
[ "$3" = "1" ] || F="$2"
shift
shift
shift
"$F" "$@"
}
run_daemon()
{
# $1 - daemon number : 1,2,3,...
@ -134,263 +115,6 @@ get_ipv6_linklocal()
}
pf_anchor_root_reload()
{
echo reloading PF root anchor
pfctl -qf "$PF_MAIN"
}
pf_anchor_root()
{
local patch
[ -f "$PF_MAIN" ] && {
grep -q '^rdr-anchor "zapret"$' "$PF_MAIN" || {
echo patching rdr-anchor in $PF_MAIN
patch=1
sed -i '' -e '/^rdr-anchor "com\.apple\/\*"$/i \
rdr-anchor "zapret"
' $PF_MAIN
}
grep -q '^anchor "zapret"$' "$PF_MAIN" || {
echo patching anchor in $PF_MAIN
patch=1
sed -i '' -e '/^anchor "com\.apple\/\*"$/i \
anchor "zapret"
' $PF_MAIN
}
grep -q "^set limit table-entries" "$PF_MAIN" || {
echo patching table-entries limit
patch=1
sed -i '' -e '/^scrub-anchor "com\.apple\/\*"$/i \
set limit table-entries 5000000
' $PF_MAIN
}
grep -q '^anchor "zapret"$' "$PF_MAIN" &&
grep -q '^rdr-anchor "zapret"$' "$PF_MAIN" &&
grep -q '^set limit table-entries' "$PF_MAIN" && {
if [ -n "$patch" ]; then
echo successfully patched $PF_MAIN
pf_anchor_root_reload
else
echo successfully checked zapret anchors in $PF_MAIN
fi
return 0
}
}
echo ----------------------------------
echo Automatic $PF_MAIN patching failed. You must apply root anchors manually in your PF config.
echo rdr-anchor \"zapret\"
echo anchor \"zapret\"
echo ----------------------------------
return 1
}
pf_anchor_root_del()
{
sed -i '' -e '/^anchor "zapret"$/d' -e '/^rdr-anchor "zapret"$/d' -e '/^set limit table-entries/d' "$PF_MAIN"
}
pf_anchor_zapret()
{
[ "$DISABLE_IPV4" = "1" ] || {
if [ -f "$ZIPLIST_EXCLUDE" ]; then
echo "table <nozapret> persist file \"$ZIPLIST_EXCLUDE\""
else
echo "table <nozapret> persist"
fi
}
[ "$DISABLE_IPV6" = "1" ] || {
if [ -f "$ZIPLIST_EXCLUDE6" ]; then
echo "table <nozapret6> persist file \"$ZIPLIST_EXCLUDE6\""
else
echo "table <nozapret6> persist"
fi
}
[ "$DISABLE_IPV4" = "1" ] || echo "rdr-anchor \"/zapret-v4\" inet to !<nozapret>"
[ "$DISABLE_IPV6" = "1" ] || echo "rdr-anchor \"/zapret-v6\" inet6 to !<nozapret6>"
[ "$DISABLE_IPV4" = "1" ] || echo "anchor \"/zapret-v4\" inet to !<nozapret>"
[ "$DISABLE_IPV6" = "1" ] || echo "anchor \"/zapret-v6\" inet6 to !<nozapret6>"
}
pf_anchor_zapret_tables()
{
# $1 - variable to receive applied table names
# $2/$3 $4/$5 ... table_name/table_file
local tblv=$1
local _tbl
shift
[ "$MODE_FILTER" = "ipset" ] &&
{
while [ -n "$1" ] && [ -n "$2" ] ; do
[ -f "$2" ] && {
echo "table <$1> file \"$2\""
_tbl="$_tbl<$1> "
}
shift
shift
done
}
[ -n "$_tbl" ] || _tbl="any"
eval $tblv="\"\$_tbl\""
}
pf_anchor_port_target()
{
if [ "$MODE_HTTP" = "1" ] && [ "$MODE_HTTPS" = "1" ]; then
echo "{80,443}"
elif [ "$MODE_HTTPS" = "1" ]; then
echo "443"
elif [ "$MODE_HTTP" = "1" ]; then
echo "80"
fi
}
pf_anchor_zapret_v4_tpws()
{
# $1 - port
local rule port=$(pf_anchor_port_target)
for lan in $IFACE_LAN; do
for t in $tbl; do
echo "rdr on $lan inet proto tcp from any to $t port $port -> 127.0.0.1 port $1"
done
done
echo "rdr on lo0 inet proto tcp from !127.0.0.0/8 to any port $port -> 127.0.0.1 port $1"
for t in $tbl; do
rule="route-to (lo0 127.0.0.1) inet proto tcp from !127.0.0.0/8 to $t port $port user { >root }"
if [ -n "$IFACE_WAN" ] ; then
for wan in $IFACE_WAN; do
echo "pass out on $wan $rule"
done
else
echo "pass out $rule"
fi
done
}
pf_anchor_zapret_v4()
{
local tbl port
[ "$DISABLE_IPV4" = "1" ] || {
case $MODE in
tpws)
[ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ] && return
pf_anchor_zapret_tables tbl zapret-user "$ZIPLIST_USER" zapret "$ZIPLIST"
pf_anchor_zapret_v4_tpws $TPPORT
;;
custom)
pf_anchor_zapret_tables tbl zapret-user "$ZIPLIST_USER" zapret "$ZIPLIST"
existf zapret_custom_firewall_v4 && zapret_custom_firewall_v4
;;
esac
}
}
pf_anchor_zapret_v6_tpws()
{
# $1 - port
local LL_LAN rule port=$(pf_anchor_port_target)
# LAN link local is only for router
for lan in $IFACE_LAN; do
LL_LAN=$(get_ipv6_linklocal $lan)
[ -n "$LL_LAN" ] && {
for t in $tbl; do
echo "rdr on $lan inet6 proto tcp from any to $t port $port -> $LL_LAN port $1"
done
}
done
echo "rdr on lo0 inet6 proto tcp from !::1 to any port $port -> fe80::1 port $1"
for t in $tbl; do
rule="route-to (lo0 fe80::1) inet6 proto tcp from !::1 to $t port $port user { >root }"
if [ -n "$IFACE_WAN" ] ; then
for wan in $IFACE_WAN; do
echo "pass out on $wan $rule"
done
else
echo "pass out $rule"
fi
done
}
pf_anchor_zapret_v6()
{
local tbl port
[ "$DISABLE_IPV6" = "1" ] || {
case $MODE in
tpws)
[ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ] && return
pf_anchor_zapret_tables tbl zapret6-user "$ZIPLIST_USER6" zapret6 "$ZIPLIST6"
pf_anchor_zapret_v6_tpws $TPPORT
;;
custom)
pf_anchor_zapret_tables tbl zapret6-user "$ZIPLIST_USER6" zapret6 "$ZIPLIST6"
existf zapret_custom_firewall_v6 && zapret_custom_firewall_v6
;;
esac
}
}
pf_anchors_create()
{
wait_lan_ll
pf_anchor_zapret >"$PF_ANCHOR_ZAPRET"
pf_anchor_zapret_v4 >"$PF_ANCHOR_ZAPRET_V4"
pf_anchor_zapret_v6 >"$PF_ANCHOR_ZAPRET_V6"
}
pf_anchors_del()
{
rm -f "$PF_ANCHOR_ZAPRET" "$PF_ANCHOR_ZAPRET_V4" "$PF_ANCHOR_ZAPRET_V6"
}
pf_anchors_load()
{
echo loading zapret anchor from "$PF_ANCHOR_ZAPRET"
pfctl -qa zapret -f "$PF_ANCHOR_ZAPRET" || {
echo error loading zapret anchor
return 1
}
if [ "$DISABLE_IPV4" = "1" ]; then
echo clearing zapret-v4 anchor
pfctl -qa zapret-v4 -F all 2>/dev/null
else
echo loading zapret-v4 anchor from "$PF_ANCHOR_ZAPRET_V4"
pfctl -qa zapret-v4 -f "$PF_ANCHOR_ZAPRET_V4" || {
echo error loading zapret-v4 anchor
return 1
}
fi
if [ "$DISABLE_IPV6" = "1" ]; then
echo clearing zapret-v6 anchor
pfctl -qa zapret-v6 -F all 2>/dev/null
else
echo loading zapret-v6 anchor from "$PF_ANCHOR_ZAPRET_V6"
pfctl -qa zapret-v6 -f "$PF_ANCHOR_ZAPRET_V6" || {
echo error loading zapret-v6 anchor
return 1
}
fi
echo successfully loaded PF anchors
return 0
}
pf_anchors_clear()
{
echo clearing zapret anchors
pfctl -qa zapret-v4 -F all 2>/dev/null
pfctl -qa zapret-v6 -F all 2>/dev/null
pfctl -qa zapret -F all 2>/dev/null
}
pf_enable()
{
echo enabling PF
pfctl -qe
}
pf_table_reload()
{
echo reloading zapret tables
[ "$DISABLE_IPV4" = "1" ] || pfctl -qTl -a zapret-v4 -f "$PF_ANCHOR_ZAPRET_V4"
[ "$DISABLE_IPV6" = "1" ] || pfctl -qTl -a zapret-v6 -f "$PF_ANCHOR_ZAPRET_V6"
pfctl -qTl -a zapret -f "$PF_ANCHOR_ZAPRET"
}
zapret_do_firewall()
{
# $1 - 1 - add, 0 - del

View File

@ -20,26 +20,26 @@ case "$1" in
"$0" start
;;
start-fw)
start-fw|start_fw)
zapret_apply_firewall
;;
stop-fw)
stop-fw|stop_fw)
zapret_unapply_firewall
;;
restart-fw)
restart-fw|stop_fw)
zapret_restart_firewall
;;
reload-fw-tables)
reload-fw-tables|reload_fw_tables)
pf_table_reload
;;
start-daemons)
start-daemons|start_daemons)
zapret_run_daemons
;;
stop-daemons)
stop-daemons|stop_daemons)
zapret_stop_daemons
;;
restart-daemons)
restart-daemons|restart_daemons)
zapret_restart_daemons
;;

69
init.d/openrc/zapret Normal 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/zapret"
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
}

View File

@ -2,7 +2,7 @@
ZAPRET=/etc/init.d/zapret
[ "$ACTION" = "ifup" ] && [ -x "$ZAPRET" ] && "$ZAPRET" enabled && {
[ -n "$INTERFACE" -a -n "$ACTION" -a -x "$ZAPRET" ] && "$ZAPRET" enabled && {
SCRIPT=$(readlink "$ZAPRET")
if [ -n "$SCRIPT" ]; then
EXEDIR=$(dirname "$SCRIPT")
@ -11,11 +11,29 @@ ZAPRET=/etc/init.d/zapret
ZAPRET_BASE=/opt/zapret
fi
. "$ZAPRET_BASE/config"
[ "$ACTION" = "ifup" ] && {
[ -n "$OPENWRT_LAN" ] || OPENWRT_LAN=lan
for lan in $OPENWRT_LAN; do
[ "$INTERFACE" = "$lan" ] && {
"$ZAPRET" restart
logger -t zapret restarting daemons due to $ACTION of $INTERFACE
"$ZAPRET" restart_daemons
break
}
done
}
. "$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

@ -4,6 +4,8 @@
zapret_custom_daemons()
{
# stop logic is managed by procd
# PLACEHOLDER
echo !!! NEED ATTENTION !!!
echo Start daemon\(s\)
@ -13,8 +15,19 @@ zapret_custom_daemons()
}
zapret_custom_firewall()
{
# $1 - 1 - run, 0 - stop
# PLACEHOLDER
echo !!! NEED ATTENTION !!!
echo Configure iptables for required actions
echo Study how other sections work
}
zapret_custom_firewall_nft()
{
# stop logic is not required
# PLACEHOLDER
echo !!! NEED ATTENTION !!!
echo Configure nftables for required actions
echo Study how other sections work
}

View File

@ -3,6 +3,8 @@
zapret_custom_daemons()
{
# stop logic is managed by procd
local opt
[ "$MODE_HTTP" = "1" ] && {
@ -19,6 +21,8 @@ zapret_custom_daemons()
}
zapret_custom_firewall()
{
# $1 - 1 - run, 0 - stop
local f4 f6
local first_packet_only="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:4"
local desync="-m mark ! --mark $DESYNC_MARK/$DESYNC_MARK"
@ -27,13 +31,35 @@ zapret_custom_firewall()
f4="--dport 80"
f6=$f4
filter_apply_ipset_target f4 f6
fw_tpws "$f4" "$f6" $TPPORT
fw_tpws $1 "$f4" "$f6" $TPPORT
}
[ "$MODE_HTTPS" = "1" ] && {
f4="--dport 443 $first_packet_only"
f6=$f4
filter_apply_ipset_target f4 f6
fw_nfqws_post "$f4 $desync" "$f6 $desync" $QNUM
fw_nfqws_post $1 "$f4 $desync" "$f6 $desync" $QNUM
}
}
zapret_custom_firewall_nft()
{
# stop logic is not required
local f4 f6
local first_packet_only="ct original packets 1-4"
local desync="mark and $DESYNC_MARK == 0"
[ "$MODE_HTTP" = "1" ] && {
f4="tcp dport 80"
f6=$f4
nft_filter_apply_ipset_target f4 f6
nft_fw_tpws "$f4" "$f6" $TPPORT
}
[ "$MODE_HTTPS" = "1" ] && {
f4="tcp dport 443 $first_packet_only"
f6=$f4
nft_filter_apply_ipset_target f4 f6
nft_fw_nfqws_post "$f4 $desync" "$f6 $desync" $QNUM
}
}

View File

@ -0,0 +1,20 @@
# this script contain your special code to launch daemons and configure firewall
# use helpers from "functions" file and "zapret" init script
# in case of upgrade keep this file only, do not modify others
zapret_custom_daemons()
{
# PLACEHOLDER
echo !!! NEED ATTENTION !!!
echo Start daemon\(s\)
echo Study how other sections work
run_daemon 1 /bin/sleep 20
}
zapret_custom_firewall()
{
# PLACEHOLDER
echo !!! NEED ATTENTION !!!
echo Configure iptables for required actions
echo Study how other sections work
}

View File

@ -2,14 +2,22 @@
[ -n "$ZAPRET_BASE" ] || ZAPRET_BASE=/opt/zapret
. "$ZAPRET_BASE/config"
. "$ZAPRET_BASE/common/base.sh"
. "$ZAPRET_BASE/common/fwtype.sh"
. "$ZAPRET_BASE/common/queue.sh"
. "$ZAPRET_BASE/common/linux_iphelper.sh"
. "$ZAPRET_BASE/common/ipt.sh"
. "$ZAPRET_BASE/common/nft.sh"
. "$ZAPRET_BASE/common/linux_fw.sh"
[ -n "$QNUM" ] || QNUM=200
[ -n "$TPPORT" ] || TPPORT=988
[ -n "$WS_USER" ] || WS_USER=daemon
TPWS_LOCALHOST4=127.0.0.127
[ -n "$DESYNC_MARK" ] || DESYNC_MARK=0x40000000
[ -n "$OPENWRT_LAN" ] || OPENWRT_LAN=lan
TPWS_LOCALHOST4=127.0.0.127
# max wait time for the link local ipv6 on the LAN interface
LINKLOCAL_WAIT_SEC=5
@ -26,14 +34,6 @@ NFQWS_OPT_DESYNC_HTTPS="${NFQWS_OPT_DESYNC_HTTPS:-$NFQWS_OPT_DESYNC}"
NFQWS_OPT_DESYNC_HTTP6="${NFQWS_OPT_DESYNC_HTTP6:-$NFQWS_OPT_DESYNC_HTTP}"
NFQWS_OPT_DESYNC_HTTPS6="${NFQWS_OPT_DESYNC_HTTPS6:-$NFQWS_OPT_DESYNC_HTTPS}"
exists()
{
which "$1" >/dev/null 2>/dev/null
}
existf()
{
type "$1" >/dev/null 2>/dev/null
}
# can be multiple ipv6 outgoing interfaces
@ -55,50 +55,13 @@ network_find_wan6_all()
network_find_wan6 $1
}
ipt()
route_localnet()
{
iptables -C "$@" >/dev/null 2>/dev/null || iptables -I "$@"
}
ipt_del()
{
iptables -C "$@" >/dev/null 2>/dev/null && iptables -D "$@"
}
ipt6()
{
ip6tables -C "$@" >/dev/null 2>/dev/null || ip6tables -I "$@"
}
ipt6_del()
{
ip6tables -C "$@" >/dev/null 2>/dev/null && ip6tables -D "$@"
}
# there's no route_localnet for ipv6
# the best we can is to route to link local of the incoming interface
# OUTPUT - can DNAT to ::1
# PREROUTING - can't DNAT to ::1. can DNAT to link local of -i interface or to any global addr
# not a good idea to expose tpws to the world (bind to ::)
get_ipv6_linklocal()
{
# $1 - interface name. if empty - any interface
if exists ip ; then
local dev
[ -n "$1" ] && dev="dev $1"
ip addr show $dev | sed -e 's/^.*inet6 \([^ ]*\)\/[0-9]* scope link.*$/\1/;t;d' | head -n 1
else
ifconfig $1 | sed -re 's/^.*inet6 addr: ([^ ]*)\/[0-9]* Scope:Link.*$/\1/;t;d' | head -n 1
fi
}
get_ipv6_global()
{
# $1 - interface name. if empty - any interface
if exists ip ; then
local dev
[ -n "$1" ] && dev="dev $1"
ip addr show $dev | sed -e 's/^.*inet6 \([^ ]*\)\/[0-9]* scope global.*$/\1/;t;d' | head -n 1
else
ifconfig $1 | sed -re 's/^.*inet6 addr: ([^ ]*)\/[0-9]* Scope:Global.*$/\1/;t;d' | head -n 1
fi
for lan in $OPENWRT_LAN; do
network_get_device DEVICE $lan
[ -n "$DEVICE" ] || continue
sysctl -qw net.ipv4.conf.$DEVICE.route_localnet=1
done
}
dnat6_target()
@ -146,10 +109,61 @@ dnat6_target()
}
set_route_localnet()
{
# $1 - 1 = enable, 0 = disable
[ "$DISABLE_IPV4" = "1" ] || {
local lan DEVICE
for lan in $OPENWRT_LAN; do
network_get_device DEVICE $lan
[ -n "$DEVICE" ] || continue
sysctl -q -w net.ipv4.conf.$DEVICE.route_localnet=$1
done
}
}
prepare_route_localnet()
{
set_route_localnet 1
}
unprepare_route_localnet()
{
set_route_localnet 0
}
prepare_tpws_fw4()
{
# otherwise linux kernel will treat 127.0.0.0/8 as "martian" ip and refuse routing to it
# NOTE : kernels <3.6 do not have this feature. consider upgrading or change DNAT to REDIRECT and do not bind to 127.0.0.0/8
[ "$DISABLE_IPV4" = "1" ] || {
iptables -N input_rule_zapret 2>/dev/null
ipt input_rule_zapret -d $TPWS_LOCALHOST4 -j RETURN
ipta input_rule_zapret -d 127.0.0.0/8 -j DROP
ipt INPUT ! -i lo -j input_rule_zapret
prepare_route_localnet
}
}
unprepare_tpws_fw4()
{
[ "$DISABLE_IPV4" = "1" ] || {
unprepare_route_localnet
ipt_del INPUT ! -i lo -j input_rule_zapret
iptables -F input_rule_zapret 2>/dev/null
iptables -X input_rule_zapret 2>/dev/null
}
}
unprepare_tpws_fw()
{
unprepare_tpws_fw4
}
fw_nfqws_pre4()
{
# $1 - filter ipv4
# $2 - queue number
# $1 - 1 - add, 0 - del
# $2 - filter ipv4
# $3 - queue number
local DEVICE wan_iface
@ -157,14 +171,15 @@ fw_nfqws_pre4()
network_find_wan_all wan_iface
for ext_iface in $wan_iface; do
network_get_device DEVICE $ext_iface
ipt PREROUTING -t mangle -i $DEVICE -p tcp $1 $IPSET_EXCLUDE src -j NFQUEUE --queue-num $2 --queue-bypass
ipt_add_del $1 PREROUTING -t mangle -i $DEVICE -p tcp $2 $IPSET_EXCLUDE src -j NFQUEUE --queue-num $3 --queue-bypass
done
}
}
fw_nfqws_pre6()
{
# $1 - filter ipv6
# $2 - queue number
# $1 - 1 - add, 0 - del
# $2 - filter ipv6
# $3 - queue number
local DEVICE wan_iface
@ -172,23 +187,25 @@ fw_nfqws_pre6()
network_find_wan6_all wan_iface
for ext_iface in $wan_iface; do
network_get_device DEVICE $ext_iface
ipt6 PREROUTING -t mangle -i $DEVICE -p tcp $1 $IPSET_EXCLUDE6 src -j NFQUEUE --queue-num $2 --queue-bypass
ipt6_add_del $1 PREROUTING -t mangle -i $DEVICE -p tcp $2 $IPSET_EXCLUDE6 src -j NFQUEUE --queue-num $3 --queue-bypass
done
}
}
fw_nfqws_pre()
{
# $1 - filter ipv4
# $2 - filter ipv6
# $3 - queue number
# $1 - 1 - add, 0 - del
# $2 - filter ipv4
# $3 - filter ipv6
# $4 - queue number
fw_nfqws_pre4 "$1" $3
fw_nfqws_pre6 "$2" $3
fw_nfqws_pre4 $1 "$2" $4
fw_nfqws_pre6 $1 "$3" $4
}
fw_nfqws_post4()
{
# $1 - filter ipv4
# $2 - queue number
# $1 - 1 - add, 0 - del
# $2 - filter ipv4
# $3 - queue number
local DEVICE wan_iface
@ -196,14 +213,15 @@ fw_nfqws_post4()
network_find_wan_all wan_iface
for ext_iface in $wan_iface; do
network_get_device DEVICE $ext_iface
ipt POSTROUTING -t mangle -o $DEVICE -p tcp $1 $IPSET_EXCLUDE dst -j NFQUEUE --queue-num $2 --queue-bypass
ipt_add_del $1 POSTROUTING -t mangle -o $DEVICE -p tcp $2 $IPSET_EXCLUDE dst -j NFQUEUE --queue-num $3 --queue-bypass
done
}
}
fw_nfqws_post6()
{
# $1 - filter ipv6
# $2 - queue number
# $1 - 1 - add, 0 - del
# $2 - filter ipv6
# $3 - queue number
local DEVICE wan_iface
@ -211,176 +229,85 @@ fw_nfqws_post6()
network_find_wan6_all wan_iface
for ext_iface in $wan_iface; do
network_get_device DEVICE $ext_iface
ipt6 POSTROUTING -t mangle -o $DEVICE -p tcp $1 $IPSET_EXCLUDE6 dst -j NFQUEUE --queue-num $2 --queue-bypass
ipt6_add_del $1 POSTROUTING -t mangle -o $DEVICE -p tcp $2 $IPSET_EXCLUDE6 dst -j NFQUEUE --queue-num $3 --queue-bypass
done
}
}
fw_nfqws_post()
{
# $1 - filter ipv4
# $2 - filter ipv6
# $3 - queue number
# $1 - 1 - add, 0 - del
# $2 - filter ipv4
# $3 - filter ipv6
# $4 - queue number
fw_nfqws_post4 "$1" $3
fw_nfqws_post6 "$2" $3
fw_nfqws_post4 $1 "$2" $4
fw_nfqws_post6 $1 "$3" $4
}
IPT_OWNER="-m owner ! --uid-owner $WS_USER"
fw_tpws4()
{
# $1 - filter ipv6
# $2 - tpws port
# $1 - 1 - add, 0 - del
# $2 - filter ipv4
# $3 - tpws port
local DEVICE wan_iface
local lan DEVICE ext_iface wan_iface
[ "$DISABLE_IPV4" = "1" ] || {
network_find_wan_all wan_iface
for ext_iface in $wan_iface; do
network_get_device DEVICE $ext_iface
ipt OUTPUT -t nat -o $DEVICE $IPT_OWNER -p tcp $1 $IPSET_EXCLUDE dst -j DNAT --to $TPWS_LOCALHOST4:$2
ipt_add_del $1 OUTPUT -t nat -o $DEVICE $IPT_OWNER -p tcp $2 $IPSET_EXCLUDE dst -j DNAT --to $TPWS_LOCALHOST4:$3
done
# allow localnet route only to special tpws IP
iptables -N input_rule_zapret 2>/dev/null
ipt input_rule_zapret -d 127.0.0.0/8 -j DROP
ipt input_rule_zapret -d $TPWS_LOCALHOST4 -j RETURN
[ "$1" = 1 ] && prepare_tpws_fw4
for lan in $OPENWRT_LAN; do
network_get_device DEVICE $lan
[ -n "$DEVICE" ] || continue
ipt prerouting_rule -t nat -i $DEVICE -p tcp $1 $IPSET_EXCLUDE dst -j DNAT --to $TPWS_LOCALHOST4:$2
ipt input_rule -i $DEVICE -j input_rule_zapret
sysctl -qw net.ipv4.conf.$DEVICE.route_localnet=1
ipt_add_del $1 PREROUTING -t nat -i $DEVICE -p tcp $2 $IPSET_EXCLUDE dst -j DNAT --to $TPWS_LOCALHOST4:$3
done
}
}
fw_tpws6()
{
# $1 - filter ipv6
# $2 - tpws port
# $1 - 1 - add, 0 - del
# $2 - filter ipv6
# $3 - tpws port
local DEVICE wan_iface DNAT6
local lan DEVICE ext_iface wan_iface DNAT6
[ "$DISABLE_IPV6" = "1" ] || {
network_find_wan6_all wan_iface
for ext_iface in $wan_iface; do
network_get_device DEVICE $ext_iface
ipt6 OUTPUT -t nat -o $DEVICE $IPT_OWNER -p tcp $1 $IPSET_EXCLUDE6 dst -j DNAT --to [::1]:$2
ipt6_add_del $1 OUTPUT -t nat -o $DEVICE $IPT_OWNER -p tcp $2 $IPSET_EXCLUDE6 dst -j DNAT --to [::1]:$3
done
for lan in $OPENWRT_LAN; do
network_get_device DEVICE $lan
[ -n "$DEVICE" ] || continue
dnat6_target $lan DNAT6
[ "$DNAT6" != '-' ] && ipt6 PREROUTING -t nat -i $DEVICE -p tcp $1 $IPSET_EXCLUDE6 dst -j DNAT --to [$DNAT6]:$2
[ "$DNAT6" != '-' ] && ipt6_add_del $1 PREROUTING -t nat -i $DEVICE -p tcp $2 $IPSET_EXCLUDE6 dst -j DNAT --to [$DNAT6]:$3
done
}
}
fw_tpws()
{
# $1 - filter ipv4
# $2 - filter ipv6
# $3 - tpws port
# $1 - 1 - add, 0 - del
# $2 - filter ipv4
# $3 - filter ipv6
# $4 - tpws port
fw_tpws4 "$1" $3
fw_tpws6 "$2" $3
fw_tpws4 $1 "$2" $4
fw_tpws6 $1 "$3" $4
}
filter_apply_port_target()
{
# $1 - var name of iptables filter
local f
if [ "$MODE_HTTP" = "1" ] && [ "$MODE_HTTPS" = "1" ]; then
f="-m multiport --dports 80,443"
elif [ "$MODE_HTTPS" = "1" ]; then
f="--dport 443"
elif [ "$MODE_HTTP" = "1" ]; then
f="--dport 80"
else
echo WARNING !!! HTTP and HTTPS are both disabled
fi
eval $1="\"\$$1 $f\""
}
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 ipv4 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
}
get_nfqws_qnums()
{
# $1 - var name for ipv4 http
# $2 - var name for ipv4 https
# $3 - var name for ipv6 http
# $4 - var name for ipv6 https
local _qn _qns _qn6 _qns6
[ "$DISABLE_IPV4" = "1" ] || {
_qn=$QNUM
_qns=$_qn
[ "$NFQWS_OPT_DESYNC_HTTP" = "$NFQWS_OPT_DESYNC_HTTPS" ] || _qns=$(($QNUM+1))
}
[ "$DISABLE_IPV6" = "1" ] || {
_qn6=$(($QNUM+2))
_qns6=$(($QNUM+3))
[ "$DISABLE_IPV4" = "1" ] || {
if [ "$NFQWS_OPT_DESYNC_HTTP6" = "$NFQWS_OPT_DESYNC_HTTP" ]; then
_qn6=$_qn;
elif [ "$NFQWS_OPT_DESYNC_HTTP6" = "$NFQWS_OPT_DESYNC_HTTPS" ]; then
_qn6=$_qns;
fi
if [ "$NFQWS_OPT_DESYNC_HTTPS6" = "$NFQWS_OPT_DESYNC_HTTP" ]; then
_qns6=$_qn;
elif [ "$NFQWS_OPT_DESYNC_HTTPS6" = "$NFQWS_OPT_DESYNC_HTTPS" ]; then
_qns6=$_qns;
fi
}
[ "$NFQWS_OPT_DESYNC_HTTPS6" = "$NFQWS_OPT_DESYNC_HTTP6" ] && _qns6=$_qn6;
}
if [ "$MODE_HTTP" = 1 ]; then
eval $1=$_qn
eval $3=$_qn6
else
eval $1=
eval $3=
fi
if [ "$MODE_HTTPS" = 1 ]; then
eval $2=$_qns
eval $4=$_qns6
else
eval $2=
eval $4=
fi
}
create_ipset()
{
echo "Creating ipset"
echo "Creating ip list table (firewall type $FWTYPE)"
"$IPSET_CR" "$@"
}
is_flow_offload_avail()
{
# $1 = '' for ipv4, '6' for ipv6
grep -q FLOWOFFLOAD /proc/net/ip$1_tables_targets
}
list_nfqws_rules()
{
# $1 = '' for ipv4, '6' for ipv6
@ -390,7 +317,7 @@ list_nfqws_rules()
}
reverse_nfqws_rule()
{
sed -e 's/-o /-i /' -e 's/--dport /--sport /' -e 's/--dports /--sports /' -e 's/ dst$/ src/' -e 's/ dst / src /'
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'
}
apply_flow_offloading_enable_rule()
{
@ -411,15 +338,19 @@ apply_flow_offloading_exempt_rule()
echo applying ipv${v:-4} flow offloading exemption : $i
ip${v}tables -A $i
}
flow_offloading_unexempt_v()
{
ipt$1_del FORWARD -j forwarding_rule_zapret
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_flow_offload_avail $1 || return 0
is_ipt_flow_offload_avail $1 || return 0
ipt$1_del forwarding_rule -j forwarding_rule_zapret
ip$1tables -F forwarding_rule_zapret 2>/dev/null
ip$1tables -X forwarding_rule_zapret 2>/dev/null
flow_offloading_unexempt_v $1
[ "$FLOWOFFLOAD" = 'software' -o "$FLOWOFFLOAD" = 'hardware' ] && {
ip$1tables -N forwarding_rule_zapret
@ -436,7 +367,7 @@ flow_offloading_exempt_v()
apply_flow_offloading_enable_rule $1
ipt$1 forwarding_rule -j forwarding_rule_zapret
ipt$1 FORWARD -j forwarding_rule_zapret
}
return 0
@ -446,75 +377,137 @@ flow_offloading_exempt()
[ "$DISABLE_IPV4" = "1" ] || flow_offloading_exempt_v
[ "$DISABLE_IPV6" = "1" ] || flow_offloading_exempt_v 6
}
zapret_apply_firewall()
flow_offloading_unexempt()
{
local first_packet_only="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:4"
local desync="-m mark ! --mark $DESYNC_MARK/$DESYNC_MARK"
local f4 f6 qn qns qn6 qns6
# always create ipsets. ip_exclude ipset is required
create_ipset no-update
case "${MODE_OVERRIDE:-$MODE}" in
tpws)
if [ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ]; then
echo both http and https are disabled. not applying redirection.
else
filter_apply_port_target f4
f6=$f4
filter_apply_ipset_target f4 f6
fw_tpws "$f4" "$f6" $TPPORT
fi
;;
nfqws)
# quite complex but we need to minimize nfqws processes to save RAM
get_nfqws_qnums qn qns qn6 qns6
if [ "$MODE_HTTP_KEEPALIVE" != "1" ] && [ -n "$qn" ] && [ "$qn" = "$qns" ]; then
filter_apply_port_target f4
f4="$f4 $first_packet_only"
filter_apply_ipset_target4 f4
fw_nfqws_post4 "$f4 $desync" $qn
else
if [ -n "$qn" ]; then
f4="--dport 80"
[ "$MODE_HTTP_KEEPALIVE" = "1" ] || f4="$f4 $first_packet_only"
filter_apply_ipset_target4 f4
fw_nfqws_post4 "$f4 $desync" $qn
fi
if [ -n "$qns" ]; then
f4="--dport 443 $first_packet_only"
filter_apply_ipset_target4 f4
fw_nfqws_post4 "$f4 $desync" $qns
fi
fi
if [ "$MODE_HTTP_KEEPALIVE" != "1" ] && [ -n "$qn6" ] && [ "$qn6" = "$qns6" ]; then
filter_apply_port_target f6
f6="$f6 $first_packet_only"
filter_apply_ipset_target6 f6
fw_nfqws_post6 "$f6 $desync" $qn6
else
if [ -n "$qn6" ]; then
f6="--dport 80"
[ "$MODE_HTTP_KEEPALIVE" = "1" ] || f6="$f6 $first_packet_only"
filter_apply_ipset_target6 f6
fw_nfqws_post6 "$f6 $desync" $qn6
fi
if [ -n "$qns6" ]; then
f6="--dport 443 $first_packet_only"
filter_apply_ipset_target6 f6
fw_nfqws_post6 "$f6 $desync" $qns6
fi
fi
;;
custom)
existf zapret_custom_firewall && zapret_custom_firewall
;;
esac
flow_offloading_exempt
return 0
[ "$DISABLE_IPV4" = "1" ] || flow_offloading_unexempt_v
[ "$DISABLE_IPV6" = "1" ] || flow_offloading_unexempt_v 6
}
nft_fill_ifsets()
{
local script elements i wan_iface DEVICE DLAN DWAN DWAN6 ALLDEVS flags
# 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
flush set inet $ZAPRET_NFT_TABLE lanif"
[ "$DISABLE_IPV4" = "1" ] || {
network_find_wan_all wan_iface
for i in $wan_iface; do
network_get_device DEVICE $i
DWAN="$DWAN $DEVICE"
done
[ -n "$DWAN" ] && {
make_comma_list elements $DWAN
script="${script}
add element inet $ZAPRET_NFT_TABLE wanif { $elements }"
}
}
[ "$DISABLE_IPV6" = "1" ] || {
network_find_wan6_all wan_iface
for i in $wan_iface; do
network_get_device DEVICE $i
DWAN6="$DWAN6 $DEVICE"
done
[ -n "$DWAN6" ] && {
make_comma_list elements $DWAN6
script="${script}
add element inet $ZAPRET_NFT_TABLE wanif6 { $elements }"
}
}
for i in $OPENWRT_LAN; do
network_get_device DEVICE $i
DLAN="$DLAN $DEVICE"
done
[ -n "$DLAN" ] && {
make_comma_list elements $DLAN
script="${script}
add element inet $ZAPRET_NFT_TABLE lanif { $elements }"
}
echo "$script" | nft -f -
[ "$FLOWOFFLOAD" = 'software' -o "$FLOWOFFLOAD" = 'hardware' ] && {
ALLDEVS=$(for i in $DLAN $DWAN $DWAN6; do echo $i; done | sort -u | xargs)
[ "$FLOWOFFLOAD" = 'hardware' ] && nft_hw_offload_supported $ALLDEVS && flags=offload
nft_create_or_update_flowtable "$flags" $ALLDEVS
}
}
nft_fw_tpws4()
{
# $1 - filter ipv4
# $2 - tpws port
[ "$DISABLE_IPV4" = "1" ] || {
nft_add_rule dnat_output skuid != $WS_USER oifname @wanif meta l4proto tcp $1 ip daddr != @nozapret dnat ip to $TPWS_LOCALHOST4:$2
nft_add_rule dnat_pre iifname @lanif meta l4proto tcp $1 ip daddr != @nozapret dnat ip to $TPWS_LOCALHOST4:$2
prepare_route_localnet
}
}
nft_fw_tpws6()
{
# $1 - filter ipv6
# $2 - tpws port
local lan DEVICE DNAT6
[ "$DISABLE_IPV6" = "1" ] || {
nft_add_rule dnat_output skuid != $WS_USER oifname @wanif6 meta l4proto tcp $1 ip6 daddr != @nozapret6 dnat ip6 to [::1]:$2
for lan in $OPENWRT_LAN; do
network_get_device DEVICE $lan
[ -n "$DEVICE" ] || continue
dnat6_target $lan DNAT6
[ "$DNAT6" != '-' ] && nft_add_rule dnat_pre iifname $DEVICE meta l4proto tcp $1 ip6 daddr != @nozapret6 dnat ip6 to [$DNAT6]:$2
done
}
}
nft_fw_tpws()
{
# $1 - filter ipv4
# $2 - filter ipv6
# $3 - tpws port
nft_fw_tpws4 "$1" $3
nft_fw_tpws6 "$2" $3
}
nft_fw_nfqws_post4()
{
# $1 - filter ipv4
# $2 - queue number
local DEVICE wan_iface rule
[ "$DISABLE_IPV4" = "1" ] || {
rule="oifname @wanif meta l4proto tcp $1 ip daddr != @nozapret"
nft_add_rule postrouting $rule queue num $2 bypass
nft_add_nfqws_flow_exempt_rule "$rule"
}
}
nft_fw_nfqws_post6()
{
# $1 - filter ipv6
# $2 - queue number
local DEVICE wan_iface rule
[ "$DISABLE_IPV6" = "1" ] || {
rule="oifname @wanif6 meta l4proto tcp $1 ip6 daddr != @nozapret6"
nft_add_rule postrouting $rule queue num $2 bypass
nft_add_nfqws_flow_exempt_rule "$rule"
}
}
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
}

View File

@ -4,6 +4,24 @@ 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/zapret)
if [ -n "$SCRIPT" ]; then
@ -12,10 +30,12 @@ if [ -n "$SCRIPT" ]; then
else
ZAPRET_BASE=/opt/zapret
fi
. "$ZAPRET_BASE/init.d/openwrt/functions"
# !!!!! in openwrt firewall rules are configured separately
# !!!!! 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
@ -23,7 +43,6 @@ PIDDIR=/var/run
NFQWS_OPT_BASE="--user=$WS_USER --dpi-desync-fwmark=$DESYNC_MARK"
[ -n "$TPWS" ] || TPWS="$ZAPRET_BASE/tpws/tpws"
TPWS_LOCALHOST4=127.0.0.127
HOSTLIST="$ZAPRET_BASE/ipset/zapret-hosts.txt.gz"
[ -f "$HOSTLIST" ] || HOSTLIST="$ZAPRET_BASE/ipset/zapret-hosts.txt"
[ -f "$HOSTLIST" ] || HOSTLIST="$ZAPRET_BASE/ipset/zapret-hosts-user.txt"
@ -104,7 +123,8 @@ tpws_apply_socks_binds()
}
start_service() {
start_daemons_procd()
{
local opt qn qns qn6 qns6
case "${MODE_OVERRIDE:-$MODE}" in
@ -149,3 +169,61 @@ start_service() {
return 0
}
start_daemons()
{
rc_procd start_daemons_procd "$@"
}
stop_daemons()
{
procd_kill "$(basename ${basescript:-$initscript})" "$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
}
}

View File

@ -22,3 +22,13 @@ zapret_custom_firewall()
echo Configure iptables for required actions
echo Study how other sections work
}
zapret_custom_firewall_nft()
{
# stop logic is not required
# PLACEHOLDER
echo !!! NEED ATTENTION !!!
echo Configure nftables for required actions
echo Study how other sections work
}

View File

@ -3,6 +3,8 @@
zapret_custom_daemons()
{
# $1 - 1 - run, 0 - stop
local opt
[ "$MODE_HTTP" = "1" ] && {
@ -19,6 +21,8 @@ zapret_custom_daemons()
}
zapret_custom_firewall()
{
# $1 - 1 - run, 0 - stop
local f4 f6
local first_packet_only="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:4"
local desync="-m mark ! --mark $DESYNC_MARK/$DESYNC_MARK"
@ -37,3 +41,25 @@ zapret_custom_firewall()
fw_nfqws_post $1 "$f4 $desync" "$f6 $desync" $QNUM
}
}
zapret_custom_firewall_nft()
{
# stop logic is not required
local f4 f6
local first_packet_only="ct original packets 1-4"
local desync="mark and $DESYNC_MARK == 0"
[ "$MODE_HTTP" = "1" ] && {
f4="tcp dport 80"
f6=$f4
nft_filter_apply_ipset_target f4 f6
nft_fw_tpws "$f4" "$f6" $TPPORT
}
[ "$MODE_HTTPS" = "1" ] && {
f4="tcp dport 443 $first_packet_only"
f6=$f4
nft_filter_apply_ipset_target f4 f6
nft_fw_nfqws_post "$f4 $desync" "$f6 $desync" $QNUM
}
}

View File

@ -0,0 +1,34 @@
# this script contain your special code to launch daemons and configure firewall
# use helpers from "functions" file
# in case of upgrade keep this file only, do not modify others
zapret_custom_daemons()
{
# $1 - 1 - run, 0 - stop
# PLACEHOLDER
echo !!! NEED ATTENTION !!!
echo Start daemon\(s\)
echo Study how other sections work
do_daemon $1 1 /bin/sleep 20
}
zapret_custom_firewall()
{
# $1 - 1 - run, 0 - stop
# PLACEHOLDER
echo !!! NEED ATTENTION !!!
echo Configure iptables for required actions
echo Study how other sections work
}
zapret_custom_firewall_nft()
{
# stop logic is not required
# PLACEHOLDER
echo !!! NEED ATTENTION !!!
echo Configure nftables for required actions
echo Study how other sections work
}

View File

@ -1,29 +1,16 @@
# init script functions library for desktop linux systems
[ -n "$ZAPRET_BASE" ] || ZAPRET_BASE=/opt/zapret
# SHOULD EDIT config
. "$ZAPRET_BASE/config"
. "$ZAPRET_BASE/common/base.sh"
. "$ZAPRET_BASE/common/fwtype.sh"
. "$ZAPRET_BASE/common/queue.sh"
. "$ZAPRET_BASE/common/linux_iphelper.sh"
. "$ZAPRET_BASE/common/ipt.sh"
. "$ZAPRET_BASE/common/nft.sh"
. "$ZAPRET_BASE/common/linux_fw.sh"
exists()
{
which "$1" >/dev/null 2>/dev/null
}
existf()
{
type "$1" >/dev/null 2>/dev/null
}
is_linked_to_busybox()
{
local IFS F P
IFS=:
for path in $PATH; do
F=$path/$1
P="$(readlink $F)"
if [ -z "$P" ] && [ -x $F ] && [ ! -L $F ]; then return 1; fi
[ "${P%busybox*}" != "$P" ] && return
done
}
user_exists()
{
id -u $1 >/dev/null 2>/dev/null
@ -111,89 +98,12 @@ IPSET_EXCLUDE="-m set ! --match-set nozapret"
IPSET_EXCLUDE6="-m set ! --match-set nozapret6"
on_off_function()
{
# $1 : function name on
# $2 : function name off
# $3 : 0 - off, 1 - on
local F="$1"
[ "$3" = "1" ] || F="$2"
shift
shift
shift
"$F" "$@"
}
ipt()
{
iptables -C "$@" >/dev/null 2>/dev/null || iptables -I "$@"
}
ipt_del()
{
iptables -C "$@" >/dev/null 2>/dev/null && iptables -D "$@"
}
ipt_add_del()
{
on_off_function ipt ipt_del "$@"
}
ipt6()
{
ip6tables -C "$@" >/dev/null 2>/dev/null || ip6tables -I "$@"
}
ipt6_del()
{
ip6tables -C "$@" >/dev/null 2>/dev/null && ip6tables -D "$@"
}
ipt6_add_del()
{
on_off_function ipt6 ipt6_del "$@"
}
# there's no route_localnet for ipv6
# the best we can is to route to link local of the incoming interface
# OUTPUT - can DNAT to ::1
# PREROUTING - can't DNAT to ::1. can DNAT to link local of -i interface or to any global addr
# not a good idea to expose tpws to the world (bind to ::)
get_ipv6_linklocal()
{
# $1 - interface name. if empty - any interface
local dev
[ -n "$1" ] && dev="dev $1"
ip addr show $dev | sed -e 's/^.*inet6 \([^ ]*\)\/[0-9]* scope link.*$/\1/;t;d' | head -n 1
}
get_ipv6_global()
{
# $1 - interface name. if empty - any interface
local dev
[ -n "$1" ] && dev="dev $1"
ip addr show $dev | sed -e 's/^.*inet6 \([^ ]*\)\/[0-9]* scope global.*$/\1/;t;d' | head -n 1
}
iface_is_up()
{
# $1 - interface name
[ -f /sys/class/net/$1/operstate ] || return
local state
read state </sys/class/net/$1/operstate
[ "$state" != "down" ]
}
wait_ifup()
{
# $1 - interface name
local ct=0
while
iface_is_up $1 && return
[ "$ct" -ge "$IFUP_WAIT_SEC" ] && break
echo waiting for ifup of $1 for another $(($IFUP_WAIT_SEC - $ct)) seconds ...
ct=$(($ct+1))
sleep 1
do :; done
false
}
dnat6_target()
{
# $1 - lan network name
@ -231,31 +141,52 @@ dnat6_target()
[ -n "$2" ] && eval $2="$DNAT6_TARGET"
}
set_route_localnet()
{
# $1 - 1 = enable, 0 = disable
[ "$DISABLE_IPV4" = "1" ] || {
local lan
for lan in $IFACE_LAN ; do
sysctl -q -w net.ipv4.conf.$lan.route_localnet=$1
done
}
}
prepare_route_localnet()
{
set_route_localnet 1
}
unprepare_route_localnet()
{
set_route_localnet 0
}
prepare_tpws_fw4()
{
# otherwise linux kernel will treat 127.0.0.0/8 as "martian" ip and refuse routing to it
# NOTE : kernels <3.6 do not have this feature. consider upgrading or change DNAT to REDIRECT and do not bind to 127.0.0.0/8
[ "$DISABLE_IPV4" = "1" ] || {
[ -n "$IFACE_LAN" ] && {
local lan
iptables -N input_rule_zapret 2>/dev/null
iptables -F input_rule_zapret
iptables -A input_rule_zapret -d $TPWS_LOCALHOST4 -j RETURN
iptables -A input_rule_zapret -d 127.0.0.0/8 -j DROP
for lan in $IFACE_LAN ; do
ipt INPUT -i $lan -j input_rule_zapret
sysctl -q -w net.ipv4.conf.$lan.route_localnet=1
done
ipt INPUT ! -i lo -j input_rule_zapret
prepare_route_localnet
}
}
}
unprepare_tpws_fw4()
{
[ "$DISABLE_IPV4" = "1" ] || {
[ -n "$IFACE_LAN" ] && {
for lan in $IFACE_LAN ; do
ipt_del INPUT -i $lan -j input_rule_zapret
sysctl -q -w net.ipv4.conf.$lan.route_localnet=0
done
local lan
unprepare_route_localnet
ipt_del INPUT ! -i lo -j input_rule_zapret
iptables -F input_rule_zapret 2>/dev/null
iptables -X input_rule_zapret 2>/dev/null
}
}
}
unprepare_tpws_fw()
{
@ -263,7 +194,7 @@ unprepare_tpws_fw()
}
print_op()
ipt_print_op()
{
if [ "$1" = "1" ]; then
echo "Adding ip$4tables rule for $3 : $2"
@ -279,7 +210,7 @@ fw_tpws4()
# $3 - tpws port
[ "$DISABLE_IPV4" = "1" ] || {
[ "$1" = 1 ] && prepare_tpws_fw4
print_op $1 "$2" "tpws (port $3)"
ipt_print_op $1 "$2" "tpws (port $3)"
for lan in $IFACE_LAN ; do
ipt_add_del $1 PREROUTING -t nat -i $lan -p tcp $2 $IPSET_EXCLUDE dst -j DNAT --to $TPWS_LOCALHOST4:$3
done
@ -298,7 +229,7 @@ fw_tpws6()
# $2 - iptable filter for ipv6
# $3 - tpws port
[ "$DISABLE_IPV6" = "1" ] || {
print_op $1 "$2" "tpws (port $3)" 6
ipt_print_op $1 "$2" "tpws (port $3)" 6
local DNAT6
for lan in $IFACE_LAN ; do
dnat6_target $lan DNAT6
@ -330,7 +261,7 @@ fw_nfqws_pre4()
# $2 - iptable filter for ipv4
# $3 - queue number
[ "$DISABLE_IPV4" = "1" ] || {
print_op $1 "$2" "nfqws prerouting (qnum $3)"
ipt_print_op $1 "$2" "nfqws prerouting (qnum $3)"
if [ -n "$IFACE_WAN" ]; then
for wan in $IFACE_WAN; do
ipt_add_del $1 PREROUTING -t mangle -i $wan -p tcp $2 $IPSET_EXCLUDE src -j NFQUEUE --queue-num $3 --queue-bypass
@ -346,7 +277,7 @@ fw_nfqws_pre6()
# $2 - iptable filter for ipv6
# $3 - queue number
[ "$DISABLE_IPV6" = "1" ] || {
print_op $1 "$2" "nfqws prerouting (qnum $3)" 6
ipt_print_op $1 "$2" "nfqws prerouting (qnum $3)" 6
if [ -n "$IFACE_WAN" ]; then
for wan in $IFACE_WAN; do
ipt6_add_del $1 PREROUTING -t mangle -i $wan -p tcp $2 $IPSET_EXCLUDE6 src -j NFQUEUE --queue-num $3 --queue-bypass
@ -371,7 +302,7 @@ fw_nfqws_post4()
# $2 - iptable filter for ipv4
# $3 - queue number
[ "$DISABLE_IPV4" = "1" ] || {
print_op $1 "$2" "nfqws postrouting (qnum $3)"
ipt_print_op $1 "$2" "nfqws postrouting (qnum $3)"
if [ -n "$IFACE_WAN" ]; then
for wan in $IFACE_WAN; do
ipt_add_del $1 POSTROUTING -t mangle -o $wan -p tcp $2 $IPSET_EXCLUDE dst -j NFQUEUE --queue-num $3 --queue-bypass
@ -387,7 +318,7 @@ fw_nfqws_post6()
# $2 - iptable filter for ipv6
# $3 - queue number
[ "$DISABLE_IPV6" = "1" ] || {
print_op $1 "$2" "nfqws postrouting (qnum $3)" 6
ipt_print_op $1 "$2" "nfqws postrouting (qnum $3)" 6
if [ -n "$IFACE_WAN" ]; then
for wan in $IFACE_WAN; do
ipt6_add_del $1 POSTROUTING -t mangle -o $wan -p tcp $2 $IPSET_EXCLUDE6 dst -j NFQUEUE --queue-num $3 --queue-bypass
@ -407,6 +338,11 @@ fw_nfqws_post()
fw_nfqws_post6 $1 "$3" $4
}
filter_apply_hostlist_target()
{
# $1 - var name of tpws or nfqws params
[ "$MODE_FILTER" = "hostlist" ] && eval $1="\"\$$1 --hostlist=$HOSTLIST\""
}
run_daemon()
{
@ -506,96 +442,6 @@ do_nfqws()
do_daemon $1 $2 "$NFQWS" "$NFQWS_OPT_BASE $3"
}
filter_apply_port_target()
{
# $1 - var name of iptables filter
local f
if [ "$MODE_HTTP" = "1" ] && [ "$MODE_HTTPS" = "1" ]; then
f="-m multiport --dports 80,443"
elif [ "$MODE_HTTPS" = "1" ]; then
f="--dport 443"
elif [ "$MODE_HTTP" = "1" ]; then
f="--dport 80"
else
echo WARNING !!! HTTP and HTTPS are both disabled
fi
eval $1="\"\$$1 $f\""
}
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 ipv4 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
}
filter_apply_hostlist_target()
{
# $1 - var name of tpws or nfqws params
[ "$MODE_FILTER" = "hostlist" ] && eval $1="\"\$$1 --hostlist=$HOSTLIST\""
}
get_nfqws_qnums()
{
# $1 - var name for ipv4 http
# $2 - var name for ipv4 https
# $3 - var name for ipv6 http
# $4 - var name for ipv6 https
local _qn _qns _qn6 _qns6
[ "$DISABLE_IPV4" = "1" ] || {
_qn=$QNUM
_qns=$_qn
[ "$NFQWS_OPT_DESYNC_HTTP" = "$NFQWS_OPT_DESYNC_HTTPS" ] || _qns=$(($QNUM+1))
}
[ "$DISABLE_IPV6" = "1" ] || {
_qn6=$(($QNUM+2))
_qns6=$(($QNUM+3))
[ "$DISABLE_IPV4" = "1" ] || {
if [ "$NFQWS_OPT_DESYNC_HTTP6" = "$NFQWS_OPT_DESYNC_HTTP" ]; then
_qn6=$_qn;
elif [ "$NFQWS_OPT_DESYNC_HTTP6" = "$NFQWS_OPT_DESYNC_HTTPS" ]; then
_qn6=$_qns;
fi
if [ "$NFQWS_OPT_DESYNC_HTTPS6" = "$NFQWS_OPT_DESYNC_HTTP" ]; then
_qns6=$_qn;
elif [ "$NFQWS_OPT_DESYNC_HTTPS6" = "$NFQWS_OPT_DESYNC_HTTPS" ]; then
_qns6=$_qns;
fi
}
[ "$NFQWS_OPT_DESYNC_HTTPS6" = "$NFQWS_OPT_DESYNC_HTTP6" ] && _qns6=$_qn6;
}
if [ "$MODE_HTTP" = 1 ]; then
eval $1=$_qn
eval $3=$_qn6
else
eval $1=
eval $3=
fi
if [ "$MODE_HTTPS" = 1 ]; then
eval $2=$_qns
eval $4=$_qns6
else
eval $2=
eval $4=
fi
}
tpws_apply_socks_binds()
{
local o
@ -613,95 +459,11 @@ tpws_apply_socks_binds()
create_ipset()
{
echo "Creating ipset"
echo "Creating ip list table (firewall type $FWTYPE)"
"$IPSET_CR" "$@"
}
zapret_do_firewall()
{
# $1 - 1 - add, 0 - del
local first_packet_only="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:4"
local desync="-m mark ! --mark $DESYNC_MARK/$DESYNC_MARK"
local f4 f6 qn qns qn6 qns6
# always create ipsets. ip_exclude ipset is required
[ "$1" != "1" ] || create_ipset no-update
case "${MODE_OVERRIDE:-$MODE}" in
tpws)
if [ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ]; then
echo both http and https are disabled. not applying redirection.
else
filter_apply_port_target f4
f6=$f4
filter_apply_ipset_target f4 f6
fw_tpws $1 "$f4" "$f6" $TPPORT
fi
;;
nfqws)
if [ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ]; then
echo both http and https are disabled. not applying redirection.
else
get_nfqws_qnums qn qns qn6 qns6
if [ "$MODE_HTTP_KEEPALIVE" != "1" ] && [ -n "$qn" ] && [ "$qn" = "$qns" ]; then
filter_apply_port_target f4
f4="$f4 $first_packet_only"
filter_apply_ipset_target4 f4
fw_nfqws_post4 $1 "$f4 $desync" $qn
else
if [ -n "$qn" ]; then
f4="--dport 80"
[ "$MODE_HTTP_KEEPALIVE" = "1" ] || f4="$f4 $first_packet_only"
filter_apply_ipset_target4 f4
fw_nfqws_post4 $1 "$f4 $desync" $qn
fi
if [ -n "$qns" ]; then
f4="--dport 443 $first_packet_only"
filter_apply_ipset_target4 f4
fw_nfqws_post4 $1 "$f4 $desync" $qns
fi
fi
if [ "$MODE_HTTP_KEEPALIVE" != "1" ] && [ -n "$qn6" ] && [ "$qn6" = "$qns6" ]; then
filter_apply_port_target f6
f6="$f6 $first_packet_only"
filter_apply_ipset_target6 f6
fw_nfqws_post6 $1 "$f6 $desync" $qn6
else
if [ -n "$qn6" ]; then
f6="--dport 80"
[ "$MODE_HTTP_KEEPALIVE" = "1" ] || f6="$f6 $first_packet_only"
filter_apply_ipset_target6 f6
fw_nfqws_post6 $1 "$f6 $desync" $qn6
fi
if [ -n "$qns6" ]; then
f6="--dport 443 $first_packet_only"
filter_apply_ipset_target6 f6
fw_nfqws_post6 $1 "$f6 $desync" $qns6
fi
fi
fi
;;
custom)
existf zapret_custom_firewall && zapret_custom_firewall $1
;;
esac
[ "$1" = 0 ] && unprepare_tpws_fw
return 0
}
zapret_apply_firewall()
{
zapret_do_firewall 1 "$@"
}
zapret_unapply_firewall()
{
zapret_do_firewall 0 "$@"
}
zapret_do_daemons()
{
# $1 - 1 - run, 0 - stop
@ -749,7 +511,6 @@ zapret_do_daemons()
return 0
}
zapret_run_daemons()
{
zapret_do_daemons 1 "$@"
@ -758,3 +519,120 @@ zapret_stop_daemons()
{
zapret_do_daemons 0 "$@"
}
nft_fill_ifsets()
{
local script elements i ALLDEVS flags
# 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
flush set inet $ZAPRET_NFT_TABLE lanif"
[ -n "$IFACE_LAN" ] && {
make_comma_list elements $IFACE_LAN
script="${script}
add element inet $ZAPRET_NFT_TABLE lanif { $elements }"
}
[ -n "$IFACE_WAN" ] && {
make_comma_list elements $IFACE_WAN
script="${script}
add element inet $ZAPRET_NFT_TABLE wanif { $elements }
add element inet $ZAPRET_NFT_TABLE wanif6 { $elements }"
}
echo "$script" | nft -f -
[ "$FLOWOFFLOAD" = 'software' -o "$FLOWOFFLOAD" = 'hardware' ] && {
ALLDEVS=$(for i in $IFACE_LAN $IFACE_WAN; do echo $i; done | sort -u | xargs)
[ -n "$ALLDEVS" ] && {
[ "$FLOWOFFLOAD" = 'hardware' ] && nft_hw_offload_supported $ALLDEVS && flags=offload
nft_create_or_update_flowtable "$flags" $ALLDEVS
}
}
}
nft_print_op()
{
echo "Adding nftables ipv$3 rule for $2 : $1"
}
nft_fw_tpws4()
{
# $1 - filter ipv4
# $2 - tpws port
[ "$DISABLE_IPV4" = "1" ] || {
nft_print_op "$1" "tpws (port $2)" 4
nft_add_rule dnat_output skuid != $WS_USER ${IFACE_WAN:+oifname @wanif }meta l4proto tcp $1 ip daddr != @nozapret dnat ip to $TPWS_LOCALHOST4:$2
[ -n "$IFACE_LAN" ] && {
prepare_route_localnet
nft_add_rule dnat_pre iifname @lanif meta l4proto tcp $1 ip daddr != @nozapret dnat ip to $TPWS_LOCALHOST4:$2
}
}
}
nft_fw_tpws6()
{
# $1 - filter ipv6
# $2 - tpws port
local lan DNAT6
[ "$DISABLE_IPV6" = "1" ] || {
nft_print_op "$1" "tpws (port $2)" 6
nft_add_rule dnat_output skuid != $WS_USER ${IFACE_WAN:+oifname @wanif6 }meta l4proto tcp $1 ip6 daddr != @nozapret6 dnat ip6 to [::1]:$2
for lan in $IFACE_LAN ; do
dnat6_target $lan DNAT6
[ "$DNAT6" != '-' ] && nft_add_rule dnat_pre iifname $lan meta l4proto tcp $1 ip6 daddr != @nozapret6 dnat ip6 to [$DNAT6]:$2
done
}
}
nft_fw_tpws()
{
# $1 - filter ipv4
# $2 - filter ipv6
# $3 - tpws port
nft_fw_tpws4 "$1" $3
nft_fw_tpws6 "$2" $3
}
nft_fw_nfqws_post4()
{
# $1 - filter ipv4
# $2 - queue number
local rule
[ "$DISABLE_IPV4" = "1" ] || {
nft_print_op "$1" "nfqws postrouting (qnum $2)" 4
rule="${IFACE_WAN:+oifname @wanif }meta l4proto tcp $1 ip daddr != @nozapret"
nft_add_rule postrouting $rule queue num $2 bypass
nft_add_nfqws_flow_exempt_rule "$rule"
}
}
nft_fw_nfqws_post6()
{
# $1 - filter ipv6
# $2 - queue number
local DEVICE rule
[ "$DISABLE_IPV6" = "1" ] || {
nft_print_op "$1" "nfqws postrouting (qnum $2)" 6
rule="${IFACE_WAN:+oifname @wanif6 }meta l4proto tcp $1 ip6 daddr != @nozapret6"
nft_add_rule postrouting $rule queue num $2 bypass
nft_add_nfqws_flow_exempt_rule "$rule"
}
}
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
}

View File

@ -18,7 +18,7 @@ DESC=anti-zapret
do_start()
{
zapret_run_daemons
[ "$INIT_APPLY_FW" != "1" ] || zapret_apply_firewall
[ "$INIT_APPLY_FW" != "1" ] || { zapret_apply_firewall; }
}
do_stop()
{
@ -40,32 +40,42 @@ case "$1" in
do_start
;;
start-fw)
start-fw|start_fw)
zapret_apply_firewall
;;
stop-fw)
stop-fw|stop_fw)
zapret_unapply_firewall
;;
restart-fw)
restart-fw|restart_fw)
zapret_unapply_firewall
zapret_apply_firewall
;;
start-daemons)
start-daemons|start_daemons)
zapret_run_daemons
;;
stop-daemons)
stop-daemons|stop_daemons)
zapret_stop_daemons
;;
restart-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
;;
*)
N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|restart|start-fw|stop-fw|restart-fw|start-daemons|stop-daemons|restart-daemons}" >&2
echo "Usage: $N {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

View File

@ -9,64 +9,23 @@ ZAPRET_CONFIG="$EXEDIR/config"
ZAPRET_BASE="$EXEDIR"
. "$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"
# install target
ZAPRET_TARGET=/opt/zapret
GET_LIST="$IPSET_DIR/get_config.sh"
GET_LIST_PREFIX=/ipset/get_
INIT_SCRIPT=/etc/init.d/zapret
[ -n "$TPPORT" ] || TPPORT=988
SYSTEMD_DIR=/lib/systemd
[ -d "$SYSTEMD_DIR" ] || SYSTEMD_DIR=/usr/lib/systemd
[ -d "$SYSTEMD_DIR" ] && SYSTEMD_SYSTEM_DIR="$SYSTEMD_DIR/system"
ECHON="echo -n"
exists()
{
which $1 >/dev/null 2>/dev/null
}
whichq()
{
which $1 2>/dev/null
}
MD5=md5sum
exists $MD5 || MD5=md5
contains()
{
# check if substring $2 contains in $1
[ "${1#*$2}" != "$1" ]
}
exitp()
{
local A
echo
echo press enter to continue
read A
exit $1
}
require_root()
{
local exe
echo \* checking privileges
[ $(id -u) -ne "0" ] && {
echo root is required
exe="$EXEDIR/$(basename "$0")"
exists sudo && exec sudo "$exe"
exists su && exec su root -c "$exe"
echo su or sudo not found
exitp 2
}
}
sedi()
{
# MacOS doesnt support -i without parameter. busybox doesnt support -i with parameter.
@ -78,58 +37,6 @@ sedi()
fi
}
read_yes_no()
{
# $1 - default (Y/N)
local A
read A
[ -z "$A" ] || ([ "$A" != "Y" ] && [ "$A" != "y" ] && [ "$A" != "N" ] && [ "$A" != "n" ]) && A=$1
[ "$A" = "Y" ] || [ "$A" = "y" ] || [ "$A" = "1" ]
}
ask_yes_no()
{
# $1 - default (Y/N or 0/1)
# $2 - text
local DEFAULT=$1
[ "$1" = "1" ] && DEFAULT=Y
[ "$1" = "0" ] && DEFAULT=N
[ -z "$DEFAULT" ] && DEFAULT=N
$ECHON "$2 (default : $DEFAULT) (Y/N) ? "
read_yes_no $DEFAULT
}
ask_yes_no_var()
{
# $1 - variable name for answer : 0/1
# $2 - text
local DEFAULT
eval DEFAULT="\$$1"
if ask_yes_no "$DEFAULT" "$2"; then
eval $1=1
else
eval $1=0
fi
}
on_off_function()
{
# $1 : function name on
# $2 : function name off
# $3 : 0 - off, 1 - on
local F="$1"
[ "$3" = "1" ] || F="$2"
shift
shift
shift
"$F" "$@"
}
get_dir_inode()
{
local dir="$1"
[ -L "$dir" ] && dir=$(readlink "$dir")
ls -id "$dir" | awk '{print $1}'
}
random()
{
# $1 - min, $2 - max
@ -144,96 +51,6 @@ random()
echo $(( ($r % ($2-$1+1)) + $1 ))
}
parse_var_checked()
{
# $1 - file name
# $2 - var name
local sed="sed -nre s/^[[:space:]]*$2=[\\\"|\']?([^\\\"|\']*)[\\\"|\']?/\1/p"
local v="$($sed <"$1" | tail -n 1)"
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 vi; do
exists "$e" && {
ed="$e"
break
}
done
}
[ -n "$ed" ] && "$ed" "$1"
}
edit_vars()
{
# $1,$2,... - var names
local n=1 var v tmp="/tmp/zvars"
rm -f "$tmp"
while [ 1=1 ]; do
eval var="\$$n"
[ -n "$var" ] || break
eval v="\$$var"
echo $var=\"$v\" >>"$tmp"
n=$(($n+1))
done
edit_file "$tmp" && parse_vars_checked "$tmp" "$@"
rm -f "$tmp"
}
check_system()
{
echo \* checking system
SYSTEM=""
SYSTEMCTL=$(whichq systemctl)
local 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
elif exists rc-update && [ "$INIT" = "openrc-init" ]; then
SYSTEM=openrc
elif [ -f "/etc/openwrt_release" ] && exists opkg && exists uci && [ "$INIT" = "procd" ] ; then
SYSTEM=openwrt
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.txt for manual setup info.
if ask_yes_no N "do you want to continue"; then
SYSTEM=linux
else
exitp 5
fi
fi
elif [ "$UNAME" = "Darwin" ]; then
SYSTEM=macos
# MacOS echo from /bin/sh does not support -n
ECHON=printf
else
echo easy installer only supports Linux and MacOS. check readme.txt for supported systems and manual setup info.
exitp 5
fi
echo system is based on $SYSTEM
}
check_readonly_system()
{
local RO
@ -304,43 +121,6 @@ install_binaries()
}
}
find_str_in_list()
{
[ -n "$1" ] && {
for v in $2; do
[ "$v" = "$1" ] && return 0
done
}
return 1
}
ask_list()
{
# $1 - mode var
# $2 - space separated value list
# $3 - (optional) default value
local M_DEFAULT
eval M_DEFAULT="\$$1"
local M_ALL=$M_DEFAULT
local M=""
local m
[ -n "$3" ] && { find_str_in_list "$M_DEFAULT" "$2" || M_DEFAULT="$3" ;}
n=1
for m in $2; do
echo $n : $m
n=$(($n+1))
done
$ECHON "your choice (default : $M_DEFAULT) : "
read m
[ -n "$m" ] && M=$(echo $2 | cut -d ' ' -f$m 2>/dev/null)
[ -z "$M" ] && M="$M_DEFAULT"
echo selected : $M
eval $1="\"$M\""
[ "$M" != "$M_OLD" ]
}
write_config_var()
{
# $1 - mode var
@ -488,6 +268,17 @@ select_ipv6()
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
}
ask_config()
{
@ -497,7 +288,7 @@ ask_config()
ask_config_offload()
{
is_flow_offload_avail && {
[ "$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\)
echo unfortuantely its not compatible with most nfqws options. nfqws traffic must be exempted from flow offloading.
@ -510,20 +301,6 @@ ask_config_offload()
}
}
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))
}
ask_config_tmpdir()
{
# ask tmpdir change for low ram systems with enough free disk space
@ -643,7 +420,7 @@ copy_openwrt()
[ -d "$2" ] || mkdir -p "$2"
mkdir "$2/tpws" "$2/nfq" "$2/ip2net" "$2/mdig" "$2/binaries" "$2/binaries/$ARCH" "$2/init.d" "$2/tmp"
cp -R "$1/ipset" "$2"
cp -R "$1/common" "$1/ipset" "$2"
cp -R "$1/init.d/openwrt" "$2/init.d"
cp "$1/config" "$1/install_easy.sh" "$1/uninstall_easy.sh" "$1/install_bin.sh" "$1/blockcheck.sh" "$2"
cp "$BINDIR/tpws" "$BINDIR/nfqws" "$BINDIR/ip2net" "$BINDIR/mdig" "$2/binaries/$ARCH"
@ -718,19 +495,34 @@ check_prerequisites_linux()
{
echo \* checking prerequisites
if exists iptables && exists ip6tables ; then
echo iptables present
else
# looks like it's a limited system. will not guess how to install base tools
echo '! iptables/ip6tables NOT present. you must install them manually.'
exitp 5
fi
local s cmd PKGS UTILS req="curl curl"
case "$FWTYPE" in
iptables)
req="$req iptables iptables ip6tables iptables ipset ipset"
;;
nftables)
req="$req nft nftables"
;;
esac
if exists ipset && exists curl ; then
echo ipset and curl are present
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)
YUM=$(whichq yum)
PACMAN=$(whichq pacman)
@ -739,39 +531,42 @@ check_prerequisites_linux()
APK=$(whichq apk)
if [ -x "$APTGET" ] ; then
"$APTGET" update
"$APTGET" install -y --no-install-recommends ipset curl dnsutils || {
"$APTGET" install -y --no-install-recommends $PKGS dnsutils || {
echo could not install prerequisites
exitp 6
}
elif [ -x "$YUM" ] ; then
"$YUM" -y install curl ipset || {
"$YUM" -y install $PKGS || {
echo could not install prerequisites
exitp 6
}
elif [ -x "$PACMAN" ] ; then
"$PACMAN" -Syy
"$PACMAN" --noconfirm -S ipset curl || {
"$PACMAN" --noconfirm -S $PKGS || {
echo could not install prerequisites
exitp 6
}
elif [ -x "$ZYPPER" ] ; then
"$ZYPPER" --non-interactive install ipset curl || {
"$ZYPPER" --non-interactive install $PKGS || {
echo could not install prerequisites
exitp 6
}
elif [ -x "$EOPKG" ] ; then
"$EOPKG" -y install ipset curl || {
"$EOPKG" -y install $PKGS || {
echo could not install prerequisites
exitp 6
}
elif [ -x "$APK" ] ; then
"$APK" add ipset curl || {
"$APK" update
# for alpine
[ "$FWTYPE" = iptables ] && [ -n "$($APK list ip6tables)" ] && PKGS="$PKGS ip6tables"
"$APK" add $PKGS || {
echo could not install prerequisites
exitp 6
}
else
echo supported package manager not found
echo you must manually install : ipset curl
echo you must manually install : $UTILS
exitp 5
fi
fi
@ -795,25 +590,6 @@ service_install_systemd()
fi
}
service_stop_systemd()
{
echo \* stopping zapret service
"$SYSTEMCTL" daemon-reload
"$SYSTEMCTL" disable zapret
"$SYSTEMCTL" stop zapret
}
service_start_systemd()
{
echo \* starting zapret service
"$SYSTEMCTL" start zapret || {
echo could not start zapret service
exitp 30
}
}
timer_install_systemd()
{
echo \* installing zapret-list-update timer
@ -851,59 +627,6 @@ download_list()
}
}
end_with_newline()
{
local c=$(tail -c 1)
[ "$c" = "" ]
}
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
}
}
dnstest()
{
@ -930,10 +653,11 @@ install_systemd()
require_root
check_readonly_system
check_location copy_all
check_prerequisites_linux
service_stop_systemd
install_binaries
check_dns
service_stop_systemd
select_fwtype
check_prerequisites_linux
install_binaries
select_ipv6
ask_config
service_install_systemd
@ -945,66 +669,19 @@ install_systemd()
service_start_systemd
}
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 zapret
}
ln -fs "$INIT_SCRIPT_SRC" "$INIT_SCRIPT"
[ "$1" != "0" ] && rc-update add zapret
}
service_start_sysv()
{
[ -x "$INIT_SCRIPT" ] && {
echo \* starting zapret service
"$INIT_SCRIPT" start || {
echo could not start zapret service
exitp 30
}
}
}
service_stop_sysv()
{
[ -x "$INIT_SCRIPT" ] && {
echo \* stopping zapret service
"$INIT_SCRIPT" stop
}
}
_install_sysv()
{
# $1 - install init script
INIT_SCRIPT_SRC="$EXEDIR/init.d/sysv/zapret"
check_bins
require_root
check_readonly_system
check_location copy_all
check_prerequisites_linux
service_stop_sysv
install_binaries
check_dns
service_stop_sysv
select_fwtype
check_prerequisites_linux
install_binaries
select_ipv6
ask_config
$1
@ -1017,11 +694,13 @@ _install_sysv()
install_sysv()
{
INIT_SCRIPT_SRC="$EXEDIR/init.d/sysv/zapret"
_install_sysv install_sysv_init
}
install_openrc()
{
INIT_SCRIPT_SRC="$EXEDIR/init.d/openrc/zapret"
_install_sysv install_openrc_init
}
@ -1033,9 +712,10 @@ install_linux()
check_bins
require_root
check_location copy_all
check_dns
select_fwtype
check_prerequisites_linux
install_binaries
check_dns
select_ipv6
ask_config
download_list
@ -1051,45 +731,22 @@ install_linux()
}
check_kmod()
{
[ -f "/lib/modules/$(uname -r)/$1.ko" ]
}
check_package_exists_openwrt()
{
[ -n "$(opkg list $1)" ]
}
check_package_openwrt()
{
[ -n "$(opkg list-installed $1)" ]
}
check_packages_openwrt()
{
for pkg in $@; do
check_package_openwrt $pkg || return
done
}
is_linked_to_busybox()
{
local IFS F P
IFS=:
for path in $PATH; do
F=$path/$1
P="$(readlink $F)"
if [ -z "$P" ] && [ -x $F ] && [ ! -L $F ]; then return 1; fi
[ "${P%busybox*}" != "$P" ] && return
done
}
check_prerequisites_openwrt()
{
echo \* checking prerequisites
local PKGS="iptables-mod-extra iptables-mod-nfqueue iptables-mod-filter iptables-mod-ipopt iptables-mod-conntrack-extra ipset curl"
local PKGS="curl" PKGS UPD=0
case "$FWTYPE" in
iptables)
PKGS="ipset iptables-mod-extra iptables-mod-nfqueue iptables-mod-filter iptables-mod-ipopt iptables-mod-conntrack-extra"
[ "$DISABLE_IPV6" != "1" ] && PKGS="$PKGS ip6tables-mod-nat ip6tables-extra"
local UPD=0
;;
nftables)
PKGS="nftables kmod-nft-nat kmod-nft-offload kmod-nft-queue"
[ "$DISABLE_IPV6" != "1" ] && PKGS="$PKGS kmod-nft-nat6"
;;
esac
if check_packages_openwrt $PKGS ; then
echo everything is present
@ -1149,111 +806,11 @@ check_prerequisites_openwrt()
}
}
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
fw3 -q restart || {
echo could not restart firewall
exitp 30
}
}
remove_openwrt_firewall()
{
echo \* removing firewall script
openwrt_fw_section_del
# from old zapret versions. now we use single include
openwrt_fw_section_del 6
# free some RAM
"$IPSET_DIR/create_ipset.sh" clear
}
install_openwrt_iface_hook()
{
echo \* installing ifup hook
ln -fs "$OPENWRT_IFACE_HOOK" /etc/hotplug.d/iface
}
is_flow_offload_avail()
{
# $1 = '' for ipv4, '6' for ipv6
grep -q FLOWOFFLOAD /proc/net/ip$1_tables_targets
}
deoffload_openwrt_firewall()
{
echo \* checking flow offloading
is_flow_offload_avail || {
[ "$FWTYPE" = "nftables" ] || is_ipt_flow_offload_avail || {
echo unavailable
return
}
@ -1262,7 +819,7 @@ deoffload_openwrt_firewall()
if [ "$fo" = "1" ] ; then
local mod=0
$ECHON "system wide flow offloading detected. "
printf "system wide flow offloading detected. "
case $FLOWOFFLOAD in
donttouch)
if [ "$MODE" = "nfqws" ]; then
@ -1303,23 +860,31 @@ install_openwrt()
check_location copy_openwrt
install_binaries
check_dns
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
# can be previous firewall preventing access
remove_openwrt_firewall
restart_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
service_start_sysv
install_openwrt_iface_hook
install_openwrt_firewall
# 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
}
@ -1332,24 +897,7 @@ remove_pf_zapret_hooks()
pf_anchors_clear
}
service_install_macos()
{
echo \* installing zapret service
ln -fs /opt/zapret/init.d/macos/zapret.plist /Library/LaunchDaemons
}
service_start_macos()
{
echo \* starting zapret service
"$INIT_SCRIPT_SRC" start
}
service_stop_macos()
{
echo \* stopping zapret service
"$INIT_SCRIPT_SRC" stop
}
macos_fw_reload_trigger_clear()
{
case "$MODE" in

View File

@ -2,16 +2,23 @@
# 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
IPSET_DIR="$(dirname "$0")"
IPSET_DIR="$(cd "$IPSET_DIR"; pwd)"
. "$IPSET_DIR/def.sh"
. "$IPSET_DIR/../common/fwtype.sh"
. "$IPSET_DIR/../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
while [ -n "$1" ]; do
[ "$1" = "no-update" ] && NO_UPDATE=1
@ -51,32 +58,30 @@ ipset_get_script()
# $2 - ipset name
zzcat "$1" | sort -u | sed -nEe "s/^.+$/add $2 &/p"
}
ipset_restore()
{
# $1 - filename
# $2 - ipset name
# $3 - "6" = ipv6
zzexist "$1" || return
local fsize=$(zzsize "$1")
# $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 $2 "
local T="Adding to ipset $1 "
[ "$svram" = "1" ] && T="$T (saveram)"
T="$T : $f"
echo $T
if [ "$svram" = "1" ]; then
ipset_get_script "$1" "$2" >"$IPSET_CMD"
ipset_get_script "$2" "$1" >"$IPSET_CMD"
ipset_restore_chunked "$IPSET_CMD" $IPSET_SAVERAM_CHUNK_SIZE
rm -f "$IPSET_CMD"
else
ipset_get_script "$1" "$2" | ipset -! restore
ipset_get_script "$2" "$1" | ipset -! restore
fi
}
create_ipset()
{
if [ "$1" -eq "6" ]; then
@ -90,12 +95,70 @@ create_ipset()
ipset flush $2
[ "$DO_CLEAR" = "1" ] || {
for f in "$5" "$6" ; do
ipset_restore "$f" "$2" $1
ipset_restore "$2" "$f"
done
}
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" | 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"
nfset_get_script_multi "$@" | nft -f -
}
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
nft flush set inet $ZAPRET_NFT_TABLE $2
}
[ "$DO_CLEAR" = "1" ] || {
nfset_restore $2 $4 $5
return
}
return 0
}
add_ipfw_table()
{
@ -124,6 +187,7 @@ create_ipfw_table()
shift
[ "$DO_CLEAR" = "1" ] || {
while [ -n "$1" ]; do
echo "Adding to ipfw table $name : $1"
populate_ipfw_table $name "$1"
shift
done
@ -136,6 +200,8 @@ print_reloading_backend()
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
@ -144,6 +210,7 @@ print_reloading_backend()
oom_adjust_high
get_fwtype
if [ -n "$LISTS_RELOAD" ] ; then
if [ "$LISTS_RELOAD" = "-" ] ; then
@ -153,7 +220,9 @@ if [ -n "$LISTS_RELOAD" ] ; then
echo executing custom ip list reload command : $LISTS_RELOAD
$LISTS_RELOAD
fi
elif exists ipset; then
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
@ -175,7 +244,27 @@ elif exists ipset; then
create_ipset 6 $ZIPSET_EXCLUDE6 hash:net "$IPSET_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE6"
}
true
elif exists ipfw; then
;;
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"
@ -195,7 +284,11 @@ elif exists ipfw; then
create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE"
fi
true
else
;;
*)
echo no supported ip list backend found
true
;;
esac
fi

View File

@ -4,17 +4,19 @@
}
. "$IPSET_DIR/../config"
. "$IPSET_DIR/../common/base.sh"
[ -z "$TMPDIR" ] && TMPDIR=/tmp
[ -z "$GZIP_LISTS" ] && GZIP_LISTS=1
[ -z "$IPSET_OPT" ] && IPSET_OPT="hashsize 262144 maxelem 2097152"
[ -z "$IPSET_OPT_EXCLUDE" ] && IPSET_OPT_EXCLUDE="hashsize 1024 maxelem 65536"
[ -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
@ -43,10 +45,6 @@ ZUSERLIST_EXCLUDE="$IPSET_DIR/zapret-hosts-user-exclude.txt"
[ -z "$MDIG_THREADS" ] && MDIG_THREADS=30
exists()
{
which "$1" >/dev/null 2>/dev/null
}
# 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
@ -55,9 +53,9 @@ if [ -x /usr/local/bin/ggrep ] ; then
elif [ -x /usr/local/bin/grep ] ; then
GREP=/usr/local/bin/grep
elif exists ggrep; then
GREP=$(which ggrep)
GREP=$(whichq ggrep)
else
GREP=$(which grep)
GREP=$(whichq grep)
fi
# GNU awk is faster
@ -109,7 +107,7 @@ zzcat()
{
if [ -f "$1.gz" ]; then
gunzip -c "$1.gz"
else
elif [ -f "$1" ]; then
cat "$1"
fi
}
@ -127,7 +125,11 @@ zzsize()
{
local f="$1"
[ -f "$1.gz" ] && f="$1.gz"
if [ -f "$f" ]; then
wc -c <"$f" | xargs
else
$ECHON -n 0
fi
}
digger()

View File

@ -5,173 +5,46 @@
EXEDIR="$(dirname "$0")"
EXEDIR="$(cd "$EXEDIR"; pwd)"
IPSET_DIR="$EXEDIR/ipset"
ZAPRET_CONFIG="$EXEDIR/config"
ZAPRET_BASE="$EXEDIR"
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/zapret
exists()
{
which $1 >/dev/null 2>/dev/null
}
whichq()
{
which $1 2>/dev/null
}
exitp()
{
echo
echo press enter to continue
read A
exit $1
}
require_root()
{
local exe
echo \* checking privileges
[ $(id -u) -ne "0" ] && {
echo root is required
exe="$EXEDIR/$(basename "$0")"
exists sudo && exec sudo "$exe"
exists su && exec su root -c "$exe"
echo su or sudo not found
exitp 2
}
}
check_system()
{
echo \* checking system
SYSTEM=""
SYSTEMCTL=$(whichq systemctl)
local 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
elif exists rc-update && [ "$INIT" = "openrc-init" ]; then
SYSTEM=openrc
elif [ -f "/etc/openwrt_release" ] && exists opkg && exists uci && [ "$INIT" = "procd" ] ; then
SYSTEM=openwrt
else
echo system is not either systemd, openrc or openwrt based
echo check readme.txt for manual setup info.
SYSTEM=linux
fi
elif [ "$UNAME" = "Darwin" ]; then
SYSTEM=macos
# MacOS echo from /bin/sh does not support -n
ECHON=printf
else
echo easy installer only supports Linux and MacOS. check readme.txt for supported systems and manual setup info.
exitp 5
fi
echo system is based on $SYSTEM
}
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
}
service_stop_systemd()
{
echo \* stopping zapret service
"$SYSTEMCTL" daemon-reload
"$SYSTEMCTL" disable zapret
"$SYSTEMCTL" stop zapret
}
service_remove_systemd()
{
echo \* removing zapret service
rm -f "$SYSTEMD_SYSTEM_DIR/zapret.service"
"$SYSTEMCTL" daemon-reload
}
timer_remove_systemd()
{
echo \* removing zapret-list-update timer
"$SYSTEMCTL" daemon-reload
"$SYSTEMCTL" disable zapret-list-update.timer
"$SYSTEMCTL" stop zapret-list-update.timer
rm -f "$SYSTEMD_SYSTEM_DIR/zapret-list-update.service" "$SYSTEMD_SYSTEM_DIR/zapret-list-update.timer"
"$SYSTEMCTL" daemon-reload
}
. "$ZAPRET_CONFIG"
. "$ZAPRET_BASE/common/base.sh"
. "$ZAPRET_BASE/common/elevate.sh"
. "$ZAPRET_BASE/common/fwtype.sh"
. "$ZAPRET_BASE/common/ipt.sh"
. "$ZAPRET_BASE/common/nft.sh"
. "$ZAPRET_BASE/common/pf.sh"
. "$ZAPRET_BASE/common/installer.sh"
remove_systemd()
{
clear_ipset
service_stop_systemd
service_remove_systemd
timer_remove_systemd
nft_del_table
crontab_del
}
service_remove_sysv()
{
echo \* removing zapret service
[ -x "$INIT_SCRIPT" ] && {
"$INIT_SCRIPT" disable
"$INIT_SCRIPT" stop
}
rm -f "$INIT_SCRIPT"
}
service_remove_openrc()
{
echo \* removing zapret service
[ -x "$INIT_SCRIPT" ] && {
rc-update del zapret
"$INIT_SCRIPT" stop
}
rm -f "$INIT_SCRIPT"
}
remove_openrc()
{
clear_ipset
service_remove_openrc
nft_del_table
crontab_del
}
remove_linux()
{
INIT_SCRIPT_SRC="$EXEDIR/init.d/sysv/zapret"
clear_ipset
echo \* executing sysv init stop
"$INIT_SCRIPT_SRC" stop
nft_del_table
crontab_del
echo
@ -179,97 +52,19 @@ remove_linux()
echo 'you must manually remove zapret auto start from your system'
}
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"
}
}
remove_openwrt_firewall()
{
echo \* removing firewall script
openwrt_fw_section_del
# from old zapret versions. now we use single include
openwrt_fw_section_del 6
# free some RAM
"$IPSET_DIR/create_ipset.sh" clear
}
restart_openwrt_firewall()
{
echo \* restarting firewall
fw3 -q restart || {
echo could not restart firewall
exitp 30
}
}
remove_openwrt_iface_hook()
{
echo \* removing ifup hook
rm -f /etc/hotplug.d/iface/??-zapret
}
remove_openwrt()
{
OPENWRT_FW_INCLUDE=/etc/firewall.zapret
remove_openwrt_firewall
restart_openwrt_firewall
clear_ipset
service_remove_sysv
remove_openwrt_firewall
remove_openwrt_iface_hook
nft_del_table
restart_openwrt_firewall
crontab_del
}
service_remove_macos()
{
echo \* removing zapret service
rm -f /Library/LaunchDaemons/zapret.plist
zapret_stop_daemons
}
remove_macos_firewall()
{
echo \* removing zapret PF hooks
pf_anchors_clear
pf_anchors_del
pf_anchor_root_del
pf_anchor_root_reload
}
remove_macos()
{
remove_macos_firewall