From f70d95966bbe55f8f467535b1a07b8c60db617ca Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 13 Jan 2023 17:37:09 +0300 Subject: [PATCH] feat: keenetic support --- common/installer.sh | 61 +++++++++-- init.d/keenetic/custom | 34 +++++++ init.d/keenetic/custom-nfqws-quic4all | 51 ++++++++++ init.d/keenetic/custom-reuse-builtin-mode | 49 +++++++++ init.d/keenetic/custom-tpws4http-nfqws4https | 69 +++++++++++++ init.d/keenetic/functions | 46 +++++++++ init.d/keenetic/netfilter.hook.sh | 20 ++++ init.d/keenetic/zapret | 43 ++++++++ install_easy.sh | 102 +++++++++++++++++++ uninstall_easy.sh | 14 +++ 10 files changed, 481 insertions(+), 8 deletions(-) create mode 100644 init.d/keenetic/custom create mode 100644 init.d/keenetic/custom-nfqws-quic4all create mode 100644 init.d/keenetic/custom-reuse-builtin-mode create mode 100644 init.d/keenetic/custom-tpws4http-nfqws4https create mode 100644 init.d/keenetic/functions create mode 100644 init.d/keenetic/netfilter.hook.sh create mode 100755 init.d/keenetic/zapret diff --git a/common/installer.sh b/common/installer.sh index 269fa61..922848d 100644 --- a/common/installer.sh +++ b/common/installer.sh @@ -107,14 +107,16 @@ check_system() } elif openrc_test; then SYSTEM=openrc + elif [ -f "/bin/ndm" ]; then + SYSTEM=keenetic 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 + SYSTEM=linux else - exitp 5 + exitp 5 fi fi elif [ "$UNAME" = "Darwin" ]; then @@ -129,16 +131,16 @@ check_system() get_free_space_mb() { - df -m $PWD | awk '/[0-9]%/{print $(NF-2)}' + df -m $PWD | awk '/[0-9]%/{print $(NF-2)}' } get_ram_kb() { - grep MemTotal /proc/meminfo | awk '{print $2}' + grep MemTotal /proc/meminfo | awk '{print $2}' } get_ram_mb() { - local R=$(get_ram_kb) - echo $(($R/1024)) + local R=$(get_ram_kb) + echo $(($R/1024)) } crontab_del() @@ -202,6 +204,9 @@ cron_ensure_running() /etc/init.d/cron enable /etc/init.d/cron start } + [ "$SYSTEM" = "keenetic" ] && { + /opt/etc/init.d/S10cron start + } } @@ -347,8 +352,8 @@ openwrt_fw_section_find() path=$(uci -q get firewall.@include[$i].path) [ -n "$path" ] || break [ "$path" = "$OPENWRT_FW_INCLUDE$1" ] && { - echo $i - return 0 + echo $i + return 0 } i=$(($i+1)) done @@ -462,3 +467,43 @@ remove_macos_firewall() pf_anchor_root_del pf_anchor_root_reload } + +install_keenetic_netfilter_hook() +{ + echo \* installing netfilter hook + + [ -n "MODE" ] || { + echo "should specify MODE in $ZAPRET_CONFIG" + exitp 7 + } + + echo "linking : $KEENETIC_NETFILTER_HOOK_SRC => $KEENETIC_NETFILTER_HOOK_DST" + ln -fs "$KEENETIC_NETFILTER_HOOK_SRC" "$KEENETIC_NETFILTER_HOOK_DST" +} + +remove_keenetic_netfilter_hook() +{ + rm -f "$KEENETIC_NETFILTER_HOOK_DST" +} + +service_install_keenetic() +{ + echo \* installing zapret service + + ln -sf "$ZAPRET_BASE/init.d/keenetic/zapret" /opt/etc/init.d/S99zapret +} + +service_start_keenetic() +{ + echo \* starting zapret service + + "$INIT_SCRIPT_SRC" start +} + +service_remove_keenetic() +{ + echo \* removing zapret service + + rm -f /opt/etc/init.d/S99zapret + zapret_stop_daemons +} diff --git a/init.d/keenetic/custom b/init.d/keenetic/custom new file mode 100644 index 0000000..666d2d4 --- /dev/null +++ b/init.d/keenetic/custom @@ -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/keenetic/custom-nfqws-quic4all b/init.d/keenetic/custom-nfqws-quic4all new file mode 100644 index 0000000..fcf21a4 --- /dev/null +++ b/init.d/keenetic/custom-nfqws-quic4all @@ -0,0 +1,51 @@ +#!/bin/sh + +# this custom script in addition to MODE=nfqws runs desync to all QUIC initial packets, without ipset/hostlist filtering +# need to add to config : NFQWS_OPT_DESYNC_QUIC="--dpi-desync=fake" +# russian TSPU version : NFQWS_OPT_DESYNC_QUIC="--dpi-desync=fake --dpi-desync-fake-quic=/opt/zapret/files/fake/quic_short_header.bin" +# NOTE : do not use TTL fooling. chromium QUIC engine breaks sessions if TTL expired in transit received + +QNUM2=$(($QNUM+10)) + +zapret_custom_daemons() +{ + # $1 - 1 - run, 0 - stop + + local MODE_OVERRIDE=nfqws + local opt + + zapret_do_daemons $1 + + opt="--qnum=$QNUM2 $NFQWS_OPT_DESYNC_QUIC" + do_nfqws $1 100 "$opt" +} +zapret_custom_firewall() +{ + # $1 - 1 - run, 0 - stop + + local MODE_OVERRIDE=nfqws + local f + local first_packets_only="-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:3" + local desync="-m mark ! --mark $DESYNC_MARK/$DESYNC_MARK" + + zapret_do_firewall_rules_ipt $1 + + f="-p udp --dport 443" + fw_nfqws_post $1 "$f $desync $first_packets_only" "$f $desync $first_packets_only" $QNUM2 + fw_nfqws_quic $1 "$f -m mark --mark $DESYNC_MARK/$DESYNC_MARK" +} +zapret_custom_firewall_nft() +{ + # stop logic is not required + + local MODE_OVERRIDE=nfqws + local f + local first_packets_only="ct original packets 1-3" + local desync="mark and $DESYNC_MARK == 0" + + zapret_apply_firewall_rules_nft + + f="udp dport 443" + nft_fw_nfqws_post "$f $desync $first_packets_only" "$f $desync $first_packets_only" $QNUM2 + nft_fw_nfqws_quic "$f mark and $DESYNC_MARK == $DESYNC_MARK" +} diff --git a/init.d/keenetic/custom-reuse-builtin-mode b/init.d/keenetic/custom-reuse-builtin-mode new file mode 100644 index 0000000..62fd8de --- /dev/null +++ b/init.d/keenetic/custom-reuse-builtin-mode @@ -0,0 +1,49 @@ +#!/bin/sh + +# this custom script demonstrates how to reuse built-in modes and add something from yourself + +MY_TPPORT=$(($TPPORT + 1)) +MY_TPWS_OPT="--methodeol --hostcase" +MY_DPORT=81 + +zapret_custom_daemons() +{ + # $1 - 1 - run, 0 - stop + + local MODE_OVERRIDE=tpws + local opt + + zapret_do_daemons $1 + + opt="--port=$MY_TPPORT $MY_TPWS_OPT" + filter_apply_hostlist_target opt + do_tpws $1 100 "$opt" +} +zapret_custom_firewall() +{ + # $1 - 1 - run, 0 - stop + + local MODE_OVERRIDE=tpws + local f4 f6 + + zapret_do_firewall_rules_ipt $1 + + f4="-p tcp --dport $MY_DPORT" + f6=$f4 + filter_apply_ipset_target f4 f6 + fw_tpws $1 "$f4" "$f6" $MY_TPPORT +} +zapret_custom_firewall_nft() +{ + # stop logic is not required + + local MODE_OVERRIDE=tpws + local f4 f6 + + zapret_apply_firewall_rules_nft + + f4="tcp dport $MY_DPORT" + f6=$f4 + nft_filter_apply_ipset_target f4 f6 + nft_fw_tpws "$f4" "$f6" $MY_TPPORT +} diff --git a/init.d/keenetic/custom-tpws4http-nfqws4https b/init.d/keenetic/custom-tpws4http-nfqws4https new file mode 100644 index 0000000..a07d7af --- /dev/null +++ b/init.d/keenetic/custom-tpws4http-nfqws4https @@ -0,0 +1,69 @@ +#!/bin/sh + +# this custom script demonstrates how to apply tpws to http and nfqws to https +# it preserves config settings : MODE_HTTP, MODE_HTTPS, MODE_FILTER, TPWS_OPT, NFQWS_OPT_DESYNC, NFQWS_OPT_DESYNC_HTTPS + +zapret_custom_daemons() +{ + # $1 - 1 - run, 0 - stop + + local opt + + [ "$MODE_HTTP" = "1" ] && { + opt="--port=$TPPORT $TPWS_OPT" + filter_apply_hostlist_target opt + do_tpws $1 1 "$opt" + } + + [ "$MODE_HTTPS" = "1" ] && { + opt="--qnum=$QNUM $NFQWS_OPT_DESYNC_HTTPS" + filter_apply_hostlist_target opt + do_nfqws $1 2 "$opt" + } +} +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" + + [ "$MODE_HTTP" = "1" ] && { + f4="-p tcp --dport 80" + f6=$f4 + filter_apply_ipset_target f4 f6 + fw_tpws $1 "$f4" "$f6" $TPPORT + } + + [ "$MODE_HTTPS" = "1" ] && { + f4="-p tcp --dport 443 $first_packet_only" + f6=$f4 + filter_apply_ipset_target f4 f6 + fw_nfqws_post $1 "$f4 $desync" "$f6 $desync" $QNUM + fw_nfqws_quic $1 "$f4 -m mark --mark $DESYNC_MARK/$DESYNC_MARK" + } +} +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 + nft_fw_nfqws_quic $1 "$f4 mark and $DESYNC_MARK == $DESYNC_MARK" + } +} diff --git a/init.d/keenetic/functions b/init.d/keenetic/functions new file mode 100644 index 0000000..56f58f2 --- /dev/null +++ b/init.d/keenetic/functions @@ -0,0 +1,46 @@ +#!/bin/sh + +SCRIPT=$(readlink -f "$0") +EXEDIR=$(dirname "$SCRIPT") +ZAPRET_BASE=$(readlink -f "$EXEDIR/../..") +. "$ZAPRET_BASE/init.d/sysv/functions" + +CUSTOM_SCRIPT="$ZAPRET_BASE/init.d/keenetic/custom" +[ -f "$CUSTOM_SCRIPT" ] && . "$CUSTOM_SCRIPT" + +load_kmod() +{ + if lsmod | grep "$1" &> /dev/null ; then + echo "$1.ko is already loaded" + else + if insmod "/lib/modules/$(uname -r)/$1.ko" &> /dev/null; then + echo "$1.ko loaded" + else + echo "Cannot find $1.ko kernel module, aborting" + exit 1 + fi + fi +} + +# Fix local source ip issue when quic packets were sent with raw sockets (no Keenetic-specific iptable marks were applied) +fw_nfqws_quic() +{ + # $1 - 1 - add, 0 - del + # $2 - iptable filter + + ipt_print_op $1 "$2" "nfqws quic masquerade" + + rule="$2 -d 0/0 -j MASQUERADE" + if [ -n "$IFACE_WAN" ] ; then + for wan in $IFACE_WAN; do + ipt_add_del $1 POSTROUTING -t nat -o $wan $rule + done + else + ipt_add_del $1 POSTROUTING -t nat $rule + fi +} + +nft_fw_nfqws_quic() +{ + echo "Quic through nft is not supported" +} diff --git a/init.d/keenetic/netfilter.hook.sh b/init.d/keenetic/netfilter.hook.sh new file mode 100644 index 0000000..13c206a --- /dev/null +++ b/init.d/keenetic/netfilter.hook.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +[ "$type" == "ip6tables" ] && exit 0 +[ "$table" != "mangle" ] && exit 0 + +SCRIPT=$(readlink /opt/etc/init.d/S99zapret) +if [ -n "$SCRIPT" ]; then + EXEDIR=$(dirname "$SCRIPT") + ZAPRET_BASE=$(readlink -f "$EXEDIR/../..") +else + ZAPRET_BASE=/opt/zapret +fi + +. "$EXEDIR/functions" + +case $MODE in + nfqws|twps|custom) + zapret_apply_firewall + ;; +esac diff --git a/init.d/keenetic/zapret b/init.d/keenetic/zapret new file mode 100755 index 0000000..c2099a4 --- /dev/null +++ b/init.d/keenetic/zapret @@ -0,0 +1,43 @@ +#!/bin/sh + +SCRIPT=$(readlink -f "$0") +EXEDIR=$(dirname "$SCRIPT") +ZAPRET_BASE=$(readlink -f "$EXEDIR/../..") +. "$EXEDIR/functions" + +do_start() +{ + sysctl -w "net.netfilter.nf_conntrack_checksum=0" + load_kmod xt_string + load_kmod xt_NFQUEUE + load_kmod xt_multiport + load_kmod xt_owner + load_kmod xt_connbytes + zapret_run_daemons + [ "$INIT_APPLY_FW" != "1" ] || { zapret_apply_firewall; } +} +do_stop() +{ + sysctl -w "net.netfilter.nf_conntrack_checksum=1" + zapret_stop_daemons + [ "$INIT_APPLY_FW" != "1" ] || zapret_unapply_firewall +} + +case $1 in + start) + do_start + ;; + stop|kill) + do_stop + ;; + restart) + "$0" stop + "$0" start + ;; + *) + echo -e "Usage: $0 (start|stop|restart)" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/install_easy.sh b/install_easy.sh index ebef8ff..0ab0b79 100755 --- a/install_easy.sh +++ b/install_easy.sh @@ -964,6 +964,105 @@ install_macos() service_start_macos } +check_prerequisites_keenetic() +{ + echo \* checking prerequisites + + local PKGS="curl" PKGS UPD=0 + + case "$FWTYPE" in + iptables) + PKGS="$PKGS" + [ "$DISABLE_IPV6" != "1" ] && PKGS="$PKGS" + ;; + nftables) + PKGS="$PKGS" + [ "$DISABLE_IPV6" != "1" ] && PKGS="$PKGS" + ;; + esac + + if check_packages_openwrt $PKGS ; then + echo everything is present + else + echo \* installing prerequisites + + opkg update + UPD=1 + opkg install $PKGS || { + echo could not install prerequisites + exitp 6 + } + fi + + is_linked_to_busybox gzip && { + echo + echo your system uses default busybox gzip. its several times slower than GNU gzip. + echo ip/host list scripts will run much faster with GNU gzip + echo installer can install GNU gzip but it requires about 100 Kb space + if ask_yes_no N "do you want to install GNU gzip"; then + [ "$UPD" = "0" ] && { + opkg update + UPD=1 + } + opkg install --force-overwrite gzip + fi + } + is_linked_to_busybox sort && { + echo + echo your system uses default busybox sort. its much slower and consumes much more RAM than GNU sort + echo ip/host list scripts will run much faster with GNU sort + echo installer can install GNU sort but it requires about 100 Kb space + if ask_yes_no N "do you want to install GNU sort"; then + [ "$UPD" = "0" ] && { + opkg update + UPD=1 + } + opkg install --force-overwrite coreutils-sort + fi + } + is_linked_to_busybox grep && { + echo + echo your system uses default busybox grep. its damn infinite slow with -f option + echo get_combined.sh will be severely impacted + echo installer can install GNU grep but it requires about 0.5 Mb space + if ask_yes_no N "do you want to install GNU grep"; then + [ "$UPD" = "0" ] && { + opkg update + UPD=1 + } + opkg install --force-overwrite grep + + # someone reported device partially fail if /bin/grep is absent + # grep package deletes /bin/grep + [ -f /bin/grep ] || ln -s busybox /bin/grep + fi + } +} + +install_keenetic() +{ + INIT_SCRIPT_SRC="$EXEDIR/init.d/keenetic/zapret" + KEENETIC_NETFILTER_HOOK_SRC="$EXEDIR/init.d/keenetic/netfilter.hook.sh" + KEENETIC_NETFILTER_HOOK_DST=/opt/etc/ndm/netfilter.d/zapret.sh + + check_bins + require_root + check_location copy_all + install_binaries + check_dns + select_fwtype + check_prerequisites_keenetic + select_ipv6 + ask_config + ask_config_offload + service_install_keenetic + download_list + crontab_del_quiet + crontab_add 0 6 + cron_ensure_running + install_keenetic_netfilter_hook + service_start_keenetic +} # build binaries, do not use precompiled [ "$1" = "make" ] && FORCE_BUILD=1 @@ -986,6 +1085,9 @@ case $SYSTEM in openwrt) install_openwrt ;; + keenetic) + install_keenetic + ;; macos) install_macos ;; diff --git a/uninstall_easy.sh b/uninstall_easy.sh index 47126e6..db4e633 100755 --- a/uninstall_easy.sh +++ b/uninstall_easy.sh @@ -73,6 +73,17 @@ remove_macos() crontab_del } +remove_keenetic() +{ + KEENETIC_NETFILTER_HOOK_DST=/opt/etc/ndm/netfilter.d/zapret.sh + + clear_ipset + remove_keenetic_netfilter_hook + service_remove_keenetic + nft_del_table + crontab_del +} + fix_sbin_path check_system @@ -96,6 +107,9 @@ case $SYSTEM in macos) remove_macos ;; + keenetic) + remove_keenetic + ;; esac