From 95667733a6fb3582698bcc066f08d5bd6f233111 Mon Sep 17 00:00:00 2001 From: bol-van Date: Tue, 15 Feb 2022 17:15:36 +0300 Subject: [PATCH] nftables support --- blockcheck.sh | 101 +-- common/base.sh | 135 ++++ common/dialog.sh | 58 ++ common/elevate.sh | 13 + common/fwtype.sh | 64 ++ common/installer.sh | 462 +++++++++++++ common/ipt.sh | 172 +++++ common/linux_fw.sh | 23 + common/linux_iphelper.sh | 51 ++ common/nft.sh | 348 ++++++++++ common/pf.sh | 262 ++++++++ common/queue.sh | 45 ++ config | 12 +- docs/changes.txt | 4 + docs/nftables_notes.txt | 92 +++ docs/readme.eng.md | 66 +- docs/readme.txt | 94 ++- init.d/macos/functions | 286 +------- init.d/macos/zapret | 14 +- init.d/openrc/zapret | 69 ++ init.d/openwrt/90-zapret | 34 +- init.d/openwrt/custom | 13 + init.d/openwrt/custom-tpws4http-nfqws4https | 30 +- init.d/openwrt/custom.default | 20 + init.d/openwrt/functions | 695 ++++++++++---------- init.d/openwrt/zapret | 84 ++- init.d/sysv/custom | 10 + init.d/sysv/custom-tpws4http-nfqws4https | 26 + init.d/sysv/custom.default | 34 + init.d/sysv/functions | 472 +++++-------- init.d/sysv/zapret | 26 +- install_easy.sh | 634 +++--------------- ipset/create_ipset.sh | 367 +++++++---- ipset/def.sh | 24 +- uninstall_easy.sh | 259 +------- 35 files changed, 3099 insertions(+), 2000 deletions(-) create mode 100644 common/base.sh create mode 100644 common/dialog.sh create mode 100644 common/elevate.sh create mode 100644 common/fwtype.sh create mode 100644 common/installer.sh create mode 100644 common/ipt.sh create mode 100644 common/linux_fw.sh create mode 100644 common/linux_iphelper.sh create mode 100644 common/nft.sh create mode 100644 common/pf.sh create mode 100644 common/queue.sh create mode 100644 docs/nftables_notes.txt create mode 100644 init.d/openrc/zapret create mode 100644 init.d/openwrt/custom.default create mode 100644 init.d/sysv/custom.default diff --git a/blockcheck.sh b/blockcheck.sh index da9694c..6acfefe 100755 --- a/blockcheck.sh +++ b/blockcheck.sh @@ -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" diff --git a/common/base.sh b/common/base.sh new file mode 100644 index 0000000..46758a4 --- /dev/null +++ b/common/base.sh @@ -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 +} diff --git a/common/dialog.sh b/common/dialog.sh new file mode 100644 index 0000000..0cb3890 --- /dev/null +++ b/common/dialog.sh @@ -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" ] +} diff --git a/common/elevate.sh b/common/elevate.sh new file mode 100644 index 0000000..fc8ed6a --- /dev/null +++ b/common/elevate.sh @@ -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 + } +} diff --git a/common/fwtype.sh b/common/fwtype.sh new file mode 100644 index 0000000..61390bb --- /dev/null +++ b/common/fwtype.sh @@ -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 +} diff --git a/common/installer.sh b/common/installer.sh new file mode 100644 index 0000000..22236c6 --- /dev/null +++ b/common/installer.sh @@ -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 +} diff --git a/common/ipt.sh b/common/ipt.sh new file mode 100644 index 0000000..d1c835c --- /dev/null +++ b/common/ipt.sh @@ -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 +} diff --git a/common/linux_fw.sh b/common/linux_fw.sh new file mode 100644 index 0000000..65af95c --- /dev/null +++ b/common/linux_fw.sh @@ -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 "$@" +} diff --git a/common/linux_iphelper.sh b/common/linux_iphelper.sh new file mode 100644 index 0000000..c734456 --- /dev/null +++ b/common/linux_iphelper.sh @@ -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 /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 +} diff --git a/common/pf.sh b/common/pf.sh new file mode 100644 index 0000000..d0c119e --- /dev/null +++ b/common/pf.sh @@ -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 persist file \"$ZIPLIST_EXCLUDE\"" + else + echo "table persist" + fi + } + [ "$DISABLE_IPV6" = "1" ] || { + if [ -f "$ZIPLIST_EXCLUDE6" ]; then + echo "table persist file \"$ZIPLIST_EXCLUDE6\"" + else + echo "table persist" + fi + } + [ "$DISABLE_IPV4" = "1" ] || echo "rdr-anchor \"/zapret-v4\" inet to !" + [ "$DISABLE_IPV6" = "1" ] || echo "rdr-anchor \"/zapret-v6\" inet6 to !" + [ "$DISABLE_IPV4" = "1" ] || echo "anchor \"/zapret-v4\" inet to !" + [ "$DISABLE_IPV6" = "1" ] || echo "anchor \"/zapret-v6\" inet6 to !" +} +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" +} diff --git a/common/queue.sh b/common/queue.sh new file mode 100644 index 0000000..b66216e --- /dev/null +++ b/common/queue.sh @@ -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 +} diff --git a/config b/config index 5ab421a..0dfb970 100644 --- a/config +++ b/config @@ -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 diff --git a/docs/changes.txt b/docs/changes.txt index 8acee53..87c0331 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -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 diff --git a/docs/nftables_notes.txt b/docs/nftables_notes.txt new file mode 100644 index 0000000..a615305 --- /dev/null +++ b/docs/nftables_notes.txt @@ -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. diff --git a/docs/readme.eng.md b/docs/readme.eng.md index 61547a2..1a7c999 100644 --- a/docs/readme.eng.md +++ b/docs/readme.eng.md @@ -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 diff --git a/docs/readme.txt b/docs/readme.txt index 70664ba..1a09f9e 100644 --- a/docs/readme.txt +++ b/docs/readme.txt @@ -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 diff --git a/init.d/macos/functions b/init.d/macos/functions index 01f5bfd..1cc6a80 100644 --- a/init.d/macos/functions +++ b/init.d/macos/functions @@ -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 persist file \"$ZIPLIST_EXCLUDE\"" - else - echo "table persist" - fi - } - [ "$DISABLE_IPV6" = "1" ] || { - if [ -f "$ZIPLIST_EXCLUDE6" ]; then - echo "table persist file \"$ZIPLIST_EXCLUDE6\"" - else - echo "table persist" - fi - } - [ "$DISABLE_IPV4" = "1" ] || echo "rdr-anchor \"/zapret-v4\" inet to !" - [ "$DISABLE_IPV6" = "1" ] || echo "rdr-anchor \"/zapret-v6\" inet6 to !" - [ "$DISABLE_IPV4" = "1" ] || echo "anchor \"/zapret-v4\" inet to !" - [ "$DISABLE_IPV6" = "1" ] || echo "anchor \"/zapret-v6\" inet6 to !" -} -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 diff --git a/init.d/macos/zapret b/init.d/macos/zapret index c563926..17f7897 100755 --- a/init.d/macos/zapret +++ b/init.d/macos/zapret @@ -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 ;; diff --git a/init.d/openrc/zapret b/init.d/openrc/zapret new file mode 100644 index 0000000..3a1ca58 --- /dev/null +++ b/init.d/openrc/zapret @@ -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 +} diff --git a/init.d/openwrt/90-zapret b/init.d/openwrt/90-zapret index 381f9f1..364a6e7 100644 --- a/init.d/openwrt/90-zapret +++ b/init.d/openwrt/90-zapret @@ -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" - [ -n "$OPENWRT_LAN" ] || OPENWRT_LAN=lan - for lan in $OPENWRT_LAN; do - [ "$INTERFACE" = "$lan" ] && { - "$ZAPRET" restart - break - } - done + [ "$ACTION" = "ifup" ] && { + [ -n "$OPENWRT_LAN" ] || OPENWRT_LAN=lan + for lan in $OPENWRT_LAN; do + [ "$INTERFACE" = "$lan" ] && { + 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 } diff --git a/init.d/openwrt/custom b/init.d/openwrt/custom index 2136b74..508013e 100644 --- a/init.d/openwrt/custom +++ b/init.d/openwrt/custom @@ -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 +} diff --git a/init.d/openwrt/custom-tpws4http-nfqws4https b/init.d/openwrt/custom-tpws4http-nfqws4https index 41307e9..306a7e9 100644 --- a/init.d/openwrt/custom-tpws4http-nfqws4https +++ b/init.d/openwrt/custom-tpws4http-nfqws4https @@ -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 } } diff --git a/init.d/openwrt/custom.default b/init.d/openwrt/custom.default new file mode 100644 index 0000000..2136b74 --- /dev/null +++ b/init.d/openwrt/custom.default @@ -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 +} diff --git a/init.d/openwrt/functions b/init.d/openwrt/functions index 89fa44c..e7a8b32 100644 --- a/init.d/openwrt/functions +++ b/init.d/openwrt/functions @@ -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' && !@.table]].interface" "" 10 2>/dev/null && return - network_find_wan $1 + __network_ifstatus "$1" "" "[@.route[@.target='0.0.0.0' && !@.table]].interface" "" 10 2>/dev/null && return + network_find_wan $1 } network_find_wan6_all() { - __network_ifstatus "$1" "" "[@.route[@.target='::' && !@.table]].interface" "" 10 2>/dev/null && return - network_find_wan6 $1 + __network_ifstatus "$1" "" "[@.route[@.target='::' && !@.table]].interface" "" 10 2>/dev/null && return + 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() { - # $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) + # $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='' + 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 + [ -n "$DNAT6_TARGET" ] || { + # no reason to query if its down + network_is_up $1 || return - local DEVICE - network_get_device DEVICE $1 + 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 + 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" + [ -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" } +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 + 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 } - - -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 } diff --git a/init.d/openwrt/zapret b/init.d/openwrt/zapret index 75494cd..86b53d3 100755 --- a/init.d/openwrt/zapret +++ b/init.d/openwrt/zapret @@ -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 + } +} diff --git a/init.d/sysv/custom b/init.d/sysv/custom index ed6183b..666d2d4 100644 --- a/init.d/sysv/custom +++ b/init.d/sysv/custom @@ -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 +} diff --git a/init.d/sysv/custom-tpws4http-nfqws4https b/init.d/sysv/custom-tpws4http-nfqws4https index 6b5add4..b7b4327 100644 --- a/init.d/sysv/custom-tpws4http-nfqws4https +++ b/init.d/sysv/custom-tpws4http-nfqws4https @@ -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 + } +} diff --git a/init.d/sysv/custom.default b/init.d/sysv/custom.default new file mode 100644 index 0000000..666d2d4 --- /dev/null +++ b/init.d/sysv/custom.default @@ -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 +} diff --git a/init.d/sysv/functions b/init.d/sysv/functions index 85f79db..7566a44 100644 --- a/init.d/sysv/functions +++ b/init.d/sysv/functions @@ -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 /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 + [ "$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 + ipt INPUT ! -i lo -j input_rule_zapret + prepare_route_localnet + } } } unprepare_tpws_fw4() { - [ -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 - iptables -F input_rule_zapret 2>/dev/null - iptables -X input_rule_zapret 2>/dev/null + [ "$DISABLE_IPV4" = "1" ] || { + [ -n "$IFACE_LAN" ] && { + 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 +} diff --git a/init.d/sysv/zapret b/init.d/sysv/zapret index 5f65558..9e247a4 100755 --- a/init.d/sysv/zapret +++ b/init.d/sysv/zapret @@ -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 diff --git a/install_easy.sh b/install_easy.sh index df44a9d..0b3c7a0 100755 --- a/install_easy.sh +++ b/install_easy.sh @@ -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" - [ "$DISABLE_IPV6" != "1" ] && PKGS="$PKGS ip6tables-mod-nat ip6tables-extra" - local UPD=0 + 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" + ;; + 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 diff --git a/ipset/create_ipset.sh b/ipset/create_ipset.sh index 14ee16c..84e6db0 100755 --- a/ipset/create_ipset.sh +++ b/ipset/create_ipset.sh @@ -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 @@ -22,180 +29,266 @@ done file_extract_lines() { - # $1 - filename - # $2 - from line (starting with 0) - # $3 - line count - # awk "{ err=1 } NR < $(($2+1)) { next } { print; err=0 } NR == $(($2+$3)) { exit err } END {exit err}" "$1" - $AWK "NR < $(($2+1)) { next } { print } NR == $(($2+$3)) { exit }" "$1" + # $1 - filename + # $2 - from line (starting with 0) + # $3 - line count + # awk "{ err=1 } NR < $(($2+1)) { next } { print; err=0 } NR == $(($2+$3)) { exit err } END {exit err}" "$1" + $AWK "NR < $(($2+1)) { next } { print } NR == $(($2+$3)) { exit }" "$1" } ipset_restore_chunked() { - # $1 - filename - # $2 - chunk size - local pos lines - [ -f "$1" ] || return - lines=$(wc -l <"$1") - pos=$lines - while [ "$pos" -gt "0" ]; do - pos=$((pos-$2)) - [ "$pos" -lt "0" ] && pos=0 - file_extract_lines "$1" $pos $2 | ipset -! restore - sed -i "$(($pos+1)),$ d" "$1" - done + # $1 - filename + # $2 - chunk size + local pos lines + [ -f "$1" ] || return + lines=$(wc -l <"$1") + pos=$lines + while [ "$pos" -gt "0" ]; do + pos=$((pos-$2)) + [ "$pos" -lt "0" ] && pos=0 + file_extract_lines "$1" $pos $2 | ipset -! restore + sed -i "$(($pos+1)),$ d" "$1" + done } ipset_get_script() { - # $1 - filename - # $2 - ipset name - zzcat "$1" | sort -u | sed -nEe "s/^.+$/add $2 &/p" + # $1 - filename + # $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") - local svram=0 - # do not saveram small files. file can also be gzipped - [ "$SAVERAM" = "1" ] && [ "$fsize" -ge "$IPSET_SAVERAM_MIN_FILESIZE" ] && svram=1 + # $1 - ipset name + # $2 - filename - local T="Adding to ipset $2 " - [ "$svram" = "1" ] && T="$T (saveram)" - T="$T : $f" - echo $T + 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 - if [ "$svram" = "1" ]; then - ipset_get_script "$1" "$2" >"$IPSET_CMD" - ipset_restore_chunked "$IPSET_CMD" $IPSET_SAVERAM_CHUNK_SIZE - rm -f "$IPSET_CMD" - else - ipset_get_script "$1" "$2" | ipset -! restore - fi + local T="Adding to ipset $1 " + [ "$svram" = "1" ] && T="$T (saveram)" + T="$T : $f" + echo $T + + if [ "$svram" = "1" ]; then + ipset_get_script "$2" "$1" >"$IPSET_CMD" + ipset_restore_chunked "$IPSET_CMD" $IPSET_SAVERAM_CHUNK_SIZE + rm -f "$IPSET_CMD" + else + ipset_get_script "$2" "$1" | ipset -! restore + fi } - create_ipset() { - if [ "$1" -eq "6" ]; then - FAMILY=inet6 - else - FAMILY=inet - fi - ipset create $2 $3 $4 family $FAMILY 2>/dev/null || { - [ "$NO_UPDATE" = "1" ] && return - } - ipset flush $2 - [ "$DO_CLEAR" = "1" ] || { - for f in "$5" "$6" ; do - ipset_restore "$f" "$2" $1 - done - } - return 0 + if [ "$1" -eq "6" ]; then + FAMILY=inet6 + else + FAMILY=inet + fi + ipset create $2 $3 $4 family $FAMILY 2>/dev/null || { + [ "$NO_UPDATE" = "1" ] && return + } + ipset flush $2 + [ "$DO_CLEAR" = "1" ] || { + for f in "$5" "$6" ; do + 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() { - # $1 - table name - sed -nEe "s/^.+$/table $1 add &/p" | ipfw -q /dev/stdin + # $1 - table name + sed -nEe "s/^.+$/table $1 add &/p" | ipfw -q /dev/stdin } populate_ipfw_table() { - # $1 - table name - # $2 - ip list file - zzexist "$2" || return - zzcat "$2" | sort -u | add_ipfw_table $1 + # $1 - table name + # $2 - ip list file + zzexist "$2" || return + zzcat "$2" | sort -u | add_ipfw_table $1 } create_ipfw_table() { - # $1 - table name - # $2 - table options - # $3,$4, ... - ip list files. can be v4,v6 or mixed + # $1 - table name + # $2 - table options + # $3,$4, ... - ip list files. can be v4,v6 or mixed - local name=$1 - ipfw table "$name" create $2 2>/dev/null || { - [ "$NO_UPDATE" = "1" ] && return - } - ipfw -q table $1 flush - shift - shift - [ "$DO_CLEAR" = "1" ] || { - while [ -n "$1" ]; do - populate_ipfw_table $name "$1" - shift - done - } + local name=$1 + ipfw table "$name" create $2 2>/dev/null || { + [ "$NO_UPDATE" = "1" ] && return + } + ipfw -q table $1 flush + shift + shift + [ "$DO_CLEAR" = "1" ] || { + while [ -n "$1" ]; do + echo "Adding to ipfw table $name : $1" + populate_ipfw_table $name "$1" + shift + done + } } print_reloading_backend() { - # $1 - backend name - local s="reloading $1 backend" - if [ "$NO_UPDATE" = 1 ]; then - s="$s (no-update)" - else - s="$s (forced-update)" - fi - echo $s + # $1 - backend name + local s="reloading $1 backend" + if [ "$NO_UPDATE" = 1 ]; then + s="$s (no-update)" + elif [ "$DO_CLEAR" = 1 ]; then + s="$s (clear)" + else + s="$s (forced-update)" + fi + echo $s } oom_adjust_high +get_fwtype if [ -n "$LISTS_RELOAD" ] ; then - if [ "$LISTS_RELOAD" = "-" ] ; then - echo not reloading ip list backend - true - else - echo executing custom ip list reload command : $LISTS_RELOAD - $LISTS_RELOAD - fi -elif exists ipset; then - # ipset seem to buffer the whole script to memory - # on low RAM system this can cause oom errors - # in SAVERAM mode we feed script lines in portions starting from the end, while truncating source file to free /tmp space - # only /tmp is considered tmpfs. other locations mean tmpdir was redirected to a disk - SAVERAM=0 - [ "$TMPDIR" = "/tmp" ] && { - RAMSIZE=$($GREP MemTotal /proc/meminfo | $AWK '{print $2}') - [ "$RAMSIZE" -lt "110000" ] && SAVERAM=1 - } - print_reloading_backend ipset - [ "$DISABLE_IPV4" != "1" ] && { - create_ipset 4 $ZIPSET hash:net "$IPSET_OPT" "$ZIPLIST" "$ZIPLIST_USER" - create_ipset 4 $ZIPSET_IPBAN hash:net "$IPSET_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN" - create_ipset 4 $ZIPSET_EXCLUDE hash:net "$IPSET_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE" - } - [ "$DISABLE_IPV6" != "1" ] && { - create_ipset 6 $ZIPSET6 hash:net "$IPSET_OPT" "$ZIPLIST6" "$ZIPLIST_USER6" - create_ipset 6 $ZIPSET_IPBAN6 hash:net "$IPSET_OPT" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6" - create_ipset 6 $ZIPSET_EXCLUDE6 hash:net "$IPSET_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE6" - } - true -elif exists ipfw; then - print_reloading_backend "ipfw table" - if [ "$DISABLE_IPV4" != "1" ] && [ "$DISABLE_IPV6" != "1" ]; then - create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST" "$ZIPLIST_USER" "$ZIPLIST6" "$ZIPLIST_USER6" - create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6" - create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE" "$ZIPLIST_EXCLUDE6" - elif [ "$DISABLE_IPV4" != "1" ]; then - create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST" "$ZIPLIST_USER" - create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN" - create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE" - elif [ "$DISABLE_IPV6" != "1" ]; then - create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST6" "$ZIPLIST_USER6" - create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6" - create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE6" - else - create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" - create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" - create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" - fi - true + if [ "$LISTS_RELOAD" = "-" ] ; then + echo not reloading ip list backend + true + else + echo executing custom ip list reload command : $LISTS_RELOAD + $LISTS_RELOAD + fi else - echo no supported ip list backend found - true + case "$FWTYPE" in + iptables) + # ipset seem to buffer the whole script to memory + # on low RAM system this can cause oom errors + # in SAVERAM mode we feed script lines in portions starting from the end, while truncating source file to free /tmp space + # only /tmp is considered tmpfs. other locations mean tmpdir was redirected to a disk + SAVERAM=0 + [ "$TMPDIR" = "/tmp" ] && { + RAMSIZE=$($GREP MemTotal /proc/meminfo | $AWK '{print $2}') + [ "$RAMSIZE" -lt "110000" ] && SAVERAM=1 + } + print_reloading_backend ipset + [ "$DISABLE_IPV4" != "1" ] && { + create_ipset 4 $ZIPSET hash:net "$IPSET_OPT" "$ZIPLIST" "$ZIPLIST_USER" + create_ipset 4 $ZIPSET_IPBAN hash:net "$IPSET_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN" + create_ipset 4 $ZIPSET_EXCLUDE hash:net "$IPSET_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE" + } + [ "$DISABLE_IPV6" != "1" ] && { + create_ipset 6 $ZIPSET6 hash:net "$IPSET_OPT" "$ZIPLIST6" "$ZIPLIST_USER6" + create_ipset 6 $ZIPSET_IPBAN6 hash:net "$IPSET_OPT" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6" + create_ipset 6 $ZIPSET_EXCLUDE6 hash:net "$IPSET_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE6" + } + true + ;; + nftables) + nft_create_table && { + SAVERAM=0 + RAMSIZE=$($GREP MemTotal /proc/meminfo | $AWK '{print $2}') + [ "$RAMSIZE" -lt "420000" ] && SAVERAM=1 + print_reloading_backend "nftables set" + [ "$DISABLE_IPV4" != "1" ] && { + create_nfset 4 $ZIPSET $SET_MAXELEM "$ZIPLIST" "$ZIPLIST_USER" + create_nfset 4 $ZIPSET_IPBAN $SET_MAXELEM "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN" + create_nfset 4 $ZIPSET_EXCLUDE $SET_MAXELEM_EXCLUDE "$ZIPLIST_EXCLUDE" + } + [ "$DISABLE_IPV6" != "1" ] && { + create_nfset 6 $ZIPSET6 $SET_MAXELEM "$ZIPLIST6" "$ZIPLIST_USER6" + create_nfset 6 $ZIPSET_IPBAN6 $SET_MAXELEM "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6" + create_nfset 6 $ZIPSET_EXCLUDE6 $SET_MAXELEM_EXCLUDE "$ZIPLIST_EXCLUDE6" + } + true + } + ;; + ipfw) + print_reloading_backend "ipfw table" + if [ "$DISABLE_IPV4" != "1" ] && [ "$DISABLE_IPV6" != "1" ]; then + create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST" "$ZIPLIST_USER" "$ZIPLIST6" "$ZIPLIST_USER6" + create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6" + create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE" "$ZIPLIST_EXCLUDE6" + elif [ "$DISABLE_IPV4" != "1" ]; then + create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST" "$ZIPLIST_USER" + create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN" + create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE" + elif [ "$DISABLE_IPV6" != "1" ]; then + create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST6" "$ZIPLIST_USER6" + create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6" + create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE6" + else + create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" + create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" + create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" + fi + true + ;; + *) + echo no supported ip list backend found + true + ;; + esac + fi diff --git a/ipset/def.sh b/ipset/def.sh index e8aff0c..37e31f7 100644 --- a/ipset/def.sh +++ b/ipset/def.sh @@ -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" - wc -c <"$f" | xargs + if [ -f "$f" ]; then + wc -c <"$f" | xargs + else + $ECHON -n 0 + fi } digger() diff --git a/uninstall_easy.sh b/uninstall_easy.sh index 22815c0..40e61a0 100755 --- a/uninstall_easy.sh +++ b/uninstall_easy.sh @@ -5,271 +5,66 @@ 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() { - crontab_del + 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 echo '!!! WARNING. YOUR UNINSTALL IS INCOMPLETE !!!' 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