mirror of https://github.com/bol-van/zapret/
35 changed files with 3093 additions and 1994 deletions
@ -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 |
|||
} |
@ -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" ] |
|||
} |
@ -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 |
|||
} |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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 "$@" |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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" |
|||
} |
@ -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 |
|||
} |
@ -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. |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
@ -46,341 +46,268 @@ existf() |
|||
|
|||
network_find_wan_all() |
|||
{ |
|||
__network_ifstatus "$1" "" "[@.route[@.target='0.0.0.0' && [email protected]]].interface" "" 10 2>/dev/null && return |
|||
network_find_wan $1 |
|||
__network_ifstatus "$1" "" "[@.route[@.target='0.0.0.0' && [email protected]]].interface" "" 10 2>/dev/null && return |
|||
network_find_wan $1 |
|||
} |
|||
network_find_wan6_all() |
|||
{ |
|||
__network_ifstatus "$1" "" "[@.route[@.target='::' && [email protected]]].interface" "" 10 2>/dev/null && return |
|||
network_find_wan6 $1 |
|||
__network_ifstatus "$1" "" "[@.route[@.target='::' && [email protected]]].interface" "" 10 2>/dev/null && return |
|||
network_find_wan6 $1 |
|||
} |
|||
|
|||
ipt() |
|||
route_localnet() |
|||
{ |
|||
iptables -C "$@" >/dev/null 2>/dev/null || iptables -I "$@" |
|||
for lan in $OPENWRT_LAN; do |
|||
network_get_device DEVICE $lan |
|||
[ -n "$DEVICE" ] || continue |
|||
sysctl -qw net.ipv4.conf.$DEVICE.route_localnet=1 |
|||
done |
|||
} |
|||
ipt_del() |
|||
|
|||
dnat6_target() |
|||
{ |
|||
iptables -C "$@" >/dev/null 2>/dev/null && iptables -D "$@" |
|||
# $1 - lan network name |
|||
# $2 - var to store target ip6 |
|||
# get target ip address for DNAT. prefer link locals |
|||
# tpws should be as inaccessible from outside as possible |
|||
# link local address can appear not immediately after ifup |
|||
# DNAT6_TARGET=- means attempt was made but address was not found (to avoid multiple re-attempts) |
|||
|
|||
local DNAT6_TARGET DVAR=DNAT6_TARGET_$1 |
|||
DVAR=$(echo $DVAR | sed 's/[^a-zA-Z0-9_]/_/g') |
|||
eval DNAT6_TARGET="\$$DVAR" |
|||
[ -n "$2" ] && eval $2='' |
|||
|
|||
[ -n "$DNAT6_TARGET" ] || { |
|||
# no reason to query if its down |
|||
network_is_up $1 || return |
|||
|
|||
local DEVICE |
|||
network_get_device DEVICE $1 |
|||
|
|||
local ct=0 |
|||
while |
|||
DNAT6_TARGET=$(get_ipv6_linklocal $DEVICE) |
|||
[ -n "$DNAT6_TARGET" ] && break |
|||
[ "$ct" -ge "$LINKLOCAL_WAIT_SEC" ] && break |
|||
echo $DEVICE: waiting for the link local for another $(($LINKLOCAL_WAIT_SEC - $ct)) seconds ... |
|||
ct=$(($ct+1)) |
|||
sleep 1 |
|||
do :; done |
|||
|
|||
[ -n "$DNAT6_TARGET" ] || { |
|||
echo $DEVICE: no link local. getting global |
|||
DNAT6_TARGET=$(get_ipv6_global $DEVICE) |
|||
[ -n "$DNAT6_TARGET" ] || { |
|||
echo $DEVICE: could not get any address |
|||
DNAT6_TARGET=- |
|||
} |
|||
} |
|||
eval $DVAR="$DNAT6_TARGET" |
|||
} |
|||
[ -n "$2" ] && eval $2="$DNAT6_TARGET" |
|||
} |
|||
ipt6() |
|||
|
|||
|
|||
set_route_localnet() |
|||
{ |
|||
ip6tables -C "$@" >/dev/null 2>/dev/null || ip6tables -I "$@" |
|||
# $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 |
|||
} |
|||
} |
|||
ipt6_del() |
|||
prepare_route_localnet() |
|||
{ |
|||
ip6tables -C "$@" >/dev/null 2>/dev/null && ip6tables -D "$@" |
|||
set_route_localnet 1 |
|||
} |
|||
|
|||
# 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() |
|||
unprepare_route_localnet() |
|||
{ |
|||
# $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 |
|||
set_route_localnet 0 |
|||
} |
|||
get_ipv6_global() |
|||
prepare_tpws_fw4() |
|||
{ |
|||
# $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 |
|||
# 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 |
|||
|
|||
dnat6_target() |
|||
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() |
|||
{ |
|||
# $1 - lan network name |
|||
# $2 - var to store target ip6 |
|||
# get target ip address for DNAT. prefer link locals |
|||
# tpws should be as inaccessible from outside as possible |
|||
# link local address can appear not immediately after ifup |
|||
# DNAT6_TARGET=- means attempt was made but address was not found (to avoid multiple re-attempts) |
|||
|
|||
local DNAT6_TARGET DVAR=DNAT6_TARGET_$1 |
|||
DVAR=$(echo $DVAR | sed 's/[^a-zA-Z0-9_]/_/g') |
|||
eval DNAT6_TARGET="\$$DVAR" |
|||
[ -n "$2" ] && eval $2='' |
|||
|
|||
[ -n "$DNAT6_TARGET" ] || { |
|||
# no reason to query if its down |
|||
network_is_up $1 || return |
|||
|
|||
local DEVICE |
|||
network_get_device DEVICE $1 |
|||
|
|||
local ct=0 |
|||
while |
|||
DNAT6_TARGET=$(get_ipv6_linklocal $DEVICE) |
|||
[ -n "$DNAT6_TARGET" ] && break |
|||
[ "$ct" -ge "$LINKLOCAL_WAIT_SEC" ] && break |
|||
echo $DEVICE: waiting for the link local for another $(($LINKLOCAL_WAIT_SEC - $ct)) seconds ... |
|||
ct=$(($ct+1)) |
|||
sleep 1 |
|||
do :; done |
|||
|
|||
[ -n "$DNAT6_TARGET" ] || { |
|||
echo $DEVICE: no link local. getting global |
|||
DNAT6_TARGET=$(get_ipv6_global $DEVICE) |
|||
[ -n "$DNAT6_TARGET" ] || { |
|||
echo $DEVICE: could not get any address |
|||
DNAT6_TARGET=- |
|||
} |
|||
} |
|||
eval $DVAR="$DNAT6_TARGET" |
|||
} |
|||
[ -n "$2" ] && eval $2="$DNAT6_TARGET" |
|||
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 |
|||
local DEVICE 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 PREROUTING -t mangle -i $DEVICE -p tcp $1 $IPSET_EXCLUDE src -j NFQUEUE --queue-num $2 --queue-bypass |
|||
done |
|||
} |
|||
[ "$DISABLE_IPV4" = "1" ] || { |
|||
network_find_wan_all wan_iface |
|||
for ext_iface in $wan_iface; do |
|||
network_get_device DEVICE $ext_iface |
|||
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 |
|||
local DEVICE wan_iface |
|||
|
|||
[ "$DISABLE_IPV6" = "1" ] || { |
|||
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 |
|||
done |
|||
} |
|||
[ "$DISABLE_IPV6" = "1" ] || { |
|||
network_find_wan6_all wan_iface |
|||
for ext_iface in $wan_iface; do |
|||
network_get_device DEVICE $ext_iface |
|||
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 |
|||
local DEVICE 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 POSTROUTING -t mangle -o $DEVICE -p tcp $1 $IPSET_EXCLUDE dst -j NFQUEUE --queue-num $2 --queue-bypass |
|||
done |
|||
} |
|||
[ "$DISABLE_IPV4" = "1" ] || { |
|||
network_find_wan_all wan_iface |
|||
for ext_iface in $wan_iface; do |
|||
network_get_device DEVICE $ext_iface |
|||
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 |
|||
local DEVICE wan_iface |
|||
|
|||
[ "$DISABLE_IPV6" = "1" ] || { |
|||
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 |
|||
done |
|||
} |
|||
[ "$DISABLE_IPV6" = "1" ] || { |
|||
network_find_wan6_all wan_iface |
|||
for ext_iface in $wan_iface; do |
|||
network_get_device DEVICE $ext_iface |
|||
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 |
|||
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 |
|||
|
|||
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 |
|||
done |
|||
} |
|||
[ "$DISABLE_IPV4" = "1" ] || { |
|||
network_find_wan_all wan_iface |
|||
for ext_iface in $wan_iface; do |
|||
network_get_device DEVICE $ext_iface |
|||
ipt_add_del $1 OUTPUT -t nat -o $DEVICE $IPT_OWNER -p tcp $2 $IPSET_EXCLUDE dst -j DNAT --to $TPWS_LOCALHOST4:$3 |
|||
done |
|||
[ "$1" = 1 ] && prepare_tpws_fw4 |
|||
for lan in $OPENWRT_LAN; do |
|||
network_get_device DEVICE $lan |
|||
[ -n "$DEVICE" ] || continue |
|||
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 |
|||
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 |
|||
done |
|||
} |
|||
[ "$DISABLE_IPV6" = "1" ] || { |
|||
network_find_wan6_all wan_iface |
|||
for ext_iface in $wan_iface; do |
|||
network_get_device DEVICE $ext_iface |
|||
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_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" |
|||
"$IPSET_CR" "$@" |
|||
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 |
|||
} |
|||
flow_offloading_unexempt() |
|||
{ |
|||
[ "$DISABLE_IPV4" = "1" ] || flow_offloading_unexempt_v |
|||
[ "$DISABLE_IPV6" = "1" ] || flow_offloading_unexempt_v 6 |
|||
} |
|||
|
|||
|
|||
|
|||
zapret_apply_firewall() |
|||
nft_fill_ifsets() |
|||
{ |
|||
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 |
|||
local script elements i wan_iface DEVICE DLAN DWAN DWAN6 ALLDEVS flags |
|||
|
|||
# always create ipsets. ip_exclude ipset is required |
|||
create_ipset no-update |
|||
# 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 |
|||
|
|||
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 |
|||
script="flush set inet $ZAPRET_NFT_TABLE wanif |
|||
flush set inet $ZAPRET_NFT_TABLE wanif6 |
|||
flush set inet $ZAPRET_NFT_TABLE lanif" |
|||
|
|||
return 0 |
|||
[ "$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 |
|||
} |
|||
|
@ -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 |
|||
} |
Loading…
Reference in new issue