UA3F/openwrt/files/ua3f.init

494 lines
17 KiB
Bash
Executable File

#!/bin/sh /etc/rc.common
# shellcheck disable=SC2034,SC1083,SC3043,SC2086
USE_PROCD=1
START=99
NAME="ua3f"
PROG="/usr/bin/$NAME"
SERVER_MODE=""
SERVER_PORT="1080"
FW_BACKEND=""
NFT_TABLE="UA3F"
UA3F_CHAIN="UA3F"
UA3F_OUT_CHAIN="UA3F_OUTPUT"
UA3F_LANSET="ua3f_localnetwork"
UA3F_SOMARK="0xc9"
UA3F_FWMARK="0x1c9"
ROUTE_TABLE="0x1c9"
UA3F_GID="65534"
UA3F_GROUP="nogroup"
SKIP_GIDS=""
SIDECAR="OC"
FAKEIP_RANGE="198.18.0.0/16, 198.18.0.1/15, 28.0.0.1/8"
LOG_FILE="/var/log/ua3f/ua3f.log"
LOG() {
if [ -n "${1}" ]; then
printf '[%s] %s\n' "$(date "+%Y-%m-%d %H:%M:%S")" "$1" >>"$LOG_FILE"
fi
}
try_modprobe() { command -v modprobe >/dev/null 2>&1 && modprobe "$1" 2>/dev/null; }
nft_available() { command -v nft >/dev/null 2>&1; }
ipt_available() { command -v iptables >/dev/null 2>&1; }
opkg_available() { command -v opkg >/dev/null 2>&1; }
openclash_exists() {
if opkg_available; then
if opkg list-installed luci-app-openclash | grep -q 'luci-app-openclash'; then
return 0
fi
fi
return 1
}
openclash_running() {
if pgrep -f "openclash" >/dev/null 2>&1; then
return 0
fi
return 1
}
shellclash_exists() {
if id -u shellclash >/dev/null 2>&1; then
return 0
fi
if id -u shellcrash >/dev/null 2>&1; then
return 0
fi
return 1
}
shellclash_running() {
if pgrep -f "ShellCrash" >/dev/null 2>&1; then
return 0
fi
return 1
}
set_ua3f_group() {
add_skip_gids "453"
if openclash_running; then
UA3F_GID="65534"
UA3F_GROUP="nogroup"
SIDECAR="OCSC"
add_skip_gids "7890"
elif shellclash_running; then
UA3F_GID="7890"
UA3F_GROUP="shellcrash"
add_skip_gids "65534"
SIDECAR="SC"
elif openclash_exists; then
UA3F_GID="65534"
UA3F_GROUP="nogroup"
add_skip_gids "7890"
SIDECAR="OC"
elif shellclash_exists; then
UA3F_GID="7890"
UA3F_GROUP="shellcrash"
add_skip_gids "65534"
SIDECAR="SC"
else
UA3F_GID="65534"
UA3F_GROUP="nogroup"
add_skip_gids "7890"
SIDECAR="OC"
fi
}
add_skip_gids() {
for gid in "$@"; do
[ -z "$gid" ] && continue
case ",$SKIP_GIDS," in
*,"$gid",*) ;;
*)
if [ -z "$SKIP_GIDS" ]; then
SKIP_GIDS=$gid
else
SKIP_GIDS=$SKIP_GIDS,$gid
fi
;;
esac
done
}
detect_backend() {
if opkg_available; then
if opkg list-installed kmod-nft-tproxy | grep -q 'kmod-nft-tproxy'; then
FW_BACKEND="nft"
return 0
else
FW_BACKEND="ipt"
return 0
fi
fi
if nft_available; then
FW_BACKEND="nft"
return 0
fi
if ipt_available; then
FW_BACKEND="ipt"
return 0
fi
FW_BACKEND=""
return 1
}
add_tproxy_route() {
sysctl -w net.bridge.bridge-nf-call-iptables=0 >/dev/null 2>&1
sysctl -w net.bridge.bridge-nf-call-ip6tables=0 >/dev/null 2>&1
if ! output=$(ip rule add fwmark "$UA3F_FWMARK" table "$ROUTE_TABLE" 2>&1); then
LOG "Failed to add ip rule fwmark: $output"
return 1
fi
if ! output=$(ip route add local 0.0.0.0/0 dev lo table "$ROUTE_TABLE" 2>&1); then
LOG "Failed to add ip route local lo: $output"
return 1
fi
}
cleanup_tproxy_route() {
ip route flush table "$ROUTE_TABLE" >/dev/null 2>&1
ip rule del fwmark "$UA3F_FWMARK" table "$ROUTE_TABLE" >/dev/null 2>&1
ip rule del fwmark 0x1c9 table "$ROUTE_TABLE" >/dev/null 2>&1
}
nft_drop_table() { nft delete table ip "$NFT_TABLE" 2>/dev/null; }
nft_reinit_table() {
nft_drop_table
nft add table ip "$NFT_TABLE" || return 1
nft "add set ip $NFT_TABLE $UA3F_LANSET { type ipv4_addr; flags interval; auto-merge; }" || return 1
nft "add element ip $NFT_TABLE $UA3F_LANSET { 0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.168.0.0/16, 224.0.0.0/4, 240.0.0.0/4 }" >/dev/null 2>&1
}
fw_setup_nft_tproxy_tcp() {
nft_reinit_table || {
LOG "Failed to reinitialize nft table"
return 1
}
add_tproxy_route || {
LOG "Failed to add tproxy route"
return 1
}
if [ "$SIDECAR" = "SC" ]; then
nft add chain ip $NFT_TABLE sidecar '{ type filter hook prerouting priority mangle - 20; }'
nft add rule ip $NFT_TABLE sidecar meta l4proto tcp mark $UA3F_FWMARK mark set 7894 tproxy to 127.0.0.1:$SERVER_PORT counter accept comment '"cap sc"'
fi
# PREROUTING -> UA3F
nft add chain ip $NFT_TABLE prerouting '{ type filter hook prerouting priority filter + 20; }'
nft add rule ip $NFT_TABLE prerouting meta l4proto != tcp counter return
nft add rule ip $NFT_TABLE prerouting ct direction reply counter return
nft add rule ip $NFT_TABLE prerouting mark {$UA3F_SOMARK} counter return comment '"UA3F somark, never hit"'
nft add rule ip $NFT_TABLE prerouting mark {0x162} counter return comment '"354"'
nft add rule ip $NFT_TABLE prerouting mark {0x1ed4} counter return comment '"sc tproxy mark 7892"'
nft add rule ip $NFT_TABLE prerouting ip daddr {$FAKEIP_RANGE} counter return comment '"fakeip range"'
nft add rule ip $NFT_TABLE prerouting ip daddr @$UA3F_LANSET counter return
nft add rule ip $NFT_TABLE prerouting meta l4proto tcp mark $UA3F_FWMARK tproxy to 127.0.0.1:$SERVER_PORT counter accept comment '"cap oc"'
nft add rule ip $NFT_TABLE prerouting meta l4proto tcp mark set $UA3F_FWMARK tproxy to 127.0.0.1:$SERVER_PORT counter accept comment '"default less hit. sc"'
# OUTPUT -> UA3F_OUTPUT
nft add chain ip $NFT_TABLE output '{ type route hook output priority filter + 20; }'
nft add rule ip $NFT_TABLE output meta l4proto != tcp counter return
nft add rule ip $NFT_TABLE output mark $UA3F_SOMARK counter return comment '"UA3F somark"'
nft add rule ip $NFT_TABLE output ip daddr {$FAKEIP_RANGE} counter return
nft add rule ip $NFT_TABLE output meta skgid {$SKIP_GIDS} counter return
nft add rule ip $NFT_TABLE output ip daddr @$UA3F_LANSET counter return
nft add rule ip $NFT_TABLE output meta l4proto tcp meta skgid $UA3F_GID mark set $UA3F_FWMARK counter accept comment '"ghost oc"'
nft add rule ip $NFT_TABLE output meta l4proto tcp mark set $UA3F_FWMARK counter accept comment '"default tproxy mark. bypass sc pre pollution"'
}
fw_setup_nft_redirect_tcp() {
nft_reinit_table || return 1
# PREROUTING -> UA3F
nft add chain ip $NFT_TABLE prerouting '{ type nat hook prerouting priority filter + 20; }'
nft add rule ip $NFT_TABLE prerouting meta l4proto != tcp counter return
nft add rule ip $NFT_TABLE prerouting ct direction reply counter return
nft add rule ip $NFT_TABLE prerouting mark {$UA3F_SOMARK} counter return comment '"UA3F somark, never hit"'
nft add rule ip $NFT_TABLE prerouting mark {0x162} counter return comment '"354"'
nft add rule ip $NFT_TABLE prerouting mark {0x1ed4} counter return comment '"sc tproxy mark 7892"'
nft add rule ip $NFT_TABLE prerouting ip daddr {$FAKEIP_RANGE} counter return comment '"fakeip range"'
nft add rule ip $NFT_TABLE prerouting ip daddr @$UA3F_LANSET counter return
nft add rule ip $NFT_TABLE prerouting tcp dport != {22} counter redirect to :$SERVER_PORT
# OUTPUT -> UA3F_OUTPUT
nft add chain ip $NFT_TABLE output '{ type nat hook output priority filter + 20; }'
nft add rule ip $NFT_TABLE output meta l4proto != tcp counter return
nft add rule ip $NFT_TABLE output mark $UA3F_SOMARK counter return comment '"UA3F somark"'
nft add rule ip $NFT_TABLE output ip daddr {$FAKEIP_RANGE} counter return
nft add rule ip $NFT_TABLE output ip daddr @$UA3F_LANSET counter return
nft add rule ip $NFT_TABLE output meta skgid {$SKIP_GIDS} counter return
nft add rule ip $NFT_TABLE output meta l4proto tcp mark {0x1ed6} counter redirect to :$SERVER_PORT comment '"cap sc meta"'
nft add rule ip $NFT_TABLE output meta skgid $UA3F_GID tcp dport != {22} counter redirect to :$SERVER_PORT comment '"cap oc"'
nft add rule ip $NFT_TABLE output tcp dport != {22} counter redirect to :$SERVER_PORT comment '"cap scc"'
}
fw_revert_nft() {
nft_drop_table
cleanup_tproxy_route
}
setup_ipset_ipt() {
cleanup_ipset_ipt
ipset create $UA3F_LANSET hash:net || return 1
ipset add $UA3F_LANSET 0.0.0.0/8
ipset add $UA3F_LANSET 10.0.0.0/8
ipset add $UA3F_LANSET 100.64.0.0/10
ipset add $UA3F_LANSET 127.0.0.0/8
ipset add $UA3F_LANSET 169.254.0.0/16
ipset add $UA3F_LANSET 172.16.0.0/12
ipset add $UA3F_LANSET 192.168.0.0/16
ipset add $UA3F_LANSET 224.0.0.0/4
ipset add $UA3F_LANSET 240.0.0.0/4
}
fw_setup_ipt_tproxy_tcp() {
setup_ipset_ipt || return 1
add_tproxy_route || return 1
if [ "$SIDECAR" = "SC" ]; then
iptables -t mangle -F SIDECAR 2>/dev/null
iptables -t mangle -D PREROUTING -p tcp -j SIDECAR 2>/dev/null
iptables -t mangle -X SIDECAR 2>/dev/null
iptables -t mangle -N SIDECAR
iptables -t mangle -I PREROUTING -p tcp -j SIDECAR
iptables -t mangle -A SIDECAR -m mark --mark $UA3F_FWMARK -p tcp -j TPROXY --on-ip 127.0.0.1 --on-port $SERVER_PORT --tproxy-mark 7894
fi
# PREROUTING
iptables -t mangle -F $UA3F_CHAIN 2>/dev/null
iptables -t mangle -D PREROUTING -p tcp -j $UA3F_CHAIN 2>/dev/null
iptables -t mangle -X $UA3F_CHAIN 2>/dev/null
iptables -t mangle -N $UA3F_CHAIN
iptables -t mangle -A PREROUTING -p tcp -j $UA3F_CHAIN
iptables -t mangle -A $UA3F_CHAIN -m conntrack --ctdir REPLY -j RETURN
iptables -t mangle -A $UA3F_CHAIN -m mark --mark $UA3F_SOMARK -j RETURN
iptables -t mangle -A $UA3F_CHAIN -m mark --mark 0x162 -j RETURN
iptables -t mangle -A $UA3F_CHAIN -m mark --mark 0x1ed4 -j RETURN
iptables -t mangle -A $UA3F_CHAIN -d 198.18.0.0/16 -j RETURN
iptables -t mangle -A $UA3F_CHAIN -d 28.0.0.1/8 -j RETURN
iptables -t mangle -A $UA3F_CHAIN -d 198.18.0.1/15 -j RETURN
iptables -t mangle -A $UA3F_CHAIN -m set --match-set $UA3F_LANSET dst -j RETURN
iptables -t mangle -A $UA3F_CHAIN -p tcp -m mark --mark $UA3F_FWMARK -j TPROXY --on-ip 127.0.0.1 --on-port $SERVER_PORT
iptables -t mangle -A $UA3F_CHAIN -p tcp -j TPROXY --on-ip 127.0.0.1 --on-port $SERVER_PORT --tproxy-mark $UA3F_FWMARK
# OUTPUT
iptables -t mangle -F $UA3F_OUT_CHAIN 2>/dev/null
iptables -t mangle -D OUTPUT -p tcp -j $UA3F_OUT_CHAIN 2>/dev/null
iptables -t mangle -X $UA3F_OUT_CHAIN 2>/dev/null
iptables -t mangle -N $UA3F_OUT_CHAIN
iptables -t mangle -I OUTPUT -p tcp -j $UA3F_OUT_CHAIN
iptables -t mangle -A $UA3F_OUT_CHAIN -m mark --mark $UA3F_SOMARK -j RETURN
iptables -t mangle -A $UA3F_OUT_CHAIN -d 198.18.0.0/16 -j RETURN
iptables -t mangle -A $UA3F_OUT_CHAIN -d 28.0.0.1/8 -j RETURN
iptables -t mangle -A $UA3F_OUT_CHAIN -d 198.18.0.1/15 -j RETURN
iptables -t mangle -A $UA3F_OUT_CHAIN -p tcp -m owner --gid-owner 453 -j RETURN
iptables -t mangle -A $UA3F_OUT_CHAIN -m set --match-set $UA3F_LANSET dst -j RETURN
iptables -t mangle -A $UA3F_OUT_CHAIN -p tcp -m owner --gid-owner $UA3F_GID -j MARK --set-mark $UA3F_FWMARK
iptables -t mangle -A $UA3F_OUT_CHAIN -p tcp -j MARK --set-mark $UA3F_FWMARK
}
fw_setup_ipt_redirect_tcp() {
setup_ipset_ipt || return 1
# PREROUTING
iptables -t nat -F $UA3F_CHAIN 2>/dev/null
iptables -t nat -D PREROUTING -p tcp -j $UA3F_CHAIN 2>/dev/null
iptables -t nat -X $UA3F_CHAIN 2>/dev/null
iptables -t nat -N $UA3F_CHAIN
iptables -t nat -A PREROUTING -p tcp -j $UA3F_CHAIN
iptables -t nat -A $UA3F_CHAIN -m conntrack --ctdir REPLY -j RETURN
iptables -t nat -A $UA3F_CHAIN -m mark --mark $UA3F_SOMARK -j RETURN
iptables -t nat -A $UA3F_CHAIN -m mark --mark 0x162 -j RETURN
iptables -t nat -A $UA3F_CHAIN -m mark --mark 0x1ed4 -j RETURN
iptables -t nat -A $UA3F_CHAIN -d 198.18.0.0/16 -j RETURN
iptables -t nat -A $UA3F_CHAIN -d 28.0.0.1/8 -j RETURN
iptables -t nat -A $UA3F_CHAIN -d 198.18.0.1/15 -j RETURN
iptables -t nat -A $UA3F_CHAIN -m set --match-set $UA3F_LANSET dst -j RETURN
iptables -t nat -A $UA3F_CHAIN -p tcp -j REDIRECT --to-ports $SERVER_PORT
# OUTPUT
iptables -t nat -F $UA3F_OUT_CHAIN 2>/dev/null
iptables -t nat -D OUTPUT -p tcp -j $UA3F_OUT_CHAIN 2>/dev/null
iptables -t nat -X $UA3F_OUT_CHAIN 2>/dev/null
iptables -t nat -N $UA3F_OUT_CHAIN
iptables -t nat -I OUTPUT -p tcp -j $UA3F_OUT_CHAIN
iptables -t nat -A $UA3F_OUT_CHAIN -m mark --mark $UA3F_SOMARK -j RETURN
iptables -t nat -A $UA3F_OUT_CHAIN -d 198.18.0.0/16 -j RETURN
iptables -t nat -A $UA3F_OUT_CHAIN -d 28.0.0.1/8 -j RETURN
iptables -t nat -A $UA3F_OUT_CHAIN -d 198.18.0.1/15 -j RETURN
iptables -t nat -A $UA3F_OUT_CHAIN -m set --match-set $UA3F_LANSET dst -j RETURN
iptables -t nat -A $UA3F_OUT_CHAIN -m owner --gid-owner 453 -j RETURN
iptables -t nat -A $UA3F_OUT_CHAIN -p tcp -m mark --mark 0x1ed6 -j REDIRECT --to-ports $SERVER_PORT
iptables -t nat -A $UA3F_OUT_CHAIN -p tcp -m owner --gid-owner $UA3F_GID -j REDIRECT --to-ports $SERVER_PORT
iptables -t nat -A $UA3F_OUT_CHAIN -p tcp -j REDIRECT --to-ports $SERVER_PORT
}
cleanup_ipset_ipt() {
ipset destroy $UA3F_LANSET 2>/dev/null
}
fw_revert_ipt() {
# sidecar
iptables -t mangle -F SIDECAR 2>/dev/null
iptables -t mangle -D PREROUTING -p tcp -j SIDECAR 2>/dev/null
iptables -t mangle -X SIDECAR 2>/dev/null
# mangle
iptables -t mangle -D PREROUTING -p tcp -j $UA3F_CHAIN 2>/dev/null
iptables -t mangle -F $UA3F_CHAIN 2>/dev/null
iptables -t mangle -X $UA3F_CHAIN 2>/dev/null
iptables -t mangle -D OUTPUT -p tcp -j $UA3F_OUT_CHAIN 2>/dev/null
iptables -t mangle -F $UA3F_OUT_CHAIN 2>/dev/null
iptables -t mangle -X $UA3F_OUT_CHAIN 2>/dev/null
# nat
iptables -t nat -D PREROUTING -p tcp -j $UA3F_CHAIN 2>/dev/null
iptables -t nat -F $UA3F_CHAIN 2>/dev/null
iptables -t nat -X $UA3F_CHAIN 2>/dev/null
iptables -t nat -D OUTPUT -p tcp -j $UA3F_OUT_CHAIN 2>/dev/null
iptables -t nat -F $UA3F_OUT_CHAIN 2>/dev/null
iptables -t nat -X $UA3F_OUT_CHAIN 2>/dev/null
# ipset
cleanup_ipset_ipt
cleanup_tproxy_route
}
start_service() {
config_load "$NAME"
mkdir -p /var/log/ua3f
chmod o+w /var/log/ua3f
touch /var/log/ua3f/ua3f.log
local enabled
config_get_bool enabled "enabled" "enabled" "0"
if [ "$enabled" -ne "1" ]; then
return 1
fi
LOG "Starting $NAME service..."
local server_mode port bind ua log_level ua_regex partial_replace
config_get server_mode "main" "server_mode" "SOCKS5"
config_get port "main" "port" "1080"
config_get bind "main" "bind" "127.0.0.1"
config_get ua "main" "ua" "FFF"
config_get ua_regex "main" "ua_regex" ""
config_get_bool partial_replace "main" "partial_replace" 0
config_get log_level "main" "log_level" "info"
SERVER_MODE="$(echo "$server_mode" | tr '[:lower:]' '[:upper:]')"
SERVER_MODE="$server_mode"
LOG "Server Mode: $SERVER_MODE"
LOG "Port: $port"
LOG "Bind: $bind"
LOG "User-Agent: $ua"
LOG "User-Agent Regex: $ua_regex"
LOG "Log level: $log_level"
LOG "Partial Replace: $partial_replace"
set_ua3f_group
LOG "Run as GID: $UA3F_GID, Group: $UA3F_GROUP"
LOG "Skip GIDs: $SKIP_GIDS"
LOG "UA3F_FWMARK: $UA3F_FWMARK"
detect_backend || {
LOG "No supported firewall backend found (nftables or iptables)"
return 1
}
LOG "Using firewall backend: $FW_BACKEND"
# Always cleanup first (idempotent)
if [ "$FW_BACKEND" = "nft" ]; then
fw_revert_nft
else
fw_revert_ipt
fi
case "$SERVER_MODE" in
SOCKS5)
# No firewall interception
;;
TPROXY)
if [ "$FW_BACKEND" = "nft" ]; then
try_modprobe nft_tproxy
fw_setup_nft_tproxy_tcp || {
LOG "fw_setup_nft_tproxy_tcp setup failed"
return 1
}
else
try_modprobe xt_TPROXY
fw_setup_ipt_tproxy_tcp || {
LOG "fw_setup_ipt_tproxy_tcp setup failed"
return 1
}
fi
;;
REDIRECT)
if [ "$FW_BACKEND" = "nft" ]; then
fw_setup_nft_redirect_tcp || {
LOG "fw_setup_nft_redirect_tcp setup failed"
return 1
}
else
fw_setup_ipt_redirect_tcp || {
LOG "fw_setup_ipt_redirect_tcp setup failed"
return 1
}
fi
;;
*)
LOG "Unsupported server_mode: $SERVER_MODE"
return 1
;;
esac
procd_open_instance "$NAME"
procd_set_param command "$PROG"
procd_append_param command -m "$server_mode"
procd_append_param command -p "$port"
procd_append_param command -b "$bind"
procd_append_param command -f "$ua"
procd_append_param command -r "$ua_regex"
procd_append_param command -l "$log_level"
[ "$partial_replace" = "1" ] && procd_append_param command -s
procd_set_param respawn
procd_set_param stdout 1
procd_set_param stderr 1
procd_set_param limits nproc="unlimited" as="unlimited" memlock="unlimited" nofile="65535 65535"
procd_set_param group $UA3F_GROUP
LOG "$NAME service started"
procd_close_instance
}
stop_service() {
LOG "Stopping $NAME service..."
fw_revert_ipt >/dev/null 2>&1
fw_revert_nft >/dev/null 2>&1
LOG "$NAME service stopped"
}
reload_service() {
set_ua3f_group
stop
start
}
service_triggers() {
procd_add_reload_trigger "$NAME"
}