From a01408a5c9a2873cfccb542895537543df743173 Mon Sep 17 00:00:00 2001 From: bol-van Date: Mon, 1 Dec 2025 15:26:56 +0300 Subject: [PATCH] init.d launch scripts --- common/base.sh | 8 +- common/custom.sh | 34 + common/ipt.sh | 333 +++++++++ common/linux_daemons.sh | 33 + common/linux_fw.sh | 40 ++ common/linux_iphelper.sh | 24 + common/list.sh | 55 ++ common/nft.sh | 668 ++++++++++++++++++ docs/changes.txt | 3 + .../10-keenetic-udp-fix | 22 + init.d/custom.d.examples.linux/20-fw-extra | 66 ++ init.d/custom.d.examples.linux/50-dht4all | 38 + .../custom.d.examples.linux/50-discord-media | 35 + init.d/custom.d.examples.linux/50-nfqws-ipset | 144 ++++ init.d/custom.d.examples.linux/50-quic4all | 30 + init.d/custom.d.examples.linux/50-stun4all | 30 + init.d/custom.d.examples.linux/50-wg4all | 32 + init.d/openrc/zapret | 69 ++ init.d/openwrt/90-zapret2 | 46 ++ init.d/openwrt/custom.d/.keep | 0 init.d/openwrt/firewall.zapret2 | 11 + init.d/openwrt/functions | 218 ++++++ init.d/openwrt/zapret2 | 135 ++++ init.d/systemd/nfqws2@.service | 62 ++ init.d/systemd/zapret2-list-update.service | 13 + init.d/systemd/zapret2-list-update.timer | 11 + init.d/systemd/zapret2.service | 17 + init.d/sysv/custom.d/.keep | 0 init.d/sysv/functions | 191 +++++ init.d/sysv/zapret2 | 82 +++ 30 files changed, 2446 insertions(+), 4 deletions(-) create mode 100644 common/custom.sh create mode 100644 common/ipt.sh create mode 100644 common/linux_daemons.sh create mode 100644 common/linux_fw.sh create mode 100644 common/linux_iphelper.sh create mode 100644 common/list.sh create mode 100644 common/nft.sh create mode 100644 init.d/custom.d.examples.linux/10-keenetic-udp-fix create mode 100644 init.d/custom.d.examples.linux/20-fw-extra create mode 100644 init.d/custom.d.examples.linux/50-dht4all create mode 100644 init.d/custom.d.examples.linux/50-discord-media create mode 100644 init.d/custom.d.examples.linux/50-nfqws-ipset create mode 100644 init.d/custom.d.examples.linux/50-quic4all create mode 100644 init.d/custom.d.examples.linux/50-stun4all create mode 100644 init.d/custom.d.examples.linux/50-wg4all create mode 100644 init.d/openrc/zapret create mode 100644 init.d/openwrt/90-zapret2 create mode 100644 init.d/openwrt/custom.d/.keep create mode 100644 init.d/openwrt/firewall.zapret2 create mode 100644 init.d/openwrt/functions create mode 100755 init.d/openwrt/zapret2 create mode 100644 init.d/systemd/nfqws2@.service create mode 100644 init.d/systemd/zapret2-list-update.service create mode 100644 init.d/systemd/zapret2-list-update.timer create mode 100644 init.d/systemd/zapret2.service create mode 100644 init.d/sysv/custom.d/.keep create mode 100644 init.d/sysv/functions create mode 100755 init.d/sysv/zapret2 diff --git a/common/base.sh b/common/base.sh index 15b2354..4b5d3cf 100644 --- a/common/base.sh +++ b/common/base.sh @@ -416,10 +416,10 @@ alloc_num() std_ports() { - NFQWS2_PORTS_TCP_IPT=$(replace_char - : $NFQWS_PORTS_TCP) - NFQWS2_PORTS_TCP_KEEPALIVE_IPT=$(replace_char - : $NFQWS_PORTS_TCP_KEEPALIVE) - NFQWS2_PORTS_UDP_IPT=$(replace_char - : $NFQWS_PORTS_UDP) - NFQWS2_PORTS_UDP_KEEPALIVE_IPT=$(replace_char - : $NFQWS_PORTS_UDP_KEEPALIVE) + NFQWS2_PORTS_TCP_IPT=$(replace_char - : $NFQWS2_PORTS_TCP) + NFQWS2_PORTS_TCP_KEEPALIVE_IPT=$(replace_char - : $NFQWS2_PORTS_TCP_KEEPALIVE) + NFQWS2_PORTS_UDP_IPT=$(replace_char - : $NFQWS2_PORTS_UDP) + NFQWS2_PORTS_UDP_KEEPALIVE_IPT=$(replace_char - : $NFQWS2_PORTS_UDP_KEEPALIVE) } has_bad_ws_options() diff --git a/common/custom.sh b/common/custom.sh new file mode 100644 index 0000000..de758f6 --- /dev/null +++ b/common/custom.sh @@ -0,0 +1,34 @@ +custom_runner() +{ + # $1 - function name + # $2+ - params + + [ "$DISABLE_CUSTOM" = 1 ] && return 0 + + local n script FUNC=$1 + + shift + + [ -d "$CUSTOM_DIR/custom.d" ] && { + dir_is_not_empty "$CUSTOM_DIR/custom.d" && { + for script in "$CUSTOM_DIR/custom.d/"*; do + [ -f "$script" ] || continue + unset -f $FUNC + . "$script" + existf $FUNC && $FUNC "$@" + done + } + } +} + +alloc_qnum() +{ + # $1 - target var name + alloc_num NUMPOOL_QNUM $1 65300 65399 +} +alloc_dnum() +{ + # alloc daemon number + # $1 - target var name + alloc_num NUMPOOL_DNUM $1 2000 2999 +} diff --git a/common/ipt.sh b/common/ipt.sh new file mode 100644 index 0000000..57bce20 --- /dev/null +++ b/common/ipt.sh @@ -0,0 +1,333 @@ +std_ports +ipt_connbytes="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes" +IPSET_EXCLUDE="-m set ! --match-set nozapret" +IPSET_EXCLUDE6="-m set ! --match-set nozapret6" +IPBAN_EXCLUDE="-m set ! --match-set ipban" +IPBAN_EXCLUDE6="-m set ! --match-set ipban6" + +ipt() +{ + iptables $FW_EXTRA_PRE -C "$@" $FW_EXTRA_POST >/dev/null 2>/dev/null || iptables $FW_EXTRA_PRE -I "$@" $FW_EXTRA_POST +} +ipta() +{ + iptables $FW_EXTRA_PRE -C "$@" $FW_EXTRA_POST >/dev/null 2>/dev/null || iptables $FW_EXTRA_PRE -A "$@" $FW_EXTRA_POST +} +ipt_del() +{ + iptables $FW_EXTRA_PRE -C "$@" $FW_EXTRA_POST >/dev/null 2>/dev/null && iptables $FW_EXTRA_PRE -D "$@" $FW_EXTRA_POST +} +ipt_add_del() +{ + on_off_function ipt ipt_del "$@" +} +ipta_add_del() +{ + on_off_function ipta ipt_del "$@" +} +ipt6() +{ + ip6tables -C "$@" >/dev/null 2>/dev/null || ip6tables -I "$@" +} +ipt6a() +{ + ip6tables -C "$@" >/dev/null 2>/dev/null || ip6tables -A "$@" +} +ipt6_del() +{ + ip6tables -C "$@" >/dev/null 2>/dev/null && ip6tables -D "$@" +} +ipt6_add_del() +{ + on_off_function ipt6 ipt6_del "$@" +} +ipt6a_add_del() +{ + on_off_function ipt6 ipt6a_del "$@" +} + +is_ipt_flow_offload_avail() +{ + # $1 = '' for ipv4, '6' for ipv6 + grep -q FLOWOFFLOAD 2>/dev/null /proc/net/ip$1_tables_targets +} + +filter_apply_ipset_target4() +{ + # $1 - var name of ipv4 iptables filter + if [ "$MODE_FILTER" = "ipset" ]; then + eval $1="\"\$$1 -m set --match-set zapret dst\"" + fi +} +filter_apply_ipset_target6() +{ + # $1 - var name of ipv6 iptables filter + if [ "$MODE_FILTER" = "ipset" ]; then + eval $1="\"\$$1 -m set --match-set zapret6 dst\"" + fi +} +filter_apply_ipset_target() +{ + # $1 - var name of ipv4 iptables filter + # $2 - var name of ipv6 iptables filter + filter_apply_ipset_target4 $1 + filter_apply_ipset_target6 $2 +} + +reverse_nfqws_rule_stream() +{ + sed -e 's/-o /-i /g' -e 's/--dport /--sport /g' -e 's/--dports /--sports /g' -e 's/ dst$/ src/' -e 's/ dst / src /g' -e 's/--connbytes-dir=original/--connbytes-dir=reply/g' -e "s/-m mark ! --mark $DESYNC_MARK\/$DESYNC_MARK//g" +} +reverse_nfqws_rule() +{ + echo "$@" | reverse_nfqws_rule_stream +} + +ipt_mark_filter() +{ + [ -n "$FILTER_MARK" ] && echo "-m mark --mark $FILTER_MARK/$FILTER_MARK" +} + +ipt_print_op() +{ + if [ "$1" = "1" ]; then + echo "Inserting ip$4tables rule for $3 : $2" + else + echo "Deleting ip$4tables rule for $3 : $2" + fi +} + + + +_fw_nfqws_post4() +{ + # $1 - 1 - add, 0 - del + # $2 - iptable filter for ipv4 + # $3 - queue number + # $4 - wan interface names space separated + [ "$DISABLE_IPV4" = "1" -o -z "$2" ] || { + local i + + ipt_print_op $1 "$2" "nfqws postrouting (qnum $3)" + + rule="$(ipt_mark_filter) -m mark ! --mark $DESYNC_MARK/$DESYNC_MARK $2 $IPSET_EXCLUDE dst -j NFQUEUE --queue-num $3 --queue-bypass" + if [ -n "$4" ] ; then + for i in $4; do + ipt_add_del $1 POSTROUTING -t mangle -o $i $rule + done + else + ipt_add_del $1 POSTROUTING -t mangle $rule + fi + } +} +_fw_nfqws_post6() +{ + # $1 - 1 - add, 0 - del + # $2 - iptable filter for ipv6 + # $3 - queue number + # $4 - wan interface names space separated + [ "$DISABLE_IPV6" = "1" -o -z "$2" ] || { + local i + + ipt_print_op $1 "$2" "nfqws postrouting (qnum $3)" 6 + + rule="$(ipt_mark_filter) -m mark ! --mark $DESYNC_MARK/$DESYNC_MARK $2 $IPSET_EXCLUDE6 dst -j NFQUEUE --queue-num $3 --queue-bypass" + if [ -n "$4" ] ; then + for i in $4; do + ipt6_add_del $1 POSTROUTING -t mangle -o $i $rule + done + else + ipt6_add_del $1 POSTROUTING -t mangle $rule + fi + } +} +fw_nfqws_post() +{ + # $1 - 1 - add, 0 - del + # $2 - iptable filter for ipv4 + # $3 - iptable filter for ipv6 + # $4 - queue number + fw_nfqws_post4 $1 "$2" $4 + fw_nfqws_post6 $1 "$3" $4 +} + +_fw_nfqws_pre4() +{ + # $1 - 1 - add, 0 - del + # $2 - iptable filter for ipv4 + # $3 - queue number + # $4 - wan interface names space separated + [ "$DISABLE_IPV4" = "1" -o -z "$2" ] || { + local i + + ipt_print_op $1 "$2" "nfqws input+forward (qnum $3)" + + rule="$2 $IPSET_EXCLUDE src -j NFQUEUE --queue-num $3 --queue-bypass" + if [ -n "$4" ] ; then + for i in $4; do + # iptables PREROUTING chain is before NAT. not possible to have DNATed ip's there + ipt_add_del $1 INPUT -t mangle -i $i $rule + ipt_add_del $1 FORWARD -t mangle -i $i $rule + done + else + ipt_add_del $1 INPUT -t mangle $rule + ipt_add_del $1 FORWARD -t mangle $rule + fi + } +} +_fw_nfqws_pre6() +{ + # $1 - 1 - add, 0 - del + # $2 - iptable filter for ipv6 + # $3 - queue number + # $4 - wan interface names space separated + [ "$DISABLE_IPV6" = "1" -o -z "$2" ] || { + local i + + ipt_print_op $1 "$2" "nfqws input+forward (qnum $3)" 6 + + rule="$2 $IPSET_EXCLUDE6 src -j NFQUEUE --queue-num $3 --queue-bypass" + if [ -n "$4" ] ; then + for i in $4; do + # iptables PREROUTING chain is before NAT. not possible to have DNATed ip's there + ipt6_add_del $1 INPUT -t mangle -i $i $rule + ipt6_add_del $1 FORWARD -t mangle -i $i $rule + done + else + ipt6_add_del $1 INPUT -t mangle $rule + ipt6_add_del $1 FORWARD -t mangle $rule + fi + } +} +fw_nfqws_pre() +{ + # $1 - 1 - add, 0 - del + # $2 - iptable filter for ipv4 + # $3 - iptable filter for ipv6 + # $4 - queue number + fw_nfqws_pre4 $1 "$2" $4 + fw_nfqws_pre6 $1 "$3" $4 +} + + +fw_reverse_nfqws_rule4() +{ + fw_nfqws_pre4 $1 "$(reverse_nfqws_rule "$2")" $3 +} +fw_reverse_nfqws_rule6() +{ + fw_nfqws_pre6 $1 "$(reverse_nfqws_rule "$2")" $3 +} +fw_reverse_nfqws_rule() +{ + # ensure that modes relying on incoming traffic work + # $1 - 1 - add, 0 - del + # $2 - rule4 + # $3 - rule6 + # $4 - queue number + fw_reverse_nfqws_rule4 $1 "$2" $4 + fw_reverse_nfqws_rule6 $1 "$3" $4 +} + +ipt_first_packets() +{ + # $1 - packet count + [ -n "$1" -a "$1" != keepalive ] && [ "$1" -ge 1 ] && echo "$ipt_connbytes 1:$1" +} +ipt_do_nfqws_in_out() +{ + # $1 - 1 - add, 0 - del + # $2 - tcp,udp + # $3 - ports + # $4 - PKT_OUT. special value : 'keepalive' + # $5 - PKT_IN + local f4 f6 first_packets_only + [ -n "$3" ] || return + [ -n "$4" -a "$4" != 0 ] && + { + first_packets_only="$(ipt_first_packets $4)" + f4="-p $2 -m multiport --dports $3 $first_packets_only" + f6=$f4 + filter_apply_ipset_target f4 f6 + fw_nfqws_post $1 "$f4" "$f6" $QNUM + } + [ -n "$5" -a "$5" != 0 ] && + { + first_packets_only="$(ipt_first_packets $5)" + f4="-p $2 -m multiport --dports $3 $first_packets_only" + f6=$f4 + filter_apply_ipset_target f4 f6 + fw_reverse_nfqws_rule $1 "$f4" "$f6" $QNUM + } +} + +zapret_do_firewall_standard_nfqws_rules_ipt() +{ + # $1 - 1 - add, 0 - del + + [ "$NFQWS2_ENABLE" = 1 ] && { + ipt_do_nfqws_in_out $1 tcp "$NFQWS2_PORTS_TCP_IPT" "$NFQWS2_TCP_PKT_OUT" "$NFQWS2_TCP_PKT_IN" + ipt_do_nfqws_in_out $1 tcp "$NFQWS2_PORTS_TCP_KEEPALIVE_IPT" keepalive "$NFQWS2_TCP_PKT_IN" + ipt_do_nfqws_in_out $1 udp "$NFQWS2_PORTS_UDP_IPT" "$NFQWS2_UDP_PKT_OUT" "$NFQWS2_UDP_PKT_IN" + ipt_do_nfqws_in_out $1 udp "$NFQWS2_PORTS_UDP_KEEPALIVE_IPT" keepalive "$NFQWS2_UDP_PKT_IN" + } +} +zapret_do_firewall_standard_rules_ipt() +{ + # $1 - 1 - add, 0 - del + + zapret_do_firewall_standard_nfqws_rules_ipt $1 +} + +zapret_do_firewall_rules_ipt() +{ + # $1 - 1 - add, 0 - del + + zapret_do_firewall_standard_rules_ipt $1 + custom_runner zapret_custom_firewall $1 + zapret_do_icmp_filter $1 +} + +zapret_do_icmp_filter() +{ + # $1 - 1 - add, 0 - del + + local FW_EXTRA_PRE= FW_EXTRA_POST= + + [ "$FILTER_TTL_EXPIRED_ICMP" = 1 ] && { + [ "$DISABLE_IPV4" = 1 ] || { + ipt_add_del $1 POSTROUTING -t mangle -m mark --mark $DESYNC_MARK/$DESYNC_MARK -j CONNMARK --or-mark $DESYNC_MARK + ipt_add_del $1 INPUT -p icmp -m icmp --icmp-type time-exceeded -m connmark --mark $DESYNC_MARK/$DESYNC_MARK -j DROP + ipt_add_del $1 FORWARD -p icmp -m icmp --icmp-type time-exceeded -m connmark --mark $DESYNC_MARK/$DESYNC_MARK -j DROP + } + [ "$DISABLE_IPV6" = 1 ] || { + ipt6_add_del $1 POSTROUTING -t mangle -m mark --mark $DESYNC_MARK/$DESYNC_MARK -j CONNMARK --or-mark $DESYNC_MARK + ipt6_add_del $1 INPUT -p icmpv6 -m icmp6 --icmpv6-type time-exceeded -m connmark --mark $DESYNC_MARK/$DESYNC_MARK -j DROP + ipt6_add_del $1 FORWARD -p icmpv6 -m icmp6 --icmpv6-type time-exceeded -m connmark --mark $DESYNC_MARK/$DESYNC_MARK -j DROP + } + } +} + +zapret_do_firewall_ipt() +{ + # $1 - 1 - add, 0 - del + + if [ "$1" = 1 ]; then + echo Applying iptables + else + echo Clearing iptables + fi + + # always create ipsets. ip_exclude ipset is required + [ "$1" = 1 ] && create_ipset no-update + + zapret_do_firewall_rules_ipt "$@" + + if [ "$1" = 1 ] ; then + existf flow_offloading_exempt && flow_offloading_exempt + else + existf flow_offloading_unexempt && flow_offloading_unexempt + fi + + return 0 +} diff --git a/common/linux_daemons.sh b/common/linux_daemons.sh new file mode 100644 index 0000000..2d5d406 --- /dev/null +++ b/common/linux_daemons.sh @@ -0,0 +1,33 @@ +standard_mode_nfqws() +{ + # $1 - 1 - run, 0 - stop + local opt + [ "$NFQWS2_ENABLE" = 1 ] && check_bad_ws_options $1 "$NFQWS2_OPT" && { + opt="--qnum=$QNUM $NFQWS2_OPT" + filter_apply_hostlist_target opt + do_nfqws $1 1 "$opt" + } +} +standard_mode_daemons() +{ + # $1 - 1 - run, 0 - stop + + standard_mode_nfqws $1 +} +zapret_do_daemons() +{ + # $1 - 1 - run, 0 - stop + + standard_mode_daemons $1 + custom_runner zapret_custom_daemons $1 + + return 0 +} +zapret_run_daemons() +{ + zapret_do_daemons 1 "$@" +} +zapret_stop_daemons() +{ + zapret_do_daemons 0 "$@" +} diff --git a/common/linux_fw.sh b/common/linux_fw.sh new file mode 100644 index 0000000..30d4b75 --- /dev/null +++ b/common/linux_fw.sh @@ -0,0 +1,40 @@ +set_conntrack_liberal_mode() +{ + [ -n "$SKIP_CONNTRACK_LIBERAL_MODE" ] || sysctl -w net.netfilter.nf_conntrack_tcp_be_liberal=$1 +} +zapret_do_firewall() +{ + linux_fwtype + + [ "$1" = 1 -a -n "$INIT_FW_PRE_UP_HOOK" ] && $INIT_FW_PRE_UP_HOOK + [ "$1" = 0 -a -n "$INIT_FW_PRE_DOWN_HOOK" ] && $INIT_FW_PRE_DOWN_HOOK + + case "$FWTYPE" in + iptables) + zapret_do_firewall_ipt "$@" + ;; + nftables) + zapret_do_firewall_nft "$@" + ;; + esac + + # russian DPI sends RST,ACK with wrong ACK. + # this is sometimes treated by conntrack as invalid and connbytes fw rules do not pass RST packet to nfqws. + # switch on liberal mode on zapret firewall start and switch off on zapret firewall stop + # this is only required for processing incoming bad RSTs. incoming rules are only applied in autohostlist mode + # calling this after firewall because conntrack module can be not loaded before applying conntrack firewall rules + [ "$MODE_FILTER" = "autohostlist" ] && set_conntrack_liberal_mode $1 + + [ "$1" = 1 -a -n "$INIT_FW_POST_UP_HOOK" ] && $INIT_FW_POST_UP_HOOK + [ "$1" = 0 -a -n "$INIT_FW_POST_DOWN_HOOK" ] && $INIT_FW_POST_DOWN_HOOK + + return 0 +} +zapret_apply_firewall() +{ + zapret_do_firewall 1 "$@" +} +zapret_unapply_firewall() +{ + zapret_do_firewall 0 "$@" +} diff --git a/common/linux_iphelper.sh b/common/linux_iphelper.sh new file mode 100644 index 0000000..912deae --- /dev/null +++ b/common/linux_iphelper.sh @@ -0,0 +1,24 @@ +get_uevent_devtype() +{ + local DEVTYPE INTERFACE IFINDEX OF_NAME OF_FULLNAME OF_COMPATIBLE_N + [ -f "/sys/class/net/$1/uevent" ] && { + . "/sys/class/net/$1/uevent" + echo -n $DEVTYPE + } +} +resolve_lower_devices() +{ + # $1 - bridge interface name + [ -d "/sys/class/net/$1" ] && { + find "/sys/class/net/$1" -follow -maxdepth 1 -name "lower_*" | + { + local l lower lowers + while read lower; do + lower="$(basename "$lower")" + l="${lower#lower_*}" + [ "$l" != "$lower" ] && append_separator_list lowers ' ' '' "$l" + done + printf "$lowers" + } + } +} diff --git a/common/list.sh b/common/list.sh new file mode 100644 index 0000000..0e23f88 --- /dev/null +++ b/common/list.sh @@ -0,0 +1,55 @@ +HOSTLIST_MARKER="" +HOSTLIST_NOAUTO_MARKER="" + +find_hostlists() +{ + [ -n "$HOSTLIST_BASE" ] || HOSTLIST_BASE="$ZAPRET_BASE/ipset" + + HOSTLIST="$HOSTLIST_BASE/zapret-hosts.txt.gz" + [ -f "$HOSTLIST" ] || HOSTLIST="$HOSTLIST_BASE/zapret-hosts.txt" + [ -f "$HOSTLIST" ] || HOSTLIST= + + HOSTLIST_USER="$HOSTLIST_BASE/zapret-hosts-user.txt.gz" + [ -f "$HOSTLIST_USER" ] || HOSTLIST_USER="$HOSTLIST_BASE/zapret-hosts-user.txt" + [ -f "$HOSTLIST_USER" ] || HOSTLIST_USER= + + HOSTLIST_EXCLUDE="$HOSTLIST_BASE/zapret-hosts-user-exclude.txt.gz" + [ -f "$HOSTLIST_EXCLUDE" ] || HOSTLIST_EXCLUDE="$HOSTLIST_BASE/zapret-hosts-user-exclude.txt" + [ -f "$HOSTLIST_EXCLUDE" ] || HOSTLIST_EXCLUDE= + + HOSTLIST_AUTO="$HOSTLIST_BASE/zapret-hosts-auto.txt" + HOSTLIST_AUTO_DEBUGLOG="$HOSTLIST_BASE/zapret-hosts-auto-debug.log" +} + +filter_apply_hostlist_target() +{ + # $1 - var name of nfqws params + + local v parm parm1 parm2 parm3 parm4 parm5 parm6 parm7 parm8 parmNA + eval v="\$$1" + if contains "$v" "$HOSTLIST_MARKER" || contains "$v" "$HOSTLIST_NOAUTO_MARKER"; then + [ "$MODE_FILTER" = hostlist -o "$MODE_FILTER" = autohostlist ] && + { + find_hostlists + parm1="${HOSTLIST_USER:+--hostlist=$HOSTLIST_USER}" + parm2="${HOSTLIST:+--hostlist=$HOSTLIST}" + parm3="${HOSTLIST_EXCLUDE:+--hostlist-exclude=$HOSTLIST_EXCLUDE}" + [ "$MODE_FILTER" = autohostlist ] && + { + parm4="--hostlist-auto=$HOSTLIST_AUTO" + parm5="${AUTOHOSTLIST_FAIL_THRESHOLD:+--hostlist-auto-fail-threshold=$AUTOHOSTLIST_FAIL_THRESHOLD}" + parm6="${AUTOHOSTLIST_FAIL_TIME:+--hostlist-auto-fail-time=$AUTOHOSTLIST_FAIL_TIME}" + parm7="${AUTOHOSTLIST_RETRANS_THRESHOLD:+--hostlist-auto-retrans-threshold=$AUTOHOSTLIST_RETRANS_THRESHOLD}" + parm8="--hostlist=$HOSTLIST_AUTO" + } + parm="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm4:+ $parm4}${parm5:+ $parm5}${parm6:+ $parm6}${parm7:+ $parm7}" + parmNA="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm8:+ $parm8}" + } + v="$(replace_str $HOSTLIST_NOAUTO_MARKER "$parmNA" "$v")" + v="$(replace_str $HOSTLIST_MARKER "$parm" "$v")" + [ "$MODE_FILTER" = autohostlist -a "$AUTOHOSTLIST_DEBUGLOG" = 1 ] && { + v="$v --hostlist-auto-debug=$HOSTLIST_AUTO_DEBUGLOG" + } + eval $1=\""$v"\" + fi +} diff --git a/common/nft.sh b/common/nft.sh new file mode 100644 index 0000000..4e0cbb5 --- /dev/null +++ b/common/nft.sh @@ -0,0 +1,668 @@ +[ -n "$ZAPRET_NFT_TABLE" ] || ZAPRET_NFT_TABLE=zapret2 +nft_connbytes="ct original packets" + +# required for : nft -f - +create_dev_stdin +std_ports + +nft_create_table() +{ + nft add table inet $ZAPRET_NFT_TABLE +} +nft_del_table() +{ + nft delete table inet $ZAPRET_NFT_TABLE 2>/dev/null +} +nft_list_table() +{ + nft -t list table inet $ZAPRET_NFT_TABLE +} + +nft_create_set() +{ + # $1 - set name + # $2 - params + nft create set inet $ZAPRET_NFT_TABLE $1 "{ $2 }" 2>/dev/null +} +nft_del_set() +{ + # $1 - set name + nft delete set inet $ZAPRET_NFT_TABLE $1 +} +nft_flush_set() +{ + # $1 - set name + nft flush set inet $ZAPRET_NFT_TABLE $1 +} +nft_set_exists() +{ + # $1 - set name + nft -t list set inet $ZAPRET_NFT_TABLE $1 2>/dev/null >/dev/null +} +nft_flush_chain() +{ + # $1 - chain name + nft flush chain inet $ZAPRET_NFT_TABLE $1 +} + +nft_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_create_chains() +{ + local b rule + +cat << EOF | nft -f - + add chain inet $ZAPRET_NFT_TABLE forward_hook { type filter hook forward priority -1; } + flush chain inet $ZAPRET_NFT_TABLE forward_hook + + add chain inet $ZAPRET_NFT_TABLE flow_offload + flush chain inet $ZAPRET_NFT_TABLE flow_offload + add chain inet $ZAPRET_NFT_TABLE flow_offload_zapret + flush chain inet $ZAPRET_NFT_TABLE flow_offload_zapret + add chain inet $ZAPRET_NFT_TABLE flow_offload_always + flush chain inet $ZAPRET_NFT_TABLE flow_offload_always + + add chain inet $ZAPRET_NFT_TABLE postrouting + flush chain inet $ZAPRET_NFT_TABLE postrouting + add chain inet $ZAPRET_NFT_TABLE postrouting_hook { type filter hook postrouting priority 99; } + flush chain inet $ZAPRET_NFT_TABLE postrouting_hook + + add chain inet $ZAPRET_NFT_TABLE postnat + flush chain inet $ZAPRET_NFT_TABLE postnat + add chain inet $ZAPRET_NFT_TABLE postnat_hook { type filter hook postrouting priority 101; } + flush chain inet $ZAPRET_NFT_TABLE postnat_hook + + add chain inet $ZAPRET_NFT_TABLE prerouting_hook { type filter hook prerouting priority -99; } + flush chain inet $ZAPRET_NFT_TABLE prerouting_hook + add chain inet $ZAPRET_NFT_TABLE prerouting + flush chain inet $ZAPRET_NFT_TABLE prerouting + + add chain inet $ZAPRET_NFT_TABLE prenat_hook { type filter hook prerouting priority -101; } + flush chain inet $ZAPRET_NFT_TABLE prenat_hook + add chain inet $ZAPRET_NFT_TABLE prenat + flush chain inet $ZAPRET_NFT_TABLE prenat + + add chain inet $ZAPRET_NFT_TABLE predefrag { type filter hook output priority -401; } + flush chain inet $ZAPRET_NFT_TABLE predefrag + add chain inet $ZAPRET_NFT_TABLE predefrag_nfqws + flush chain inet $ZAPRET_NFT_TABLE predefrag_nfqws + add rule inet $ZAPRET_NFT_TABLE predefrag mark and $DESYNC_MARK !=0 jump predefrag_nfqws comment "nfqws generated : avoid drop by INVALID conntrack state" + add rule inet $ZAPRET_NFT_TABLE predefrag_nfqws mark and $DESYNC_MARK_POSTNAT !=0 notrack comment "postnat traffic" + add rule inet $ZAPRET_NFT_TABLE predefrag_nfqws ip frag-off & 0x1fff != 0 notrack comment "ipfrag" + add rule inet $ZAPRET_NFT_TABLE predefrag_nfqws exthdr frag exists notrack comment "ipfrag" + add rule inet $ZAPRET_NFT_TABLE predefrag_nfqws tcp flags ! syn,rst,ack notrack comment "datanoack" + + add set inet $ZAPRET_NFT_TABLE wanif { type ifname; } + add set inet $ZAPRET_NFT_TABLE wanif6 { type ifname; } + +EOF + [ -n "$POSTNAT_ALL" ] && { + nft_flush_chain predefrag_nfqws + nft_add_rule predefrag_nfqws notrack comment \"do not track nfqws generated packets to avoid nat tampering and defragmentation\" + } + [ "$FILTER_TTL_EXPIRED_ICMP" = 1 ] && { + if is_postnat; then + # can be caused by untracked nfqws-generated packets + nft_add_rule prerouting_hook icmp type time-exceeded ct state invalid drop + else + nft_add_rule postrouting_hook mark and $DESYNC_MARK != 0 ct mark set ct mark or $DESYNC_MARK comment \"nfqws related : prevent ttl expired socket errors\" + fi + [ "$DISABLE_IPV4" = "1" ] || { + nft_add_rule prerouting_hook icmp type time-exceeded ct mark and $DESYNC_MARK != 0 drop comment \"nfqws related : prevent ttl expired socket errors\" + } + [ "$DISABLE_IPV6" = "1" ] || { + nft_add_rule prerouting_hook icmpv6 type time-exceeded ct mark and $DESYNC_MARK != 0 drop comment \"nfqws related : prevent ttl expired socket errors\" + } + } + [ "$DISABLE_IPV4" = "1" ] || { + b=0 + nft_wanif_filter_present && b=1 + + rule="mark and $DESYNC_MARK == 0" + [ $b = 1 ] && rule="$rule oifname @wanif" + rule="$rule ip daddr != @nozapret" + nft_add_rule postrouting_hook $rule jump postrouting + nft_add_rule postnat_hook $rule jump postnat + + rule="mark and $DESYNC_MARK == 0" + [ $b = 1 ] && rule="$rule iifname @wanif" + rule="$rule ip daddr != @nozapret" + nft_add_rule prerouting_hook $rule jump prerouting + nft_add_rule prenat_hook $rule jump prenat + } + [ "$DISABLE_IPV6" = "1" ] || { + b=0 + nft_wanif6_filter_present && b=1 + + rule="mark and $DESYNC_MARK == 0" + [ $b = 1 ] && rule="$rule oifname @wanif6" + rule="$rule ip6 daddr != @nozapret6" + nft_add_rule postrouting_hook $rule jump postrouting + nft_add_rule postnat_hook $rule jump postnat + + rule="mark and $DESYNC_MARK == 0" + [ $b = 1 ] && rule="$rule iifname @wanif6" + rule="$rule ip6 daddr != @nozapret6" + nft_add_rule prerouting_hook $rule jump prerouting + nft_add_rule prenat_hook $rule jump prenat + } + +} +nft_del_chains() +{ + # do not delete all chains because of additional user hooks + # they must be inside zapret table to use nfsets + +cat << EOF | nft -f - 2>/dev/null + delete chain inet $ZAPRET_NFT_TABLE postrouting_hook + delete chain inet $ZAPRET_NFT_TABLE postnat_hook + delete chain inet $ZAPRET_NFT_TABLE prerouting_hook + delete chain inet $ZAPRET_NFT_TABLE prenat_hook + delete chain inet $ZAPRET_NFT_TABLE forward_hook + delete chain inet $ZAPRET_NFT_TABLE postrouting + delete chain inet $ZAPRET_NFT_TABLE postnat + delete chain inet $ZAPRET_NFT_TABLE prerouting + delete chain inet $ZAPRET_NFT_TABLE prenat + delete chain inet $ZAPRET_NFT_TABLE predefrag + delete chain inet $ZAPRET_NFT_TABLE predefrag_nfqws + delete chain inet $ZAPRET_NFT_TABLE flow_offload + delete chain inet $ZAPRET_NFT_TABLE flow_offload_zapret + delete chain inet $ZAPRET_NFT_TABLE flow_offload_always +EOF +# unfortunately this approach breaks udp desync of the connection initiating packet (new, first one) +# delete chain inet $ZAPRET_NFT_TABLE predefrag +} +nft_del_flowtable() +{ + nft delete flowtable inet $ZAPRET_NFT_TABLE ft 2>/dev/null +} +nft_create_or_update_flowtable() +{ + # $1 = flags ('offload' for hw offload) + # $2,$3,$4,... - interfaces + # can be called multiple times to add interfaces. interfaces can only be added , not removed + local flags=$1 devices makelist + shift + # warning ! nft versions at least up to 1.0.1 do not allow interface names starting with digit in flowtable and do not allow quoting + # warning ! openwrt fixes this in post-21.x snapshots with special nft patch + # warning ! in traditional linux distros nft is unpatched and will fail with quoted interface definitions if unfixed + [ -n "$flags" ] && flags="flags $flags;" + for makelist in make_quoted_comma_list make_comma_list; do + $makelist devices "$@" + [ -n "$devices" ] && devices="devices={$devices};" + nft add flowtable inet $ZAPRET_NFT_TABLE ft "{ hook ingress priority -1; $flags $devices }" && break + done +} +nft_flush_ifsets() +{ +cat << EOF | nft -f - 2>/dev/null + flush set inet $ZAPRET_NFT_TABLE wanif + flush set inet $ZAPRET_NFT_TABLE wanif6 +EOF +} +nft_list_ifsets() +{ + nft list set inet $ZAPRET_NFT_TABLE wanif + nft list set inet $ZAPRET_NFT_TABLE wanif6 + nft list flowtable inet $ZAPRET_NFT_TABLE ft 2>/dev/null +} + +nft_create_firewall() +{ + nft_create_table + nft_del_flowtable + nft_create_chains +} +nft_del_firewall() +{ + nft_del_chains + nft_del_flowtable + # leave ifsets and ipsets because they may be used by custom rules +} + +nft_add_rule() +{ + # $1 - chain + # $2,$3,... - rule(s) + local chain="$1" + shift + nft add rule inet $ZAPRET_NFT_TABLE $chain $FW_EXTRA_PRE "$@" +} +nft_insert_rule() +{ + # $1 - chain + # $2,$3,... - rule(s) + local chain="$1" + shift + nft insert rule inet $ZAPRET_NFT_TABLE $chain $FW_EXTRA_PRE "$@" +} +nft_add_set_element() +{ + # $1 - set or map name + # $2 - element + [ -z "$2" ] || nft add element inet $ZAPRET_NFT_TABLE $1 "{ $2 }" +} +nft_add_set_elements() +{ + # $1 - set or map name + # $2,$3,... - element(s) + local set="$1" elements + shift + make_comma_list elements "$@" + nft_add_set_element $set "$elements" +} +nft_reverse_nfqws_rule() +{ + echo "$@" | sed -e 's/oifname /iifname /g' -e 's/dport /sport /g' -e 's/daddr /saddr /g' -e 's/ct original /ct reply /g' -e "s/mark and $DESYNC_MARK == 0//g" +} +nft_add_nfqws_flow_exempt_rule() +{ + # $1 - rule (must be all filters in one var) + local FW_EXTRA_POST= FW_EXTRA_PRE= + [ "$FLOWOFFLOAD" = 'software' -o "$FLOWOFFLOAD" = 'hardware' ] && \ + nft_insert_rule flow_offload_zapret "$1" return comment \"direct flow offloading exemption\" +} + +nft_apply_flow_offloading() +{ + # ft can be absent + nft_add_rule flow_offload_always flow add @ft 2>/dev/null && { + nft_add_rule flow_offload_always counter comment \"if offload works here must not be too much traffic\" + + [ "$DISABLE_IPV4" = "1" ] || { + # allow only outgoing packets to initiate flow offload + nft_add_rule forward_hook oifname @wanif meta l4proto "{ tcp, udp }" jump flow_offload + nft_add_rule flow_offload ip daddr == @nozapret jump flow_offload_always + } + [ "$DISABLE_IPV6" = "1" ] || { + nft_add_rule forward_hook oifname @wanif6 meta l4proto "{ tcp, udp }" jump flow_offload + nft_add_rule flow_offload ip6 daddr == @nozapret6 jump flow_offload_always + } + nft_add_rule flow_offload jump flow_offload_zapret + + nft_add_rule flow_offload_zapret jump flow_offload_always + } +} + + + +nft_filter_apply_ipset_target4() +{ + # $1 - var name of ipv4 nftables filter + if [ "$MODE_FILTER" = "ipset" ]; then + eval $1="\"\$$1 ip daddr @zapret\"" + fi +} +nft_filter_apply_ipset_target6() +{ + # $1 - var name of ipv6 nftables filter + if [ "$MODE_FILTER" = "ipset" ]; then + eval $1="\"\$$1 ip6 daddr @zapret6\"" + fi +} +nft_filter_apply_ipset_target() +{ + # $1 - var name of ipv4 nftables filter + # $2 - var name of ipv6 nftables filter + nft_filter_apply_ipset_target4 $1 + nft_filter_apply_ipset_target6 $2 +} + +nft_mark_filter() +{ + [ -n "$FILTER_MARK" ] && echo "mark and $FILTER_MARK != 0" +} + +nft_script_add_ifset_element() +{ + # $1 - set name + # $2 - space separated elements + local elements + [ -n "$2" ] && { + make_quoted_comma_list elements $2 + script="${script} +add element inet $ZAPRET_NFT_TABLE $1 { $elements }" + } +} +nft_fill_ifsets() +{ + # $1 - space separated lan interface names + # $2 - space separated wan interface names + # $3 - space separated wan6 interface names + # 4,5,6 is needed for pppoe+openwrt case. looks like it's not easily possible to resolve ethernet device behind a pppoe interface + # $4 - space separated lan physical interface names (optional) + # $5 - space separated wan physical interface names (optional) + # $6 - space separated wan6 physical interface names (optional) + + local script i j ALLDEVS devs b + + # if large sets exist nft works very ineffectively + # looks like it analyzes the whole table blob to find required data pieces + # calling all in one shot helps not to waste cpu time many times + + script="flush set inet $ZAPRET_NFT_TABLE wanif +flush set inet $ZAPRET_NFT_TABLE wanif6" + + [ "$DISABLE_IPV4" = "1" ] || nft_script_add_ifset_element wanif "$2" + [ "$DISABLE_IPV6" = "1" ] || nft_script_add_ifset_element wanif6 "$3" + + echo "$script" | nft -f - + + case "$FLOWOFFLOAD" in + software) + ALLDEVS=$(unique $1 $2 $3) + # unbound flowtable may cause error in older nft version + nft_create_or_update_flowtable '' $ALLDEVS 2>/dev/null + ;; + hardware) + ALLDEVS=$(unique $1 $2 $3 $4 $5 $6) + # first create unbound flowtable. may cause error in older nft version + nft_create_or_update_flowtable 'offload' 2>/dev/null + # then add elements. some of them can cause error because unsupported + for i in $ALLDEVS; do + # bridge members must be added instead of the bridge itself + # some members may not support hw offload. example : lan1 lan2 lan3 support, wlan0 wlan1 - not + b= + devs=$(resolve_lower_devices $i) + for j in $devs; do + # do not display error if addition failed + nft_create_or_update_flowtable 'offload' $j && b=1 2>/dev/null + done + [ -n "$b" ] || { + # no lower devices added ? try to add interface itself + nft_create_or_update_flowtable 'offload' $i 2>/dev/null + } + done + ;; + esac +} + +nft_only() +{ + linux_fwtype + + case "$FWTYPE" in + nftables) + "$@" + ;; + esac +} + + +nft_print_op() +{ + echo "Inserting nftables ipv$3 rule for $2 : $1" +} +is_postnat() +{ + [ "$POSTNAT" != 0 -o "$POSTNAT_ALL" = 1 ] +} +get_postchain() +{ + if is_postnat ; then + echo -n postnat + else + echo -n postrouting + fi +} +get_prechain() +{ + if is_postnat ; then + echo -n prenat + else + echo -n prerouting + fi +} +_nft_fw_nfqws_post4() +{ + # $1 - filter ipv4 + # $2 - queue number + # $3 - not-empty if wan interface filtering required + + [ "$DISABLE_IPV4" = "1" -o -z "$1" ] || { + local filter="$1" port="$2" rule chain=$(get_postchain) setmark + nft_print_op "$filter" "nfqws postrouting (qnum $port)" 4 + rule="meta nfproto ipv4 $(nft_mark_filter) $filter" + is_postnat && setmark="meta mark set meta mark or $DESYNC_MARK_POSTNAT" + nft_insert_rule $chain $rule $setmark $CONNMARKER $FW_EXTRA_POST queue num $port bypass + nft_add_nfqws_flow_exempt_rule "$rule" + } +} +_nft_fw_nfqws_post6() +{ + # $1 - filter ipv6 + # $2 - queue number + # $3 - not-empty if wan interface filtering required + + [ "$DISABLE_IPV6" = "1" -o -z "$1" ] || { + local filter="$1" port="$2" rule chain=$(get_postchain) setmark + nft_print_op "$filter" "nfqws postrouting (qnum $port)" 6 + rule="meta nfproto ipv6 $(nft_mark_filter) $filter" + is_postnat && setmark="meta mark set meta mark or $DESYNC_MARK_POSTNAT" + nft_insert_rule $chain $rule $setmark $CONNMARKER $FW_EXTRA_POST queue num $port bypass + nft_add_nfqws_flow_exempt_rule "$rule" + } +} +nft_fw_nfqws_post() +{ + # $1 - filter ipv4 + # $2 - filter ipv6 + # $3 - queue number + + nft_fw_nfqws_post4 "$1" $3 + nft_fw_nfqws_post6 "$2" $3 +} + +_nft_fw_nfqws_pre4() +{ + # $1 - filter ipv4 + # $2 - queue number + # $3 - not-empty if wan interface filtering required + + [ "$DISABLE_IPV4" = "1" -o -z "$1" ] || { + local filter="$1" port="$2" rule + nft_print_op "$filter" "nfqws prerouting (qnum $port)" 4 + rule="meta nfproto ipv4 $filter" + nft_insert_rule $(get_prechain) $rule $CONNMARKER $FW_EXTRA_POST queue num $port bypass + } +} +_nft_fw_nfqws_pre6() +{ + # $1 - filter ipv6 + # $2 - queue number + # $3 - not-empty if wan interface filtering required + + [ "$DISABLE_IPV6" = "1" -o -z "$1" ] || { + local filter="$1" port="$2" rule + nft_print_op "$filter" "nfqws prerouting (qnum $port)" 6 + rule="meta nfproto ipv6 $filter" + nft_insert_rule $(get_prechain) $rule $CONNMARKER $FW_EXTRA_POST queue num $port bypass + } +} +nft_fw_nfqws_pre() +{ + # $1 - filter ipv4 + # $2 - filter ipv6 + # $3 - queue number + + nft_fw_nfqws_pre4 "$1" $3 + nft_fw_nfqws_pre6 "$2" $3 +} + +nft_fw_nfqws_both4() +{ + # $1 - filter ipv4 + # $2 - queue number + nft_fw_nfqws_post4 "$@" + nft_fw_nfqws_pre4 "$(nft_reverse_nfqws_rule $1)" $2 +} +nft_fw_nfqws_both6() +{ + # $1 - filter ipv6 + # $2 - queue number + nft_fw_nfqws_post6 "$@" + nft_fw_nfqws_pre6 "$(nft_reverse_nfqws_rule $1)" $2 +} +nft_fw_nfqws_both() +{ + # $1 - filter ipv4 + # $2 - filter ipv6 + # $3 - queue number + nft_fw_nfqws_both4 "$1" "$3" + nft_fw_nfqws_both6 "$2" "$3" +} + +zapret_reload_ifsets() +{ + nft_only nft_create_table ; nft_fill_ifsets_overload + return 0 +} +zapret_list_ifsets() +{ + nft_only nft_list_ifsets + return 0 +} +zapret_list_table() +{ + nft_only nft_list_table + return 0 +} + + + +nft_fw_reverse_nfqws_rule4() +{ + nft_fw_nfqws_pre4 "$(nft_reverse_nfqws_rule "$1")" $2 +} +nft_fw_reverse_nfqws_rule6() +{ + nft_fw_nfqws_pre6 "$(nft_reverse_nfqws_rule "$1")" $2 +} +nft_fw_reverse_nfqws_rule() +{ + # ensure that modes relying on incoming traffic work + # $1 - rule4 + # $2 - rule6 + # $3 - queue number + nft_fw_reverse_nfqws_rule4 "$1" $3 + nft_fw_reverse_nfqws_rule6 "$2" $3 +} + +nft_first_packets() +{ + # $1 - packet count + [ -n "$1" -a "$1" != keepalive ] && [ "$1" -ge 1 ] && + { + if [ "$1" = 1 ] ; then + echo "$nft_connbytes 1" + else + echo "$nft_connbytes 1-$1" + fi + } +} + +nft_apply_nfqws_in_out() +{ + # $1 - tcp,udp + # $2 - ports + # $3 - PKT_OUT. special value : 'keepalive' + # $4 - PKT_IN + local f4 f6 first_packets_only + [ -n "$2" ] || return + [ -n "$3" -a "$3" != 0 ] && + { + first_packets_only="$(nft_first_packets $3)" + f4="$1 dport {$2} $first_packets_only" + f6=$f4 + nft_filter_apply_ipset_target f4 f6 + nft_fw_nfqws_post "$f4" "$f6" $QNUM + } + [ -n "$4" -a "$4" != 0 ] && + { + first_packets_only="$(nft_first_packets $4)" + f4="$1 dport {$2} $first_packets_only" + f6=$f4 + nft_filter_apply_ipset_target f4 f6 + nft_fw_reverse_nfqws_rule "$f4" "$f6" $QNUM + } +} + +zapret_apply_firewall_standard_nfqws_rules_nft() +{ + [ "$NFQWS2_ENABLE" = 1 ] && { + nft_apply_nfqws_in_out tcp "$NFQWS2_PORTS_TCP" "$NFQWS2_TCP_PKT_OUT" "$NFQWS2_TCP_PKT_IN" + nft_apply_nfqws_in_out tcp "$NFQWS2_PORTS_TCP_KEEPALIVE" keepalive "$NFQWS2_TCP_PKT_IN" + nft_apply_nfqws_in_out udp "$NFQWS2_PORTS_UDP" "$NFQWS2_UDP_PKT_OUT" "$NFQWS2_UDP_PKT_IN" + nft_apply_nfqws_in_out udp "$NFQWS2_PORTS_UDP_KEEPALIVE" keepalive "$NFQWS2_UDP_PKT_IN" + } +} +zapret_apply_firewall_standard_rules_nft() +{ + zapret_apply_firewall_standard_nfqws_rules_nft +} + +zapret_apply_firewall_rules_nft() +{ + zapret_apply_firewall_standard_rules_nft + custom_runner zapret_custom_firewall_nft +} + +zapret_apply_firewall_nft() +{ + echo Applying nftables + + create_ipset no-update + nft_create_firewall + nft_fill_ifsets_overload + + zapret_apply_firewall_rules_nft + + [ "$FLOWOFFLOAD" = 'software' -o "$FLOWOFFLOAD" = 'hardware' ] && nft_apply_flow_offloading + + return 0 +} +zapret_unapply_firewall_nft() +{ + echo Clearing nftables + + nft_del_firewall + custom_runner zapret_custom_firewall_nft_flush + return 0 +} +zapret_do_firewall_nft() +{ + # $1 - 1 - add, 0 - del + + if [ "$1" = 0 ] ; then + zapret_unapply_firewall_nft + else + zapret_apply_firewall_nft + fi + + return 0 +} + +# ctmark is not available in POSTNAT mode +CONNMARKER= +[ "$FILTER_TTL_EXPIRED_ICMP" = 1 ] && is_postnat && CONNMARKER="ct mark set ct mark or $DESYNC_MARK" diff --git a/docs/changes.txt b/docs/changes.txt index 64dbb32..ab9cde9 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -34,3 +34,6 @@ v0.2 * zapret-lib: bugfixes * zapret-lib: remove ip6_hopbyhop_x2 fooling, separately add second hopbyhop header using ip6_hopbyhop2 * zapret-pcap + +v0.3 +* init.d launch scripts diff --git a/init.d/custom.d.examples.linux/10-keenetic-udp-fix b/init.d/custom.d.examples.linux/10-keenetic-udp-fix new file mode 100644 index 0000000..f9cab11 --- /dev/null +++ b/init.d/custom.d.examples.linux/10-keenetic-udp-fix @@ -0,0 +1,22 @@ +# This script fixes keenetic issue with nfqws generated udp packets +# Keenetic uses proprietary ndmmark and does not masquerade without this mark +# If not masqueraded packets go to WAN with LAN IP and get dropped by ISP + +# It's advised to set IFACE_WAN in config + +zapret_custom_firewall() +{ + # $1 - 1 - add, 0 - stop + + local wan wanif rule + + [ "$DISABLE_IPV4" = "1" ] || { + # use IFACE_WAN if defined. if not - search for interfaces with default route. + wanif=${IFACE_WAN:-$(sed -nre 's/^([^\t]+)\t00000000\t[0-9A-F]{8}\t[0-9A-F]{4}\t[0-9]+\t[0-9]+\t[0-9]+\t00000000.*$/\1/p' /proc/net/route | sort -u | xargs)} + for wan in $wanif; do + rule="-o $wan -p udp -m mark --mark $DESYNC_MARK/$DESYNC_MARK" + ipt_print_op $1 "$rule" "keenetic udp fix" + ipt_add_del $1 POSTROUTING -t nat $rule -j MASQUERADE + done + } +} diff --git a/init.d/custom.d.examples.linux/20-fw-extra b/init.d/custom.d.examples.linux/20-fw-extra new file mode 100644 index 0000000..c93ef63 --- /dev/null +++ b/init.d/custom.d.examples.linux/20-fw-extra @@ -0,0 +1,66 @@ +# this custom script runs standard mode with extra firewall rules + +# config: use TPWS_ENABLE_OVERRIDE, NFQWS_ENABLE_OVERRIDE to enable standard mode daemons +# standard and override switches cannot be enabled simultaneously ! + +TPWS_ENABLE_OVERRIDE=${TPWS_ENABLE_OVERRIDE:-0} +NFQWS_ENABLE_OVERRIDE=${NFQWS_ENABLE_OVERRIDE:-0} + +# config: some if these values must be set in config. not setting any of these makes this script meaningless. +# pre vars put ipt/nft code to the rule beginning +#FW_EXTRA_PRE_TPWS_IPT= +#FW_EXTRA_PRE_TPWS_NFT= +#FW_EXTRA_PRE_NFQWS_IPT="-m mark --mark 0x10000000/0x10000000" +#FW_EXTRA_PRE_NFQWS_NFT="mark and 0x10000000 != 0" +# post vars put ipt/nft code to the rule end +#FW_EXTRA_POST_TPWS_IPT= +#FW_EXTRA_POST_TPWS_NFT= +#FW_EXTRA_POST_NFQWS_IPT= +#FW_EXTRA_POST_NFQWS_NFT= + +check_std_intersect() +{ + [ "$TPWS_ENABLE_OVERRIDE" = 1 -a "$TPWS_ENABLE" = 1 ] && { + echo "ERROR ! both TPWS_ENABLE_OVERRIDE and TPWS_ENABLE are enabled" + return 1 + } + [ "$NFQWS_ENABLE_OVERRIDE" = 1 -a "$NFQWS_ENABLE" = 1 ] && { + echo "ERROR ! both NFQWS_ENABLE_OVERRIDE and NFQWS_ENABLE are enabled" + return 1 + } + return 0 +} + +zapret_custom_daemons() +{ + # $1 - 1 - add, 0 - stop + + check_std_intersect || return + + local TPWS_SOCKS_ENABLE=0 TPWS_ENABLE=$TPWS_ENABLE_OVERRIDE NFQWS_ENABLE=$NFQWS_ENABLE_OVERRIDE + standard_mode_daemons "$1" +} +zapret_custom_firewall() +{ + # $1 - 1 - run, 0 - stop + + check_std_intersect || return + + local FW_EXTRA_PRE FW_EXTRA_POST TPWS_ENABLE=$TPWS_ENABLE_OVERRIDE NFQWS_ENABLE=$NFQWS_ENABLE_OVERRIDE + FW_EXTRA_PRE="$FW_EXTRA_PRE_TPWS_IPT" FW_EXTRA_POST="$FW_EXTRA_POST_TPWS_IPT" + zapret_do_firewall_standard_tpws_rules_ipt $1 + FW_EXTRA_PRE="$FW_EXTRA_PRE_NFQWS_IPT" FW_EXTRA_POST="$FW_EXTRA_POST_NFQWS_IPT" + zapret_do_firewall_standard_nfqws_rules_ipt $1 +} +zapret_custom_firewall_nft() +{ + # stop logic is not required + + check_std_intersect || return + + local FW_EXTRA_PRE FW_EXTRA_POST TPWS_ENABLE=$TPWS_ENABLE_OVERRIDE NFQWS_ENABLE=$NFQWS_ENABLE_OVERRIDE + FW_EXTRA_PRE="$FW_EXTRA_PRE_TPWS_NFT" FW_EXTRA_POST="$FW_EXTRA_POST_TPWS_NFT" + zapret_apply_firewall_standard_tpws_rules_nft + FW_EXTRA_PRE="$FW_EXTRA_PRE_NFQWS_NFT" FW_EXTRA_POST="$FW_EXTRA_POST_NFQWS_NFT" + zapret_apply_firewall_standard_nfqws_rules_nft +} diff --git a/init.d/custom.d.examples.linux/50-dht4all b/init.d/custom.d.examples.linux/50-dht4all new file mode 100644 index 0000000..391473c --- /dev/null +++ b/init.d/custom.d.examples.linux/50-dht4all @@ -0,0 +1,38 @@ +# this custom script runs desync to DHT packets with udp payload length 101..399 , without ipset/hostlist filtering +# NOTE: @ih requires nft 1.0.1+ and updated kernel version. it's confirmed to work on 5.15 (openwrt 23) and not work on 5.10 (openwrt 22) + +# can override in config : +NFQWS_OPT_DESYNC_DHT="${NFQWS_OPT_DESYNC_DHT:---payload dht --lua-desync=dht_dn}" + +alloc_dnum DNUM_DHT4ALL +alloc_qnum QNUM_DHT4ALL + +zapret_custom_daemons() +{ + # $1 - 1 - add, 0 - stop + + local opt="--qnum=$QNUM_DHT4ALL $NFQWS_OPT_DESYNC_DHT" + do_nfqws $1 $DNUM_DHT4ALL "$opt" +} +zapret_custom_firewall() +{ + # $1 - 1 - run, 0 - stop + + local f uf4 uf6 + local first_packet_only="$ipt_connbytes 1:1" + + f='-p udp -m length --length 109:407 -m u32 --u32' + uf4='0>>22&0x3C@8>>16=0x6431' + uf6='48>>16=0x6431' + fw_nfqws_post $1 "$f $uf4 $first_packet_only" "$f $uf6 $first_packet_only" $QNUM_DHT4ALL +} +zapret_custom_firewall_nft() +{ + # stop logic is not required + + local f + local first_packet_only="$nft_connbytes 1" + + f="meta length 109-407 meta l4proto udp @ih,0,16 0x6431" + nft_fw_nfqws_post "$f $first_packet_only" "$f $first_packet_only" $QNUM_DHT4ALL +} diff --git a/init.d/custom.d.examples.linux/50-discord-media b/init.d/custom.d.examples.linux/50-discord-media new file mode 100644 index 0000000..1dc24c1 --- /dev/null +++ b/init.d/custom.d.examples.linux/50-discord-media @@ -0,0 +1,35 @@ +# this custom script runs desync to all discord media packets +# NOTE: @ih requires nft 1.0.1+ and updated kernel version. it's confirmed to work on 5.15 (openwrt 23) and not work on 5.10 (openwrt 22) + +# can override in config : +NFQWS_OPT_DESYNC_DISCORD_MEDIA="${NFQWS_OPT_DESYNC_DISCORD_MEDIA:---payload discord_ip_discovery --lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2}" +DISCORD_MEDIA_PORT_RANGE="${DISCORD_MEDIA_PORT_RANGE:-50000-50099}" + +alloc_dnum DNUM_DISCORD_MEDIA +alloc_qnum QNUM_DISCORD_MEDIA + +zapret_custom_daemons() +{ + # $1 - 1 - add, 0 - stop + + local opt="--qnum=$QNUM_DISCORD_MEDIA $NFQWS_OPT_DESYNC_DISCORD_MEDIA" + do_nfqws $1 $DNUM_DISCORD_MEDIA "$opt" +} +zapret_custom_firewall() +{ + # $1 - 1 - run, 0 - stop + + local DISABLE_IPV6=1 + local port_range=$(replace_char - : $DISCORD_MEDIA_PORT_RANGE) + local f="-p udp --dport $port_range -m u32 --u32" + # this is simplified test to skip writing monstrous rule. instead of checking 64 bytes for zeroes only check 2 dwords for zero + fw_nfqws_post $1 "$f 0>>22&0x3C@4>>16=0x52&&0>>22&0x3C@8=0x00010046&&0>>22&0x3C@16=0&&0>>22&0x3C@76=0" '' $QNUM_DISCORD_MEDIA +} +zapret_custom_firewall_nft() +{ + # stop logic is not required + + local DISABLE_IPV6=1 + local f="udp dport $DISCORD_MEDIA_PORT_RANGE udp length == 82 @ih,0,32 0x00010046 @ih,64,128 0x00000000000000000000000000000000 @ih,192,128 0x00000000000000000000000000000000 @ih,320,128 0x00000000000000000000000000000000 @ih,448,128 0x00000000000000000000000000000000" + nft_fw_nfqws_post "$f" '' $QNUM_DISCORD_MEDIA +} diff --git a/init.d/custom.d.examples.linux/50-nfqws-ipset b/init.d/custom.d.examples.linux/50-nfqws-ipset new file mode 100644 index 0000000..aae6e2d --- /dev/null +++ b/init.d/custom.d.examples.linux/50-nfqws-ipset @@ -0,0 +1,144 @@ +# this custom script demonstrates how to launch extra nfqws instance limited by ipset + +# can override in config : +NFQWS_MY1_OPT="${NFQWS_MY1_OPT:---filter-udp=* --payload known,unknown --lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2:payload=all --new --filter-tcp=* --payload=known,unknown --lua-desync=multisplit}" +NFQWS_MY1_SUBNETS4="${NFQWS_MY1_SUBNETS4:-173.194.0.0/16 108.177.0.0/17 74.125.0.0/16 64.233.160.0/19 172.217.0.0/16}" +NFQWS_MY1_SUBNETS6="${NFQWS_MY1_SUBNETS6:-2a00:1450::/29}" +NFQWS_MY1_PORTS_TCP=${NFQWS_MY1_PORTS_TCP:-$NFQWS_PORTS_TCP} +NFQWS_MY1_PORTS_UDP=${NFQWS_MY1_PORTS_UDP:-$NFQWS_PORTS_UDP} +NFQWS_MY1_TCP_PKT_OUT=${NFQWS_MY1_TCP_PKT_OUT:-$NFQWS_TCP_PKT_OUT} +NFQWS_MY1_UDP_PKT_OUT=${NFQWS_MY1_UDP_PKT_OUT:-$NFQWS_UDP_PKT_OUT} +NFQWS_MY1_TCP_PKT_IN=${NFQWS_MY1_TCP_PKT_IN:-$NFQWS_TCP_PKT_IN} +NFQWS_MY1_UDP_PKT_IN=${NFQWS_MY1_UDP_PKT_IN:-$NFQWS_UDP_PKT_IN} + +NFQWS_MY1_IPSET_SIZE=${NFQWS_MY1_IPSET_SIZE:-4096} +NFQWS_MY1_IPSET_OPT="${NFQWS_MY1_IPSET_OPT:-hash:net hashsize 8192 maxelem $NFQWS_MY1_IPSET_SIZE}" + +alloc_dnum DNUM_NFQWS_MY1 +alloc_qnum QNUM_NFQWS_MY1 +NFQWS_MY1_NAME4=my1nfqws4 +NFQWS_MY1_NAME6=my1nfqws6 + +zapret_custom_daemons() +{ + # $1 - 1 - run, 0 - stop + + local opt="--qnum=$QNUM_NFQWS_MY1 $NFQWS_MY1_OPT" + do_nfqws $1 $DNUM_NFQWS_MY1 "$opt" +} + +zapret_custom_firewall() +{ + # $1 - 1 - run, 0 - stop + + local f4 f6 subnet + local NFQWS_MY1_PORTS_TCP=$(replace_char - : $NFQWS_MY1_PORTS_TCP) + local NFQWS_MY1_PORTS_UDP=$(replace_char - : $NFQWS_MY1_PORTS_UDP) + + [ "$1" = 1 -a "$DISABLE_IPV4" != 1 ] && { + ipset create $NFQWS_MY1_NAME4 $NFQWS_MY1_IPSET_OPT family inet 2>/dev/null + ipset flush $NFQWS_MY1_NAME4 + for subnet in $NFQWS_MY1_SUBNETS4; do + echo add $NFQWS_MY1_NAME4 $subnet + done | ipset -! restore + } + [ "$1" = 1 -a "$DISABLE_IPV6" != 1 ] && { + ipset create $NFQWS_MY1_NAME6 $NFQWS_MY1_IPSET_OPT family inet6 2>/dev/null + ipset flush $NFQWS_MY1_NAME6 + for subnet in $NFQWS_MY1_SUBNETS6; do + echo add $NFQWS_MY1_NAME6 $subnet + done | ipset -! restore + } + + [ -n "$NFQWS_MY1_PORTS_TCP" ] && { + [ -n "$NFQWS_MY1_TCP_PKT_OUT" -a "$NFQWS_MY1_TCP_PKT_OUT" != 0 ] && { + f4="-p tcp -m multiport --dports $NFQWS_MY1_PORTS_TCP $ipt_connbytes 1:$NFQWS_MY1_TCP_PKT_OUT -m set --match-set" + f6="$f4 $NFQWS_MY1_NAME6 dst" + f4="$f4 $NFQWS_MY1_NAME4 dst" + fw_nfqws_post $1 "$f4" "$f6" $QNUM_NFQWS_MY1 + } + [ -n "$NFQWS_MY1_TCP_PKT_IN" -a "$NFQWS_MY1_TCP_PKT_IN" != 0 ] && { + f4="-p tcp -m multiport --sports $NFQWS_MY1_PORTS_TCP $ipt_connbytes 1:$NFQWS_MY1_TCP_PKT_IN -m set --match-set" + f6="$f4 $NFQWS_MY1_NAME6 src" + f4="$f4 $NFQWS_MY1_NAME4 src" + fw_nfqws_pre $1 "$f4" "$f6" $QNUM_NFQWS_MY1 + } + } + [ -n "$NFQWS_MY1_PORTS_UDP" ] && { + [ -n "$NFQWS_MY1_UDP_PKT_OUT" -a "$NFQWS_MY1_UDP_PKT_OUT" != 0 ] && { + f4="-p udp -m multiport --dports $NFQWS_MY1_PORTS_UDP $ipt_connbytes 1:$NFQWS_MY1_UDP_PKT_OUT -m set --match-set" + f6="$f4 $NFQWS_MY1_NAME6 dst" + f4="$f4 $NFQWS_MY1_NAME4 dst" + fw_nfqws_post $1 "$f4" "$f6" $QNUM_NFQWS_MY1 + } + [ -n "$NFQWS_MY1_UDP_PKT_IN" -a "$NFQWS_MY1_UDP_PKT_IN" != 0 ] && { + f4="-p udp -m multiport --sports $NFQWS_MY1_PORTS_UDP $ipt_connbytes 1:$NFQWS_MY1_UDP_PKT_IN -m set --match-set" + f6="$f4 $NFQWS_MY1_NAME6 src" + f4="$f4 $NFQWS_MY1_NAME4 src" + fw_nfqws_pre $1 "$f4" "$f6" $QNUM_NFQWS_MY1 + } + } + + [ "$1" = 1 ] || { + ipset destroy $NFQWS_MY1_NAME4 2>/dev/null + ipset destroy $NFQWS_MY1_NAME6 2>/dev/null + } +} + +zapret_custom_firewall_nft() +{ + local f4 f6 subnets + local first_packets_only="$nft_connbytes 1-$NFQWS_MY1_PKT_OUT" + + [ "$DISABLE_IPV4" != 1 ] && { + make_comma_list subnets $NFQWS_MY1_SUBNETS4 + nft_create_set $NFQWS_MY1_NAME4 "type ipv4_addr; size $NFQWS_MY1_IPSET_SIZE; auto-merge; flags interval;" + nft_flush_set $NFQWS_MY1_NAME4 + nft_add_set_element $NFQWS_MY1_NAME4 "$subnets" + } + [ "$DISABLE_IPV6" != 1 ] && { + make_comma_list subnets $NFQWS_MY1_SUBNETS6 + nft_create_set $NFQWS_MY1_NAME6 "type ipv6_addr; size $NFQWS_MY1_IPSET_SIZE; auto-merge; flags interval;" + nft_flush_set $NFQWS_MY1_NAME6 + nft_add_set_element $NFQWS_MY1_NAME6 "$subnets" + } + + [ -n "$NFQWS_MY1_PORTS_TCP" ] && { + [ -n "$NFQWS_MY1_TCP_PKT_OUT" -a "$NFQWS_MY1_TCP_PKT_OUT" != 0 ] && { + f4="tcp dport {$NFQWS_MY1_PORTS_TCP} $(nft_first_packets $NFQWS_MY1_TCP_PKT_OUT)" + f6="$f4 ip6 daddr @$NFQWS_MY1_NAME6" + f4="$f4 ip daddr @$NFQWS_MY1_NAME4" + nft_fw_nfqws_post $1 "$f4" "$f6" $QNUM_NFQWS_MY1 + } + [ -n "$NFQWS_MY1_TCP_PKT_IN" -a "$NFQWS_MY1_TCP_PKT_IN" != 0 ] && { + f4="tcp sport {$NFQWS_MY1_PORTS_TCP} $(nft_first_packets $NFQWS_MY1_TCP_PKT_IN)" + f6="$f4 ip6 saddr @$NFQWS_MY1_NAME6" + f4="$f4 ip saddr @$NFQWS_MY1_NAME4" + nft_fw_nfqws_pre $1 "$f4" "$f6" $QNUM_NFQWS_MY1 + } + } + [ -n "$NFQWS_MY1_PORTS_UDP" ] && { + [ -n "$NFQWS_MY1_UDP_PKT_OUT" -a "$NFQWS_MY1_UDP_PKT_OUT" != 0 ] && { + f4="udp dport {$NFQWS_MY1_PORTS_UDP} $(nft_first_packets $NFQWS_MY1_UDP_PKT_OUT)" + f6="$f4 ip6 daddr @$NFQWS_MY1_NAME6" + f4="$f4 ip daddr @$NFQWS_MY1_NAME4" + nft_fw_nfqws_post $1 "$f4" "$f6" $QNUM_NFQWS_MY1 + } + [ -n "$NFQWS_MY1_UDP_PKT_IN" -a "$NFQWS_MY1_UDP_PKT_IN" != 0 ] && { + f4="udp sport {$NFQWS_MY1_PORTS_UDP} $(nft_first_packets $NFQWS_MY1_UDP_PKT_IN)" + f6="$f4 ip6 saddr @$NFQWS_MY1_NAME6" + f4="$f4 ip saddr @$NFQWS_MY1_NAME4" + nft_fw_nfqws_pre $1 "$f4" "$f6" $QNUM_NFQWS_MY1 + } + } +} + + +zapret_custom_firewall_nft_flush() +{ + # this function is called after all nft fw rules are deleted + # however sets are not deleted. it's desired to clear sets here. + + nft_del_set $NFQWS_MY1_NAME4 2>/dev/null + nft_del_set $NFQWS_MY1_NAME6 2>/dev/null +} diff --git a/init.d/custom.d.examples.linux/50-quic4all b/init.d/custom.d.examples.linux/50-quic4all new file mode 100644 index 0000000..34971b2 --- /dev/null +++ b/init.d/custom.d.examples.linux/50-quic4all @@ -0,0 +1,30 @@ +# this custom script runs desync to all IETF QUIC initials +# NOTE: @ih requires nft 1.0.1+ and updated kernel version. it's confirmed to work on 5.15 (openwrt 23) and not work on 5.10 (openwrt 22) + +# can override in config : +NFQWS_OPT_DESYNC_QUIC="${NFQWS_OPT_DESYNC_QUIC:---payload quic_initial --lua-desync=fake:blob=fake_default_quic:repeats=2}" + +alloc_dnum DNUM_QUIC4ALL +alloc_qnum QNUM_QUIC4ALL + +zapret_custom_daemons() +{ + # $1 - 1 - add, 0 - stop + + local opt="--qnum=$QNUM_QUIC4ALL $NFQWS_OPT_DESYNC_QUIC" + do_nfqws $1 $DNUM_QUIC4ALL "$opt" +} +zapret_custom_firewall() +{ + # $1 - 1 - run, 0 - stop + + local f='-p udp -m u32 --u32' + fw_nfqws_post $1 "$f 0>>22&0x3C@4>>16=264:65535&&0>>22&0x3C@8>>28=0xC&&0>>22&0x3C@9=0x00000001" "$f 44>>16=264:65535&&48>>28=0xC&&49=0x00000001" $QNUM_QUIC4ALL +} +zapret_custom_firewall_nft() +{ + # stop logic is not required + + local f="udp length >= 264 @ih,0,4 0xC @ih,8,32 0x00000001" + nft_fw_nfqws_post "$f" "$f" $QNUM_QUIC4ALL +} diff --git a/init.d/custom.d.examples.linux/50-stun4all b/init.d/custom.d.examples.linux/50-stun4all new file mode 100644 index 0000000..6ddf5bc --- /dev/null +++ b/init.d/custom.d.examples.linux/50-stun4all @@ -0,0 +1,30 @@ +# this custom script runs desync to all stun packets +# NOTE: @ih requires nft 1.0.1+ and updated kernel version. it's confirmed to work on 5.15 (openwrt 23) and not work on 5.10 (openwrt 22) + +# can override in config : +NFQWS_OPT_DESYNC_STUN="${NFQWS_OPT_DESYNC_STUN:---payload stun_binding_req --lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2}" + +alloc_dnum DNUM_STUN4ALL +alloc_qnum QNUM_STUN4ALL + +zapret_custom_daemons() +{ + # $1 - 1 - add, 0 - stop + + local opt="--qnum=$QNUM_STUN4ALL $NFQWS_OPT_DESYNC_STUN" + do_nfqws $1 $DNUM_STUN4ALL "$opt" +} +zapret_custom_firewall() +{ + # $1 - 1 - run, 0 - stop + + local f='-p udp -m u32 --u32' + fw_nfqws_post $1 "$f 0>>22&0x3C@4>>16=28:65535&&0>>22&0x3C@12=0x2112A442&&0>>22&0x3C@8&0xC0000003=0" "$f 44>>16=28:65535&&52=0x2112A442&&48&0xC0000003=0" $QNUM_STUN4ALL +} +zapret_custom_firewall_nft() +{ + # stop logic is not required + + local f="udp length >= 28 @ih,32,32 0x2112A442 @ih,0,2 0 @ih,30,2 0" + nft_fw_nfqws_post "$f" "$f" $QNUM_STUN4ALL +} diff --git a/init.d/custom.d.examples.linux/50-wg4all b/init.d/custom.d.examples.linux/50-wg4all new file mode 100644 index 0000000..cb21e75 --- /dev/null +++ b/init.d/custom.d.examples.linux/50-wg4all @@ -0,0 +1,32 @@ +# this custom script runs desync to all wireguard handshake initiation packets +# NOTE: this works for original wireguard and may not work for 3rd party implementations such as xray +# NOTE: @ih requires nft 1.0.1+ and updated kernel version. it's confirmed to work on 5.15 (openwrt 23) and not work on 5.10 (openwrt 22) + +# can override in config : +NFQWS_OPT_DESYNC_WG="${NFQWS_OPT_DESYNC_WG:---payload wireguard_initiation --lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2}" + +alloc_dnum DNUM_WG4ALL +alloc_qnum QNUM_WG4ALL + +zapret_custom_daemons() +{ + # $1 - 1 - add, 0 - stop + + local opt="--qnum=$QNUM_WG4ALL $NFQWS_OPT_DESYNC_WG" + do_nfqws $1 $DNUM_WG4ALL "$opt" +} +# size = 156 (8 udp header + 148 payload) && payload starts with 0x01000000 +zapret_custom_firewall() +{ + # $1 - 1 - run, 0 - stop + + local f='-p udp -m u32 --u32' + fw_nfqws_post $1 "$f 0>>22&0x3C@4>>16=0x9c&&0>>22&0x3C@8=0x01000000" "$f 44>>16=0x9c&&48=0x01000000" $QNUM_WG4ALL +} +zapret_custom_firewall_nft() +{ + # stop logic is not required + + local f="udp length 156 @ih,0,32 0x01000000" + nft_fw_nfqws_post "$f" "$f" $QNUM_WG4ALL +} 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-zapret2 b/init.d/openwrt/90-zapret2 new file mode 100644 index 0000000..457e2eb --- /dev/null +++ b/init.d/openwrt/90-zapret2 @@ -0,0 +1,46 @@ +#!/bin/sh + +ZAPRET=/etc/init.d/zapret2 + +check_lan() +{ + IS_LAN= + [ -n "$OPENWRT_LAN" ] || OPENWRT_LAN=lan + for lan in $OPENWRT_LAN; do + [ "$INTERFACE" = "$lan" ] && { + IS_LAN=1 + break + } + done +} + + +[ -n "$INTERFACE" ] && [ "$ACTION" = ifup -o "$ACTION" = ifdown ] && [ -x "$ZAPRET" ] && "$ZAPRET" enabled && { + SCRIPT=$(readlink "$ZAPRET") + if [ -n "$SCRIPT" ]; then + EXEDIR=$(dirname "$SCRIPT") + ZAPRET_BASE=$(readlink -f "$EXEDIR/../..") + else + ZAPRET_BASE=/opt/zapret2 + fi + ZAPRET_RW=${ZAPRET_RW:-"$ZAPRET_BASE"} + ZAPRET_CONFIG=${ZAPRET_CONFIG:-"$ZAPRET_RW/config"} + CUSTOM_DIR="$ZAPRET_RW/init.d/openwrt" + . "$ZAPRET_CONFIG" + . "$ZAPRET_BASE/common/base.sh" + . "$ZAPRET_BASE/common/fwtype.sh" + + linux_fwtype + case "$FWTYPE" in + nftables) + logger -t zapret reloading nftables ifsets due to $ACTION of $INTERFACE + "$ZAPRET" reload_ifsets + ;; + iptables) + openwrt_fw3 || { + logger -t zapret reloading iptables due to $ACTION of $INTERFACE + "$ZAPRET" restart_fw + } + ;; + esac +} diff --git a/init.d/openwrt/custom.d/.keep b/init.d/openwrt/custom.d/.keep new file mode 100644 index 0000000..e69de29 diff --git a/init.d/openwrt/firewall.zapret2 b/init.d/openwrt/firewall.zapret2 new file mode 100644 index 0000000..37ad0c4 --- /dev/null +++ b/init.d/openwrt/firewall.zapret2 @@ -0,0 +1,11 @@ +SCRIPT=$(readlink /etc/init.d/zapret2) +if [ -n "$SCRIPT" ]; then + EXEDIR=$(dirname "$SCRIPT") + ZAPRET_BASE=$(readlink -f "$EXEDIR/../..") +else + ZAPRET_BASE=/opt/zapret2 +fi + +. "$ZAPRET_BASE/init.d/openwrt/functions" + +zapret_apply_firewall diff --git a/init.d/openwrt/functions b/init.d/openwrt/functions new file mode 100644 index 0000000..f3bc19c --- /dev/null +++ b/init.d/openwrt/functions @@ -0,0 +1,218 @@ +. /lib/functions/network.sh + +ZAPRET_BASE=${ZAPRET_BASE:-/opt/zapret2} +ZAPRET_RW=${ZAPRET_RW:-"$ZAPRET_BASE"} +ZAPRET_CONFIG=${ZAPRET_CONFIG:-"$ZAPRET_RW/config"} +. "$ZAPRET_CONFIG" +. "$ZAPRET_BASE/common/base.sh" +. "$ZAPRET_BASE/common/fwtype.sh" +. "$ZAPRET_BASE/common/linux_iphelper.sh" +. "$ZAPRET_BASE/common/ipt.sh" +. "$ZAPRET_BASE/common/nft.sh" +. "$ZAPRET_BASE/common/linux_fw.sh" +. "$ZAPRET_BASE/common/linux_daemons.sh" +. "$ZAPRET_BASE/common/list.sh" +. "$ZAPRET_BASE/common/custom.sh" +CUSTOM_DIR="$ZAPRET_RW/init.d/openwrt" + +QNUM=${QNUM:-300} +WS_USER=${WS_USER:-daemon} +DESYNC_MARK=${DESYNC_MARK:-0x40000000} +DESYNC_MARK_POSTNAT=${DESYNC_MARK_POSTNAT:-0x20000000} +OPENWRT_LAN=${OPENWRT_LAN:-lan} + +IPSET_CR="$ZAPRET_BASE/ipset/create_ipset.sh" + +# can be multiple ipv6 outgoing interfaces +# uplink from isp, tunnelbroker, vpn, ... +# want them all. who knows what's the real one that blocks sites +# dont want any manual configuration - want to do it automatically +# standard network_find_wan[6] return only the first +# we use low level function from network.sh to avoid this limitation +# it can change theoretically and stop working + +network_find_wan4_all() +{ + if [ -n "$OPENWRT_WAN4" ]; then + eval $1="\$OPENWRT_WAN4" + else + __network_ifstatus "$1" "" "[@.route[@.target='0.0.0.0' && !@.table]].interface" "" 10 2>/dev/null && return + network_find_wan $1 + fi +} +network_find_wan_all() +{ + network_find_wan4_all "$@" +} +network_find_wan6_all() +{ + if [ -n "$OPENWRT_WAN6" ]; then + eval $1="\$OPENWRT_WAN6" + else + __network_ifstatus "$1" "" "[@.route[@.target='::' && !@.table]].interface" "" 10 2>/dev/null && return + network_find_wan6 $1 + fi +} +network_find_wanX_devices() +{ + # $1 - ip version: 4 or 6 + # $2 - variable to put result to + local ifaces + network_find_wan${1}_all ifaces + call_for_multiple_items network_get_device $2 "$ifaces" +} + + +fw_nfqws_prepost_x() +{ + # $1 - 1 - add, 0 - del + # $2 - filter + # $3 - queue number + # $4 - 4/6 + # $5 - post/pre + + local ifaces DWAN + network_find_wan${4}_all ifaces + call_for_multiple_items network_get_device DWAN "$ifaces" + + [ -n "$DWAN" ] && _fw_nfqws_${5}${4} $1 "$2" $3 "$(unique $DWAN)" +} +fw_nfqws_post4() +{ + fw_nfqws_prepost_x $1 "$2" $3 4 post +} +fw_nfqws_post6() +{ + fw_nfqws_prepost_x $1 "$2" $3 6 post +} +fw_nfqws_pre4() +{ + fw_nfqws_prepost_x $1 "$2" $3 4 pre +} +fw_nfqws_pre6() +{ + fw_nfqws_prepost_x $1 "$2" $3 6 pre +} + +create_ipset() +{ + echo "Creating ip list table (firewall type $FWTYPE)" + "$IPSET_CR" "$@" +} + +list_nfqws_rules() +{ + # $1 = '' for ipv4, '6' for ipv6 + ip$1tables -S POSTROUTING -t mangle | \ + grep -E "NFQUEUE --queue-num $QNUM --queue-bypass|NFQUEUE --queue-num $(($QNUM+1)) --queue-bypass|NFQUEUE --queue-num $(($QNUM+2)) --queue-bypass|NFQUEUE --queue-num $(($QNUM+3)) --queue-bypass|NFQUEUE --queue-num $(($QNUM+10)) --queue-bypass|NFQUEUE --queue-num $(($QNUM+11)) --queue-bypass" | \ + sed -re 's/^-A POSTROUTING (.*) -j NFQUEUE.*$/\1/' -e "s/-m mark ! --mark $DESYNC_MARK\/$DESYNC_MARK//" +} +apply_flow_offloading_enable_rule() +{ + # $1 = '' for ipv4, '6' for ipv6 + local i off='-j FLOWOFFLOAD' + [ "$FLOWOFFLOAD" = "hardware" ] && off="$off --hw" + i="forwarding_rule_zapret -m comment --comment zapret_traffic_offloading_enable -m conntrack --ctstate RELATED,ESTABLISHED $off" + echo enabling ipv${1:-4} flow offloading : $i + ip$1tables -A $i +} +apply_flow_offloading_exempt_rule() +{ + # $1 = '' for ipv4, '6' for ipv6 + local i v + v=$1 + shift + i="forwarding_rule_zapret $@ -m comment --comment zapret_traffic_offloading_exemption -j RETURN" + echo applying ipv${v:-4} flow offloading exemption : $i + ip${v}tables -A $i +} +flow_offloading_unexempt_v() +{ + # $1 = '' for ipv4, '6' for ipv6 + local DWAN + network_find_wanX_devices ${1:-4} DWAN + for i in $DWAN; do ipt$1_del FORWARD -o $i -j forwarding_rule_zapret ; done + ip$1tables -F forwarding_rule_zapret 2>/dev/null + ip$1tables -X forwarding_rule_zapret 2>/dev/null +} +flow_offloading_exempt_v() +{ + # $1 = '' for ipv4, '6' for ipv6 + is_ipt_flow_offload_avail $1 || return 0 + + flow_offloading_unexempt_v $1 + + [ "$FLOWOFFLOAD" = 'software' -o "$FLOWOFFLOAD" = 'hardware' ] && { + ip$1tables -N forwarding_rule_zapret + + # remove outgoing interface + list_nfqws_rules $1 | sed -re 's/-o +[^ ]+//g' | + while read rule; do + apply_flow_offloading_exempt_rule "$1" $rule + done + + apply_flow_offloading_enable_rule $1 + + # only outgoing to WAN packets trigger flow offloading + local DWAN + network_find_wanX_devices ${1:-4} DWAN + for i in $DWAN; do ipt$1 FORWARD -o $i -j forwarding_rule_zapret; done + } + return 0 +} +flow_offloading_exempt() +{ + [ "$DISABLE_IPV4" = "1" ] || flow_offloading_exempt_v + [ "$DISABLE_IPV6" = "1" ] || flow_offloading_exempt_v 6 +} +flow_offloading_unexempt() +{ + [ "$DISABLE_IPV4" = "1" ] || flow_offloading_unexempt_v + [ "$DISABLE_IPV6" = "1" ] || flow_offloading_unexempt_v 6 +} + +nft_fill_ifsets_overload() +{ + local ifaces DLAN DWAN DWAN6 PDLAN PDWAN PDWAN6 + + call_for_multiple_items network_get_device DLAN "$OPENWRT_LAN" + call_for_multiple_items network_get_physdev PDLAN "$OPENWRT_LAN" + + network_find_wan4_all ifaces + call_for_multiple_items network_get_device DWAN "$ifaces" + call_for_multiple_items network_get_physdev PDWAN "$ifaces" + + network_find_wan6_all ifaces + call_for_multiple_items network_get_device DWAN6 "$ifaces" + call_for_multiple_items network_get_physdev PDWAN6 "$ifaces" + + nft_fill_ifsets "$DLAN" "$DWAN" "$DWAN6" "$PDLAN" "$PDWAN" "$PDWAN6" +} +nft_wanif_filter_present() +{ + # in openwrt we always use wanif filter + return 0 +} +nft_wanif6_filter_present() +{ + # in openwrt we always use wanif6 filter + return 0 +} + + +nft_fw_nfqws_post4() +{ + _nft_fw_nfqws_post4 "$1" $2 always_apply_wan_filter +} +nft_fw_nfqws_post6() +{ + _nft_fw_nfqws_post6 "$1" $2 always_apply_wan_filter +} +nft_fw_nfqws_pre4() +{ + _nft_fw_nfqws_pre4 "$1" $2 always_apply_wan_filter +} +nft_fw_nfqws_pre6() +{ + _nft_fw_nfqws_pre6 "$1" $2 always_apply_wan_filter +} diff --git a/init.d/openwrt/zapret2 b/init.d/openwrt/zapret2 new file mode 100755 index 0000000..43342db --- /dev/null +++ b/init.d/openwrt/zapret2 @@ -0,0 +1,135 @@ +#!/bin/sh /etc/rc.common + +USE_PROCD=1 +# after network +START=21 + +my_extra_command() { + local cmd="$1" + local help="$2" + + local extra="$(printf "%-16s%s" "${cmd}" "${help}")" + EXTRA_HELP="${EXTRA_HELP} ${extra} +" + EXTRA_COMMANDS="${EXTRA_COMMANDS} ${cmd}" +} +my_extra_command stop_fw "Stop zapret firewall (noop in iptables+fw3 case)" +my_extra_command start_fw "Start zapret firewall (noop in iptables+fw3 case)" +my_extra_command restart_fw "Restart zapret firewall (noop in iptables+fw3 case)" +my_extra_command reload_ifsets "Reload interface lists (nftables only)" +my_extra_command list_ifsets "Display interface lists (nftables only)" +my_extra_command list_table "Display zapret nftable (nftables only)" +my_extra_command stop_daemons "Stop zapret daemons only (=stop in iptables+fw3 case)" +my_extra_command start_daemons "Start zapret daemons only (=start in iptables+fw3 case)" +my_extra_command restart_daemons "Restart zapret firewall only (=restart in iptables+fw3 case)" + +SCRIPT=$(readlink /etc/init.d/zapret2) +if [ -n "$SCRIPT" ]; then + EXEDIR=$(dirname "$SCRIPT") + ZAPRET_BASE=$(readlink -f "$EXEDIR/../..") +else + ZAPRET_BASE=/opt/zapret2 +fi + +. "$ZAPRET_BASE/init.d/openwrt/functions" + + +# !!!!! in old openwrt 21.x- with iptables firewall rules are configured separately +# !!!!! in new openwrt >21.x with nftables firewall is configured here + +PIDDIR=/var/run + +USEROPT="--user=$WS_USER" +NFQWS2="${NFQWS2:-$ZAPRET_BASE/nfq2/nfqws2}" +LUAOPT="--lua-init=@$ZAPRET_BASE/lua/zapret-lib.lua --lua-init=@$ZAPRET_BASE/lua/zapret-antidpi.lua" +NFQWS2_OPT_BASE="$USEROPT --fwmark=$DESYNC_MARK $LUAOPT" + +run_daemon() +{ + # $1 - daemon string id or number. can use 1,2,3,... + # $2 - daemon + # $3 - daemon args + # use $PIDDIR/$DAEMONBASE$1.pid as pidfile + local DAEMONBASE="$(basename "$2")" + echo "Starting daemon $1: $2 $3" + procd_open_instance + procd_set_param command $2 $3 + procd_set_param pidfile $PIDDIR/$DAEMONBASE$1.pid + procd_close_instance +} + +run_nfqws() +{ + run_daemon $1 "$NFQWS2" "$NFQWS2_OPT_BASE $2" +} +do_nfqws() +{ + [ "$1" = 0 ] || { shift; run_nfqws "$@"; } +} + +start_daemons_procd() +{ + standard_mode_daemons 1 + custom_runner zapret_custom_daemons 1 + + return 0 +} +start_daemons() +{ + rc_procd start_daemons_procd "$@" +} +stop_daemons() +{ + local svc="$(basename ${basescript:-$initscript})" + procd_running "$svc" "$1" && procd_kill "$svc" "$1" +} +restart_daemons() +{ + stop_daemons + start_daemons +} + +start_fw() +{ + zapret_apply_firewall +} +stop_fw() +{ + zapret_unapply_firewall +} +restart_fw() +{ + stop_fw + start_fw +} +reload_ifsets() +{ + zapret_reload_ifsets +} +list_ifsets() +{ + zapret_list_ifsets +} +list_table() +{ + zapret_list_table +} + +start_service() +{ + start_daemons_procd + [ "$INIT_APPLY_FW" != "1" ] || { + linux_fwtype + openwrt_fw3_integration || start_fw + } +} + +stop_service() +{ + # this procedure is called from stop() + # stop() already stop daemons + [ "$INIT_APPLY_FW" != "1" ] || { + linux_fwtype + openwrt_fw3_integration || stop_fw + } +} diff --git a/init.d/systemd/nfqws2@.service b/init.d/systemd/nfqws2@.service new file mode 100644 index 0000000..8dcb42b --- /dev/null +++ b/init.d/systemd/nfqws2@.service @@ -0,0 +1,62 @@ +# Example systemd service unit for nfqws. Adjust for your installation. + +# WARNING ! This unit requires to compile nfqws using `make systemd` +# WARNING ! This makefile target enables special systemd notify support. + +# PREPARE +# install build depends +# make -C /opt/zapret2 systemd +# cp nfqws2\@.service /lib/systemd/system +# systemctl daemon-reload + +# MANAGE INSTANCE +# prepare /etc/zapret2/nfqws1.conf with nfqws parameters +# systemctl start nfqws2@nfqws1 +# systemctl status nfqws2@nfqws1 +# systemctl restart nfqws2@nfqws1 +# systemctl enable nfqws2@nfqws1 +# systemctl disable nfqws2@nfqws1 +# systemctl stop nfqws2@nfqws1 + +# DELETE +# rm /lib/systemd/system/nfqws@.service +# systemctl daemon-reload + + +[Unit] +After=network.target + +[Service] +Type=notify +Restart=on-failure + +ExecSearchPath=/opt/zapret2/nfq2 +ExecStart=nfqws2 @${CONFIG_DIR}/${INSTANCE}.conf +Environment=CONFIG_DIR=/etc/zapret2 +Environment=INSTANCE=%i + +RestrictAddressFamilies=AF_NETLINK AF_UNIX AF_INET6 AF_INET + +LockPersonality=true +MemoryDenyWriteExecute=true +PrivateDevices=true +PrivateMounts=true +PrivateTmp=true +ProcSubset=pid +ProtectClock=true +ProtectControlGroups=true +ProtectHome=true +ProtectHostname=true +ProtectKernelLogs=true +ProtectKernelModules=true +ProtectKernelTunables=true +ProtectProc=invisible +ProtectSystem=full +RemoveIPC=true +RestrictNamespaces=true +RestrictRealtime=true +RestrictSUIDSGID=true +UMask=0077 + +[Install] +WantedBy=multi-user.target diff --git a/init.d/systemd/zapret2-list-update.service b/init.d/systemd/zapret2-list-update.service new file mode 100644 index 0000000..a4cc4e6 --- /dev/null +++ b/init.d/systemd/zapret2-list-update.service @@ -0,0 +1,13 @@ +[Unit] +Description=zapret2 ip/host list update + +[Service] +Restart=no +IgnoreSIGPIPE=no +KillMode=control-group +GuessMainPID=no +RemainAfterExit=no +ExecStart=/opt/zapret2/ipset/get_config.sh + +[Install] +WantedBy=multi-user.target diff --git a/init.d/systemd/zapret2-list-update.timer b/init.d/systemd/zapret2-list-update.timer new file mode 100644 index 0000000..f0c550c --- /dev/null +++ b/init.d/systemd/zapret2-list-update.timer @@ -0,0 +1,11 @@ +[Unit] +Description=zapret2 ip/host list update timer + +[Timer] +OnCalendar=*-*-2,4,6,8,10,12,14,16,18,20,22,24,26,28,30 00:00:00 +RandomizedDelaySec=86400 +Persistent=true +Unit=zapret2-list-update.service + +[Install] +WantedBy=timers.target diff --git a/init.d/systemd/zapret2.service b/init.d/systemd/zapret2.service new file mode 100644 index 0000000..ecb2811 --- /dev/null +++ b/init.d/systemd/zapret2.service @@ -0,0 +1,17 @@ +[Unit] +After=network-online.target +Wants=network-online.target + +[Service] +Type=forking +Restart=no +TimeoutSec=30sec +IgnoreSIGPIPE=no +KillMode=none +GuessMainPID=no +RemainAfterExit=no +ExecStart=/opt/zapret2/init.d/sysv/zapret2 start +ExecStop=/opt/zapret2/init.d/sysv/zapret2 stop + +[Install] +WantedBy=multi-user.target diff --git a/init.d/sysv/custom.d/.keep b/init.d/sysv/custom.d/.keep new file mode 100644 index 0000000..e69de29 diff --git a/init.d/sysv/functions b/init.d/sysv/functions new file mode 100644 index 0000000..a748974 --- /dev/null +++ b/init.d/sysv/functions @@ -0,0 +1,191 @@ +# init script functions library for desktop linux systems + +ZAPRET_BASE=${ZAPRET_BASE:-/opt/zapret2} +ZAPRET_RW=${ZAPRET_RW:-"$ZAPRET_BASE"} +ZAPRET_CONFIG=${ZAPRET_CONFIG:-"$ZAPRET_RW/config"} +. "$ZAPRET_CONFIG" +. "$ZAPRET_BASE/common/base.sh" +. "$ZAPRET_BASE/common/fwtype.sh" +. "$ZAPRET_BASE/common/linux_iphelper.sh" +. "$ZAPRET_BASE/common/ipt.sh" +. "$ZAPRET_BASE/common/nft.sh" +. "$ZAPRET_BASE/common/linux_fw.sh" +. "$ZAPRET_BASE/common/linux_daemons.sh" +. "$ZAPRET_BASE/common/list.sh" +. "$ZAPRET_BASE/common/custom.sh" +CUSTOM_DIR="$ZAPRET_RW/init.d/sysv" + + +user_exists() +{ + id -u $1 >/dev/null 2>/dev/null +} +useradd_compat() +{ + # $1 - username + # skip for readonly systems + [ -w "/etc" ] && { + if exists useradd ; then + useradd --no-create-home --system --shell /bin/false $1 + elif is_linked_to_busybox adduser ; then + # some systems may miss nogroup group in /etc/group + # adduser fails if it's absent and no group is specified + addgroup nogroup 2>/dev/null + # busybox has special adduser syntax + adduser -S -H -D $1 + elif exists adduser; then + adduser --no-create-home --system --disabled-login $1 + fi + } + user_exists $1 +} +prepare_user() +{ + user_exists $WS_USER || { + # fallback to daemon if we cant add WS_USER + useradd_compat $WS_USER || { + for user in daemon nobody; do + user_exists $user && { + WS_USER=$user + return 0 + } + done + return 1 + } + } +} + +# this complex user selection allows to survive in any locked/readonly/minimalistic environment +[ -n "$WS_USER" ] || WS_USER=tpws +if prepare_user; then + USEROPT="--user=$WS_USER" +else + WS_USER=1 + USEROPT="--uid $WS_USER:$WS_USER" +fi + +PIDDIR=/var/run +IPSET_CR="$ZAPRET_BASE/ipset/create_ipset.sh" + +DESYNC_MARK=${DESYNC_MARK:-0x40000000} +DESYNC_MARK_POSTNAT=${DESYNC_MARK_POSTNAT:-0x20000000} + +QNUM=${QNUM:-300} +NFQWS2="${NFQWS2:-$ZAPRET_BASE/nfq2/nfqws2}" +LUAOPT="--lua-init=@$ZAPRET_BASE/lua/zapret-lib.lua --lua-init=@$ZAPRET_BASE/lua/zapret-antidpi.lua" +NFQWS2_OPT_BASE="$USEROPT --fwmark=$DESYNC_MARK $LUAOPT" + + +fw_nfqws_post4() +{ + _fw_nfqws_post4 $1 "$2" $3 "$IFACE_WAN" +} +fw_nfqws_post6() +{ + _fw_nfqws_post6 $1 "$2" $3 "${IFACE_WAN6:-$IFACE_WAN}" +} +fw_nfqws_pre4() +{ + _fw_nfqws_pre4 $1 "$2" $3 "$IFACE_WAN" +} +fw_nfqws_pre6() +{ + _fw_nfqws_pre6 $1 "$2" $3 "${IFACE_WAN6:-$IFACE_WAN}" +} +nft_fw_nfqws_post4() +{ + _nft_fw_nfqws_post4 "$1" $2 "$IFACE_WAN" +} +nft_fw_nfqws_post6() +{ + _nft_fw_nfqws_post6 "$1" $2 "${IFACE_WAN6:-$IFACE_WAN}" +} +nft_fw_nfqws_pre4() +{ + _nft_fw_nfqws_pre4 "$1" $2 "$IFACE_WAN" +} +nft_fw_nfqws_pre6() +{ + _nft_fw_nfqws_pre6 "$1" $2 "${IFACE_WAN6:-$IFACE_WAN}" +} + +nft_wanif_filter_present() +{ + [ -n "$IFACE_WAN" ] +} +nft_wanif6_filter_present() +{ + [ -n "${IFACE_WAN6:-$IFACE_WAN}" ] +} +nft_fill_ifsets_overload() +{ + nft_fill_ifsets "$IFACE_WAN" "${IFACE_WAN6:-$IFACE_WAN}" "$IFACE_LAN" +} + + +run_daemon() +{ + # $1 - daemon number : 1,2,3,... + # $2 - daemon + # $3 - daemon args + # use $PIDDIR/$DAEMONBASE$1.pid as pidfile + + local DAEMONBASE="$(basename "$2")" + local PID= PIDFILE=$PIDDIR/${DAEMONBASE}_$1.pid + echo "Starting daemon $1: $2 $3" + + [ -f "$PIDFILE" ] && { + read PID <"$PIDFILE" + [ -d "/proc/$PID" ] || PID= + } + + if [ -n "$PID" ]; then + echo already running + else + "$2" $3 >/dev/null & + PID=$! + if [ -n "$PID" ]; then + echo $PID >$PIDFILE + else + echo could not start daemon $1 : $2 $3 + false + fi + fi +} +stop_daemon() +{ + # $1 - daemon number : 1,2,3,... + # $2 - daemon + # use $PIDDIR/$DAEMONBASE$1.pid as pidfile + local DAEMONBASE="$(basename "$2")" + local PID PIDFILE=$PIDDIR/${DAEMONBASE}_$1.pid + echo "Stopping daemon $1: $2" + if [ -f "$PIDFILE" ]; then + read PID <"$PIDFILE" + kill $PID + rm -f "$PIDFILE" + else + echo no pidfile : $PIDFILE + fi +} +do_daemon() +{ + # $1 - 1 - run, 0 - stop + on_off_function run_daemon stop_daemon "$@" +} + +do_nfqws() +{ + # $1 : 1 - run, 0 - stop + # $2 : daemon number + # $3 : daemon args + + do_daemon $1 $2 "$NFQWS2" "$NFQWS2_OPT_BASE $3" +} + + +create_ipset() +{ + echo "Creating ip list table (firewall type $FWTYPE)" + "$IPSET_CR" "$@" +} diff --git a/init.d/sysv/zapret2 b/init.d/sysv/zapret2 new file mode 100755 index 0000000..a3d8b73 --- /dev/null +++ b/init.d/sysv/zapret2 @@ -0,0 +1,82 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: zapret +# Required-Start: $local_fs $network +# Required-Stop: $local_fs $network +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +### END INIT INFO + +SCRIPT=$(readlink -f "$0") +EXEDIR=$(dirname "$SCRIPT") +ZAPRET_BASE=$(readlink -f "$EXEDIR/../..") +. "$EXEDIR/functions" + +NAME=zapret +DESC=anti-zapret + +do_start() +{ + zapret_run_daemons + [ "$INIT_APPLY_FW" != "1" ] || { zapret_apply_firewall; } +} +do_stop() +{ + zapret_stop_daemons + [ "$INIT_APPLY_FW" != "1" ] || zapret_unapply_firewall +} + +case "$1" in + start) + do_start + ;; + + stop) + do_stop + ;; + + restart) + do_stop + do_start + ;; + + start-fw|start_fw) + zapret_apply_firewall + ;; + stop-fw|stop_fw) + zapret_unapply_firewall + ;; + + restart-fw|restart_fw) + zapret_unapply_firewall + zapret_apply_firewall + ;; + + start-daemons|start_daemons) + zapret_run_daemons + ;; + stop-daemons|stop_daemons) + zapret_stop_daemons + ;; + restart-daemons|restart_daemons) + zapret_stop_daemons + zapret_run_daemons + ;; + + reload-ifsets|reload_ifsets) + zapret_reload_ifsets + ;; + list-ifsets|list_ifsets) + zapret_list_ifsets + ;; + list-table|list_table) + zapret_list_table + ;; + + *) + echo "Usage: $SCRIPT {start|stop|restart|start-fw|stop-fw|restart-fw|start-daemons|stop-daemons|restart-daemons|reload-ifsets|list-ifsets|list-table}" >&2 + exit 1 + ;; +esac + +exit 0