Files
BFR/box/scripts/box.iptables
2026-08-12 15:12:01 +03:00

895 lines
34 KiB
Bash
Executable File

#!/system/bin/sh
# Validate settings.ini
if ! /system/bin/sh -n /data/adb/box/settings.ini 2>"/data/adb/box/run/settings_err.log"; then
echo "Err: settings.ini contains a syntax error" | tee -a "/data/adb/box/run/settings_err.log"
exit 1
fi
scripts_dir="${0%/*}"
source /data/adb/box/settings.ini
fwmark="16777216/16777216"
table="2024"
pref="100"
# disable or enable QUIC using iptables rules. Note that this may cause some websites to become inaccessible.
quic="enable"
clash_dns_forward="enable"
fake_ip_range=""
[ "${stealth_block_quic}" = "true" ] && quic="disable"
# ex: 7.1.1
buildVersion=$(getprop ro.build.version.release)
minBuildVersion="11"
IPV="iptables" # Default
IP6V="ip6tables" # Default
# ex: 7.1.1 -> 7
buildVersionMajor=${buildVersion%%.*}
if [ "$buildVersionMajor" -ge "$minBuildVersion" ]; then
IPV="iptables -w 100"
IP6V="ip6tables -w 100"
fi
case "${bin_name}" in
"clash")
clash_mode=$(busybox awk '!/^ *#/ && /mode: / { print $2;found=1; exit } END{ if(!found) print "rules" }' "${clash_config}" 2>/dev/null)
clash_enhanced_mode=$(busybox awk '!/^ *#/ && /enhanced-mode: / { print $2;found=1; exit } END{ if(!found) print "fake-ip" }' "${clash_config}" 2>/dev/null)
fake_ip_range=$(busybox awk '!/^ *#/ && /fake-ip-range:/ { print $2; found=1; exit } END { if (!found) print "198.18.0.1/16" }' "${clash_config}" 2>/dev/null)
clash_dns_port=$(sed -n '/^dns:/,/^[^ ]/p' "${clash_config}" | grep -E '^[^#]*listen:.*:[0-9]+' | grep -Eo '[0-9]+' | tail -n 1)
clash_dns_port=${clash_dns_port:-1053}
if [[ "${network_mode}" == @(mixed|tun) ]]; then
tun_device=$(busybox awk '!/^ *#/ && /device: / { print $2;found=1; exit } END{ if(!found) print "meta" }' "${clash_config}" 2>/dev/null)
fi
;;
"sing-box")
if [[ "${network_mode}" == @(mixed|tun) ]]; then
tun_device=$(find "${box_dir}/sing-box/" -maxdepth 1 -type f -name "*.json" -exec busybox grep -oE '"interface_name": "[^"]*' {} + | busybox awk -F'"' '{print $4}' 2>/dev/null | head -n 1)
if [ -z "$tun_device" ]; then
tun_device="tun0"
fi
fi
fake_ip_range=$(find ${box_dir}/sing-box/ -maxdepth 1 -type f -name "*.json" -exec busybox awk -F'"' '/inet4_range/ {print $4}' {} +)
fake_ip6_range=$(find ${box_dir}/sing-box/ -maxdepth 1 -type f -name "*.json" -exec busybox awk -F'"' '/inet6_range/ {print $4}' {} +)
;;
"hysteria")
case "${network_mode}" in
redirect|tproxy|enhance)
true # do nothing
;;
*)
log Warning "$bin_name does not support network_mode: $network_mode, return to TProxy"
sed -i 's/\(network_mode=\)\"[^\"]*\"/\1"tproxy"/g' ${settings}
;;
esac
;;
"xray" | "v2fly")
if [[ "${network_mode}" != "tproxy" ]]; then
log Warning "$bin_name does not support network_mode: $network_mode, return to TProxy"
sed -i 's/\(network_mode=\)\"[^\"]*\"/\1"tproxy"/g' ${settings}
fi
;;
*)
log Error "<${bin_name}> unknown binary."
exit 1
;;
esac
box_etc() {
case "${bin_name}" in
clash)
log Debug "enhanced-mode: $clash_enhanced_mode, fake-ip-range: $fake_ip_range, listen-port: $clash_dns_port, mode: $clash_mode"
;;
sing-box)
if [ -n "${fake_ip_range}" ] && [ "${bin_name}" = "sing-box" ]; then
log Debug "fake-ip-range: ${fake_ip_range}, ${fake_ip6_range}"
fi
;;
*)
true
;;
esac
if [[ "${network_mode}" == @(mixed|tun) ]]; then
log Info "tun device: ($tun_device)"
fi
}
bin_alive() {
local PID=$(<"${box_pid}" 2>/dev/null)
if ! kill -0 "$PID" >/dev/null; then
log Error "$(<"${box_run}/${bin_name}.log")"
log Error "${bin_name} service is not running."
log Error "please check ${bin_name}.log for more information."
log Error "killing stale pid $PID"
for bin in "${bin_list[@]}"; do
killall -15 "${bin}" >/dev/null 2>&1 || busybox pkill -15 "${bin}" >/dev/null 2>&1
done
cleanup_iptables
[ -f "${box_pid}" ] && rm -f "${box_pid}"
return 1
else
return 0
fi
}
find_packages_uid() {
echo -n "" > "${uid_list}"
for package in "${packages_list[@]}"; do
if [[ "$package" == *:* ]]; then
user="${package%%:*}"
pkg="${package##*:}"
else
user=0
pkg="$package"
fi
appid="$(busybox awk -v p="$pkg" '$1 == p {print $2}' "$system_packages_file")"
if [[ -n "$appid" ]]; then
uid=$((user * 100000 + appid))
echo "$uid" >> "${uid_list}"
fi
done
}
probe_user_group() {
if PID=$(busybox pidof ${bin_name}) ; then
box_user=$(stat -c %U /proc/$PID)
box_group=$(stat -c %G /proc/$PID)
return 0
else
IFS=':' read -r box_user box_group <<< "${box_user_group}"
return 1
fi
}
disable_ipv6() {
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.forwarding=0
sysctl -w net.ipv6.conf.all.accept_ra=0
sysctl -w net.ipv6.conf.wlan0.accept_ra=0
sysctl -w net.ipv6.conf.all.disable_ipv6=1
sysctl -w net.ipv6.conf.default.disable_ipv6=1
sysctl -w net.ipv6.conf.wlan0.disable_ipv6=1
# add: block Askes ipv6 completely
ip -6 rule add unreachable pref "${pref}"
} >/dev/null 2>&1
ipv6_enable() {
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.forwarding=1
sysctl -w net.ipv6.conf.all.accept_ra=2
sysctl -w net.ipv6.conf.wlan0.accept_ra=2
sysctl -w net.ipv6.conf.all.disable_ipv6=0
sysctl -w net.ipv6.conf.default.disable_ipv6=0
sysctl -w net.ipv6.conf.wlan0.disable_ipv6=0
# del: block Askes ipv6 completely
ip -6 rule del unreachable pref "${pref}"
# add: blocks all outgoing IPv6 traffic using the UDP protocol to port 53, effectively preventing DNS queries over IPv6.
$IP6V -A OUTPUT -p udp --destination-port 53 -j DROP
} >/dev/null 2>&1
intranet=(
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.0.0.0/24
192.0.2.0/24
192.88.99.0/24
192.168.0.0/16
198.51.100.0/24
203.0.113.0/24
224.0.0.0/4
240.0.0.0/4
255.0.0.0/4
255.255.255.0/24
255.255.255.255/32
)
# The use of 100.0.0.0/8 instead of 100.64.0.0/10 is purely due to a mistake by China Telecom's service provider, and you can change it back.
intranet+=($(ip -4 a | busybox awk '/inet/ {print $2}' | busybox grep -vE "^127.0.0.1"))
intranet6=(
::/128
::1/128
::ffff:0:0/96
100::/64
64:ff9b::/96
2001::/32
2001:10::/28
2001:20::/28
2001:db8::/32
2002::/16
fc00::/7
fe80::/10
ff00::/8
)
intranet6+=($(ip -6 a | busybox awk '/inet6/ {print $2}' | busybox grep -vE "^fe80|^::1|^fd00"))
probe_tun_device() {
busybox ifconfig | grep -q "${tun_device}" || return 1
}
forward() {
local action=$1
# ${iptables} -t nat ${action} POSTROUTING -o ${tun_device} -j MASQUERADE
${iptables} "${action}" FORWARD -i "${tun_device}" -j ACCEPT
${iptables} "${action}" FORWARD -o "${tun_device}" -j ACCEPT
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.default.rp_filter=2
sysctl -w net.ipv4.conf.all.rp_filter=2
} >/dev/null 2>&1
start_redirect() {
if [ "${iptables}" = "$IPV" ]; then
${iptables} -t nat -N BOX_EXTERNAL
${iptables} -t nat -F BOX_EXTERNAL
${iptables} -t nat -N BOX_LOCAL
${iptables} -t nat -F BOX_LOCAL
fi
if [ "${iptables}" = "$IPV" ]; then
if [ "${bin_name}" = "clash" ]; then
${iptables} -t nat -A BOX_EXTERNAL -p udp --dport 53 -j REDIRECT --to-ports "${clash_dns_port}"
${iptables} -t nat -A BOX_LOCAL -p udp --dport 53 -j REDIRECT --to-ports "${clash_dns_port}"
# else
# Other types of inbound should be added here to receive DNS traffic instead of sniffing
# ${iptables} -t nat -A BOX_EXTERNAL -p udp --dport 53 -j REDIRECT --to-ports "${redir_port}"
# ${iptables} -t nat -A BOX_LOCAL -p udp --dport 53 -j REDIRECT --to-ports "${redir_port}"
fi
# Fix ICMP (ping). This does not guarantee that the ping result is valid. Just that it returns a result
# if [[ "${bin_name}" == @(clash|sing-box) ]]; then
# if [ -n "${fake_ip_range}" ]; then
# ${iptables} -t nat -A BOX_EXTERNAL -d "${fake_ip_range}" -p icmp -j DNAT --to-destination 127.0.0.1
# ${iptables} -t nat -A BOX_LOCAL -d "${fake_ip_range}" -p icmp -j DNAT --to-destination 127.0.0.1
# fi
# fi
${iptables} -t nat -N LOCAL_IP_V4
${iptables} -t nat -F LOCAL_IP_V4
for subnet in ${intranet[@]} ; do
${iptables} -t nat -A BOX_EXTERNAL -d ${subnet} -j RETURN
${iptables} -t nat -A BOX_LOCAL -d ${subnet} -j RETURN
done
${iptables} -t nat -A BOX_EXTERNAL -j LOCAL_IP_V4
${iptables} -t nat -A BOX_LOCAL -j LOCAL_IP_V4
${iptables} -t nat -A BOX_EXTERNAL -p tcp -i lo -j REDIRECT --to-ports "${redir_port}"
if [ "${ap_list}" != "" ]; then
for ap in "${ap_list[@]}"; do
${iptables} -t nat -A BOX_EXTERNAL -p tcp -i "${ap}" -j REDIRECT --to-ports "${redir_port}"
done
[ ${network_mode} = "enhance" ] || log Info "${ap_list[*]} transparent proxy."
fi
${iptables} -t nat -I PREROUTING -j BOX_EXTERNAL
${iptables} -t nat -I BOX_LOCAL -m owner --uid-owner "${box_user}" --gid-owner "${box_group}" -j RETURN
if [ "${ignore_out_list}" != "" ]; then
for ignore in "${ignore_out_list[@]}"; do
${iptables} -t nat -A BOX_LOCAL -o "${ignore}" -j RETURN
done
[ ${network_mode} = "enhance" ] || log Info "${ignore_out_list[*]} ignore transparent proxy."
fi
fi
if [ "${iptables}" = "$IPV" ]; then
case "${proxy_mode}" in
blacklist|black)
if [ -z "$(cat "${uid_list[@]}")" ] ; then
${iptables} -t nat -A BOX_LOCAL -p tcp -j REDIRECT --to-ports "${redir_port}"
[ ${network_mode} = "enhance" ] || log Info "Transparent proxy for all apps."
else
while read -r appid; do
${iptables} -t nat -A BOX_LOCAL -m owner --uid-owner "${appid}" -j RETURN
done < "${uid_list[@]}"
${iptables} -t nat -A BOX_LOCAL -p tcp -j REDIRECT --to-ports "${redir_port}"
[ ${network_mode} = "enhance" ] || log Info "proxy mode: ${proxy_mode} (${packages_list[*]}) no transparent proxy."
fi
if [ "${gid_list}" != "" ] ; then
for gid in ${gid_list[@]} ; do
${iptables} -t nat -A BOX_LOCAL -m owner --gid-owner ${gid} -j RETURN
done
[ ${network_mode} = "enhance" ] || {
[ "${iptables}" = "$IPV" ] && log Info "proxy mode: ${proxy_mode}, GID (${gid_list[*]}) no transparent proxy."
}
fi
;;
whitelist|white)
if [ -z "$(cat "${uid_list[@]}")" ] ; then
${iptables} -t nat -A BOX_LOCAL -p tcp -j REDIRECT --to-ports "${redir_port}"
[ ${network_mode} = "enhance" ] || log Info "Transparent proxy for all apps."
else
while read -r appid; do
${iptables} -t nat -A BOX_LOCAL -p tcp -m owner --uid-owner "${appid}" -j REDIRECT --to-ports "${redir_port}"
done < "${uid_list[@]}"
${iptables} -t nat -A BOX_LOCAL -p tcp -m owner --uid-owner 0 -j REDIRECT --to-ports "${redir_port}"
${iptables} -t nat -A BOX_LOCAL -p tcp -m owner --uid-owner 1052 -j REDIRECT --to-ports "${redir_port}"
[ ${network_mode} = "enhance" ] || log Info "proxy mode: ${proxy_mode} (${packages_list[*]}) transparent proxy."
fi
if [ "${gid_list}" != "" ] ; then
for gid in ${gid_list[@]} ; do
${iptables} -t nat -A BOX_LOCAL -p tcp -m owner --gid-owner ${gid} -j REDIRECT --to-ports ${redir_port}
done
[ ${network_mode} = "enhance" ] || [ "${iptables}" = "$IPV" ] && log Info "proxy mode: ${proxy_mode}, GID (${gid_list[*]}) transparent proxy."
fi
;;
*)
log Warning "proxy mode: ${proxy_mode} < error."
${iptables} -t nat -A BOX_LOCAL -p tcp -j REDIRECT --to-ports "${redir_port}"
[ ${network_mode} = "enhance" ] || log Info "Transparent proxy for all apps."
;;
esac
fi
if [ "${iptables}" = "$IPV" ]; then
${iptables} -t nat -I OUTPUT -j BOX_LOCAL
fi
if [ "${iptables}" = "$IPV" ]; then
${iptables} -A OUTPUT -d 127.0.0.1 -p tcp -m owner --uid-owner "${box_user}" --gid-owner "${box_group}" -m tcp --dport "${redir_port}" -j REJECT
else
${iptables} -A OUTPUT -d ::1 -p tcp -m owner --uid-owner "${box_user}" --gid-owner "${box_group}" -m tcp --dport "${redir_port}" -j REJECT
fi
}
stop_redirect() {
if [ "${iptables}" = "$IPV" ]; then
${iptables} -t nat -D PREROUTING -j BOX_EXTERNAL
${iptables} -t nat -D OUTPUT -j BOX_LOCAL
fi
if [ "${iptables}" = "$IPV" ]; then
${iptables} -D OUTPUT -d 127.0.0.1 -p tcp -m owner --uid-owner "${box_user}" --gid-owner "${box_group}" -m tcp --dport "${redir_port}" -j REJECT
${iptables} -D OUTPUT -d 127.0.0.1 -p tcp -m owner --uid-owner 0:3005 -m tcp --dport "${redir_port}" -j REJECT
else
${iptables} -D OUTPUT -d ::1 -p tcp -m owner --uid-owner "${box_user}" --gid-owner "${box_group}" -m tcp --dport "${redir_port}" -j REJECT
${iptables} -D OUTPUT -d ::1 -p tcp -m owner --uid-owner 0:3005 -m tcp --dport "${redir_port}" -j REJECT
fi
if [ "${iptables}" = "$IPV" ]; then
# ${iptables} -t nat -D BOX_EXTERNAL -d "${fake_ip_range}" -p icmp -j DNAT --to-destination 127.0.0.1
# ${iptables} -t nat -D BOX_LOCAL -d "${fake_ip_range}" -p icmp -j DNAT --to-destination 127.0.0.1
${iptables} -t nat -F BOX_EXTERNAL
${iptables} -t nat -X BOX_EXTERNAL
${iptables} -t nat -F BOX_LOCAL
${iptables} -t nat -X BOX_LOCAL
${iptables} -t nat -F LOCAL_IP_V4
${iptables} -t nat -X LOCAL_IP_V4
fi
}
start_tproxy() {
if [ "${iptables}" = "$IPV" ]; then
ip rule add fwmark "${fwmark}" table "${table}" pref "${pref}"
ip route add local default dev lo table "${table}"
else
ip -6 rule add fwmark "${fwmark}" table "${table}" pref "${pref}"
ip -6 route add local default dev lo table "${table}"
fi
${iptables} -t mangle -N BOX_EXTERNAL >/dev/null 2>&1
${iptables} -t mangle -F BOX_EXTERNAL >/dev/null 2>&1
# ${iptables} -t mangle -A BOX_EXTERNAL -m mark --mark ${routing_mark} -j RETURN
# Bypass other if, notice: Some interface is named with r_ / oem / nm_ / qcom_, it might need more complicated solution.
# ${iptables} -t mangle -I BOX_EXTERNAL -i rmnet_data+ -j RETURN
# ${iptables} -t mangle -I BOX_EXTERNAL -i ccmni+ -j RETURN
if [ "${clash_dns_forward}" = "enable" ] && [[ "${bin_name}" == @(clash|hysteria) ]] ; then
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_EXTERNAL -p tcp --dport 53 -j RETURN
${iptables} -t mangle -A BOX_EXTERNAL -p udp --dport 53 -j RETURN
else
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_EXTERNAL -p tcp --dport 53 -j TPROXY --on-port ${tproxy_port} --tproxy-mark ${fwmark}
${iptables} -t mangle -A BOX_EXTERNAL -p udp --dport 53 -j TPROXY --on-port ${tproxy_port} --tproxy-mark ${fwmark}
fi
# Skip traffic already handled by TProxy
# If the interface of the default route has a public IPv4 or IPv6 address assigned by the ISP, omitting these rules will result in abnormal proxy behavior for local traffic'
# [ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_EXTERNAL -p tcp -m socket --transparent -j MARK --set-xmark ${fwmark}
# ${iptables} -t mangle -A BOX_EXTERNAL -p udp -m socket --transparent -j MARK --set-xmark ${fwmark}
# ${iptables} -t mangle -A BOX_EXTERNAL -m socket -j RETURN
# Bypass intranet, run `su -c 'zcat /proc/config.gz | grep -i addrtype'` to check compatibility
# ${iptables} -t mangle -A BOX_EXTERNAL -m addrtype --dst-type LOCAL -j RETURN
if [ "${iptables}" = "$IPV" ]; then
for subnet in ${intranet[@]} ; do
${iptables} -t mangle -A BOX_EXTERNAL -d ${subnet} -j RETURN
done
${iptables} -t mangle -N LOCAL_IP_V4
${iptables} -t mangle -F LOCAL_IP_V4
${iptables} -t mangle -A BOX_EXTERNAL -j LOCAL_IP_V4
else
for subnet6 in ${intranet6[@]} ; do
${iptables} -t mangle -A BOX_EXTERNAL -d ${subnet6} -j RETURN
done
${iptables} -t mangle -N LOCAL_IP_V6
${iptables} -t mangle -F LOCAL_IP_V6
${iptables} -t mangle -A BOX_EXTERNAL -j LOCAL_IP_V6
fi
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_EXTERNAL -p tcp -i lo -j TPROXY --on-port "${tproxy_port}" --tproxy-mark "${fwmark}"
${iptables} -t mangle -A BOX_EXTERNAL -p udp -i lo -j TPROXY --on-port "${tproxy_port}" --tproxy-mark "${fwmark}"
# Allow ap interface, Notice: Old android device may only have one wlan interface. Some new android device have multiple wlan interface like wlan0(for internet), wlan1(for AP), loop through the access point list
if [ "${ap_list}" != "" ]; then
for ap in ${ap_list[@]} ; do
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_EXTERNAL -p tcp -i "${ap}" -j TPROXY --on-port "${tproxy_port}" --tproxy-mark "${fwmark}"
${iptables} -t mangle -A BOX_EXTERNAL -p udp -i "${ap}" -j TPROXY --on-port "${tproxy_port}" --tproxy-mark "${fwmark}"
done
[ "${iptables}" = "$IPV" ] && log Info "${ap_list[*]} transparent proxy."
fi
${iptables} -t mangle -I PREROUTING -j BOX_EXTERNAL
${iptables} -t mangle -N BOX_LOCAL
${iptables} -t mangle -F BOX_LOCAL
${iptables} -t mangle -A BOX_LOCAL -m owner --uid-owner ${box_user} --gid-owner ${box_group} -j RETURN
# ${iptables} -t mangle -A BOX_LOCAL -m mark --mark ${routing_mark} -j RETURN
if [ "${ignore_out_list}" != "" ]; then
for ignore in ${ignore_out_list[@]} ; do
${iptables} -t mangle -A BOX_LOCAL -o "${ignore}" -j RETURN
done
[ "${iptables}" = "$IPV" ] && log Info "${ignore_out_list[*]} ignore transparent proxy."
fi
if [ "${clash_dns_forward}" = "enable" ] && [[ "${bin_name}" == @(clash|hysteria) ]] ; then
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_LOCAL -p tcp --dport 53 -j RETURN
${iptables} -t mangle -A BOX_LOCAL -p udp --dport 53 -j RETURN
else
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_LOCAL -p tcp --dport 53 -j MARK --set-xmark ${fwmark}
${iptables} -t mangle -A BOX_LOCAL -p udp --dport 53 -j MARK --set-xmark ${fwmark}
fi
if [ "${iptables}" = "$IPV" ]; then
for subnet in ${intranet[@]} ; do
${iptables} -t mangle -A BOX_LOCAL -d ${subnet} -j RETURN
done
${iptables} -t mangle -A BOX_LOCAL -j LOCAL_IP_V4
else
for subnet6 in ${intranet6[@]} ; do
${iptables} -t mangle -A BOX_LOCAL -d ${subnet6} -j RETURN
done
${iptables} -t mangle -A BOX_LOCAL -j LOCAL_IP_V6
fi
case "${proxy_mode}" in
blacklist|black)
if [ -z "$(cat "${uid_list[@]}")" ] ; then
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_LOCAL -p tcp -j MARK --set-xmark "${fwmark}"
${iptables} -t mangle -A BOX_LOCAL -p udp -j MARK --set-xmark "${fwmark}"
[ "${iptables}" = "$IPV" ] && log Info "transparent proxy for all apps."
else
while read -r appid; do
${iptables} -t mangle -A BOX_LOCAL -m owner --uid-owner "${appid}" -j RETURN
done < "${uid_list[@]}"
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_LOCAL -p tcp -j MARK --set-xmark "${fwmark}"
${iptables} -t mangle -A BOX_LOCAL -p udp -j MARK --set-xmark "${fwmark}"
[ "${iptables}" = "$IPV" ] && log Info "proxy mode: ${proxy_mode} (${packages_list[*]}) no transparent proxy."
fi
if [ "${gid_list}" != "" ] ; then
for gid in ${gid_list[@]} ; do
${iptables} -t mangle -A BOX_LOCAL -m owner --gid-owner ${gid} -j RETURN
done
[ "${iptables}" = "$IPV" ] && log Info "proxy mode: ${proxy_mode}, GID (${gid_list[*]}) no transparent proxy."
fi
;;
whitelist|white)
if [ -z "$(cat "${uid_list[@]}")" ] ; then
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_LOCAL -p tcp -j MARK --set-xmark "${fwmark}"
${iptables} -t mangle -A BOX_LOCAL -p udp -j MARK --set-xmark "${fwmark}"
[ "${iptables}" = "$IPV" ] && log Info "transparent proxy for all apps."
else
while read -r appid; do
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_LOCAL -p tcp -m owner --uid-owner "${appid}" -j MARK --set-xmark "${fwmark}"
${iptables} -t mangle -A BOX_LOCAL -p udp -m owner --uid-owner "${appid}" -j MARK --set-xmark "${fwmark}"
done < "${uid_list[@]}"
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_LOCAL -p tcp -m owner --uid-owner 0 -j MARK --set-xmark "${fwmark}"
${iptables} -t mangle -A BOX_LOCAL -p udp -m owner --uid-owner 0 -j MARK --set-xmark "${fwmark}"
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_LOCAL -p tcp -m owner --uid-owner 1052 -j MARK --set-xmark "${fwmark}"
${iptables} -t mangle -A BOX_LOCAL -p udp -m owner --uid-owner 1052 -j MARK --set-xmark "${fwmark}"
[ "${iptables}" = "$IPV" ] && log Info "proxy mode: ${proxy_mode} (${packages_list[*]}) transparent proxy."
fi
if [ "${gid_list}" != "" ] ; then
for gid in ${gid_list[@]} ; do
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_LOCAL -p tcp -m owner --gid-owner ${gid} -j MARK --set-xmark "${fwmark}"
${iptables} -t mangle -A BOX_LOCAL -p udp -m owner --gid-owner ${gid} -j MARK --set-xmark "${fwmark}"
done
[ "${iptables}" = "$IPV" ] && log Info "proxy mode: ${proxy_mode}, GID (${gid_list[*]}) transparent proxy."
fi
;;
*)
log Debug "proxy mode: ${proxy_mode} < error"
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -A BOX_LOCAL -p tcp -j MARK --set-xmark "${fwmark}"
${iptables} -t mangle -A BOX_LOCAL -p udp -j MARK --set-xmark "${fwmark}"
[ "${iptables}" = "$IPV" ] && log Info "transparent proxy for all apps."
;;
esac
${iptables} -t mangle -I OUTPUT -j BOX_LOCAL
${iptables} -t mangle -N DIVERT
${iptables} -t mangle -F DIVERT
${iptables} -t mangle -A DIVERT -j MARK --set-xmark "${fwmark}"
${iptables} -t mangle -A DIVERT -j ACCEPT
[ ${network_mode} = "enhance" ] || ${iptables} -t mangle -I PREROUTING -p tcp -m socket -j DIVERT
# Disable QUIC
if [ "${quic}" = "disable" ]; then
${iptables} -A OUTPUT -p udp --dport 443 -j REJECT
${iptables} -A OUTPUT -p udp --dport 80 -j REJECT
# ${iptables} -A OUTPUT -p udp -m multiport --dport 443,80 -j REJECT
[ "${iptables}" = "$IPV" ] && log Warning "Disabling QUIC"
fi
if [ ${network_mode} != "enhance" ]; then
# This rule blocks local access to tproxy-port to prevent traffic loopback.
if [ "${iptables}" = "$IPV" ]; then
${iptables} -A OUTPUT -d 127.0.0.1 -p tcp -m owner --uid-owner "${box_user}" --gid-owner "${box_group}" -m tcp --dport "${tproxy_port}" -j REJECT
else
${iptables} -A OUTPUT -d ::1 -p tcp -m owner --uid-owner "${box_user}" --gid-owner "${box_group}" -m tcp --dport "${tproxy_port}" -j REJECT
fi
fi
if [ "${iptables}" = "$IPV" ]; then
if [ "${clash_dns_forward}" = "enable" ] && [ "${bin_name}" = "clash" ]; then
${iptables} -t nat -N CLASH_DNS_EXTERNAL
${iptables} -t nat -F CLASH_DNS_EXTERNAL
${iptables} -t nat -A CLASH_DNS_EXTERNAL -p udp --dport 53 -j REDIRECT --to-ports "${clash_dns_port}"
${iptables} -t nat -I PREROUTING -j CLASH_DNS_EXTERNAL
${iptables} -t nat -N CLASH_DNS_LOCAL
${iptables} -t nat -F CLASH_DNS_LOCAL
${iptables} -t nat -A CLASH_DNS_LOCAL -m owner --uid-owner "${box_user}" --gid-owner "${box_group}" -j RETURN
${iptables} -t nat -A CLASH_DNS_LOCAL -p udp --dport 53 -j REDIRECT --to-ports "${clash_dns_port}"
${iptables} -t nat -I OUTPUT -j CLASH_DNS_LOCAL
fi
# Fix ICMP (ping), this does not guarantee that the ping result is valid (proxies such as clash do not support forwarding ICMP),just that it returns a result, "--to-destination" can be set to a reachable address.
if [[ "${bin_name}" == @(clash|sing-box) ]]; then
if [ -n "${fake_ip_range}" ]; then
${iptables} -t nat -I OUTPUT -d "${fake_ip_range}" -p icmp -j DNAT --to-destination 127.0.0.1
${iptables} -t nat -I PREROUTING -d "${fake_ip_range}" -p icmp -j DNAT --to-destination 127.0.0.1
fi
fi
fi
}
stop_tproxy() {
if [ "${iptables}" = "$IPV" ]; then
ip rule del fwmark "${fwmark}" table "${table}" pref "${pref}"
ip route del local default dev lo table "${table}"
ip route flush table "${table}"
ip rule del pref "${pref}"
else
ip -6 rule del fwmark "${fwmark}" table "${table}" pref "${pref}"
ip -6 route del local default dev lo table "${table}"
ip -6 route flush table "${table}"
ip -6 rule del pref "${pref}"
fi
${iptables} -t mangle -D PREROUTING -j BOX_EXTERNAL
${iptables} -t mangle -D PREROUTING -p tcp -m socket -j DIVERT
${iptables} -t mangle -D OUTPUT -j BOX_LOCAL
${iptables} -t mangle -D BOX_EXTERNAL -i rmnet_data+ -j RETURN
${iptables} -t mangle -D BOX_EXTERNAL -i ccmni+ -j RETURN
${iptables} -t mangle -F BOX_EXTERNAL
${iptables} -t mangle -X BOX_EXTERNAL
${iptables} -t mangle -F BOX_LOCAL
${iptables} -t mangle -X BOX_LOCAL
${IPV} -t mangle -F LOCAL_IP_V4
${IPV} -t mangle -X LOCAL_IP_V4
${IP6V} -t mangle -F LOCAL_IP_V6
${IP6V} -t mangle -X LOCAL_IP_V6
${iptables} -t mangle -F DIVERT
${iptables} -t mangle -X DIVERT
# flush QUIC
${iptables} -D OUTPUT -p udp -m multiport --dport 443,80 -j REJECT
${iptables} -D OUTPUT -p udp --dport 443 -j REJECT
${iptables} -D OUTPUT -p udp --dport 80 -j REJECT
if [ "${iptables}" = "$IPV" ]; then
${iptables} -D OUTPUT -d 127.0.0.1 -p tcp -m owner --uid-owner "${box_user}" --gid-owner "${box_group}" -m tcp --dport "${tproxy_port}" -j REJECT
${iptables} -D OUTPUT -d 127.0.0.1 -p tcp -m owner --uid-owner 0 --gid-owner 3005 -m tcp --dport "${tproxy_port}" -j REJECT
else
${iptables} -D OUTPUT -d ::1 -p tcp -m owner --uid-owner "${box_user}" --gid-owner "${box_group}" -m tcp --dport "${tproxy_port}" -j REJECT
${iptables} -D OUTPUT -d ::1 -p tcp -m owner --uid-owner 0 --gid-owner 3005 -m tcp --dport "${tproxy_port}" -j REJECT
fi
if [ "${iptables}" = "$IPV" ]; then
${iptables} -t nat -D PREROUTING -j CLASH_DNS_EXTERNAL
${iptables} -t nat -D OUTPUT -j CLASH_DNS_LOCAL
${iptables} -t nat -F CLASH_DNS_EXTERNAL
${iptables} -t nat -X CLASH_DNS_EXTERNAL
${iptables} -t nat -F CLASH_DNS_LOCAL
${iptables} -t nat -X CLASH_DNS_LOCAL
if [ -n "${fake_ip_range}" ]; then
${iptables} -t nat -D OUTPUT -p icmp -d "${fake_ip_range}" -j DNAT --to-destination 127.0.0.1
${iptables} -t nat -D PREROUTING -p icmp -d "${fake_ip_range}" -j DNAT --to-destination 127.0.0.1
${iptables} -t nat -D OUTPUT -d "${fake_ip_range}" -p icmp -j DNAT --to-destination 127.0.0.1
${iptables} -t nat -D PREROUTING -d "${fake_ip_range}" -p icmp -j DNAT --to-destination 127.0.0.1
fi
fi
}
cleanup_iptables() {
for iptables in "$IPV" "$IP6V"; do
iptables="${iptables}" && {
stop_redirect
stop_tproxy
forward -D
} >/dev/null 2>&1
if [ "${iptables}" = "$IP6V" ]; then
${iptables} -D OUTPUT -p udp --destination-port 53 -j DROP >/dev/null 2>&1
fi
done
}
if [[ "${network_mode}" == @(redirect|mixed|tproxy|enhance) ]]; then
case "$1" in
enable|renew)
box_etc
log Info "$IPV + $IP6V"
probe_user_group || {
log Error "Failed to check BOX user group. Please ensure ${bin_name} kernel is started."
}
find_packages_uid
cleanup_iptables
[ $1 = "renew" ] && log Warning "cleaning up iptables transparent proxy rules."
case "${network_mode}" in
tproxy)
log Info "Using Tproxy: tcp + udp."
log Info "Creating iptables transparent proxy rules."
iptables="$IPV"
if start_tproxy; then
log Info "Creating iptables transparent proxy rules done."
else
log Error "Creating iptables transparent proxy rules failed."
stop_tproxy >/dev/null 2>&1
fi
if [ "${ipv6}" = "true" ]; then
log Debug "Using IPv6."
ipv6_enable
iptables="$IP6V"
if start_tproxy; then
log Info "Creating ip6tables transparent proxy rules done."
else
log Error "Creating ip6tables transparent proxy rules failed."
stop_tproxy >/dev/null 2>&1
fi
else
disable_ipv6
log Warning "Disabling IPv6."
fi
;;
redirect)
log Info "Using Redirect: tcp + udp (direct)."
log Info "Creating iptables transparent proxy rules."
iptables="$IPV"
if start_redirect; then
log Info "Creating iptables transparent proxy rules done."
else
log Error "Creating iptables transparent proxy rule failed."
stop_redirect >/dev/null 2>&1
fi
if [ "${ipv6}" = "true" ]; then
log Debug "Using IPv6."
ipv6_enable
iptables="$IP6V"
if start_redirect; then
log Info "Creating ip6tables transparent proxy rules done."
else
log Error "Creating ip6tables transparent proxy rule failed."
stop_redirect >/dev/null 2>&1
fi
else
disable_ipv6
log Warning "Disabling IPv6."
fi
;;
mixed)
log Info "Using Mixed: tcp(redirect) + udp(tun)."
log Info "Creating iptables transparent proxy rules."
iptables="$IPV"
probe_tun_device || log Error "tun device: (${tun_device}) not found"
forward -I || forward -D >/dev/null 2>&1
if start_redirect; then
log Info "Creating iptables transparent proxy rules done."
else
log Error "Creating iptables transparent proxy rule failed."
stop_redirect >/dev/null 2>&1
fi
if [ "${ipv6}" = "true" ]; then
log Debug "Using IPv6."
ipv6_enable
iptables="$IP6V"
forward -I || forward -D >/dev/null 2>&1
if start_redirect; then
log Info "Creating ip6tables transparent proxy rules done."
else
log Error "Creating ip6tables transparent proxy rule failed."
stop_redirect >/dev/null 2>&1
fi
else
disable_ipv6
log Warning "Disabling IPv6."
fi
;;
enhance)
log Info "Using Enhance: tcp(redirect) + udp(tproxy)"
log Info "Creating iptables transparent proxy rules."
iptables="$IPV"
if start_redirect && start_tproxy; then
log Info "Creating iptables transparent proxy rules done."
else
log Error "Creating iptables transparent proxy rule failed."
stop_redirect >/dev/null 2>&1
fi
if [ "${ipv6}" = "true" ]; then
log Debug "Using IPv6."
ipv6_enable
iptables="$IP6V"
if start_redirect && start_tproxy; then
log Info "Creating ip6tables transparent proxy rules done."
else
log Error "Creating ip6tables transparent proxy rule failed."
stop_redirect >/dev/null 2>&1
fi
else
disable_ipv6
log Warning "Disabling IPv6."
fi
;;
*)
log Error "network_mode: ${network_mode}, unknown"
exit 1
;;
esac
[ $1 = "renew" ] && log Debug "restart iptables transparent proxy rules done."
bin_alive && log Info "${bin_name} connected."
"${scripts_dir}/box.stealth" enable >/dev/null 2>&1
;;
disable)
ipv6_enable
"${scripts_dir}/box.stealth" disable >/dev/null 2>&1
probe_user_group || log Error "Failed to check BOX user group. Please ensure ${bin_name} kernel is started."
log Warning "Cleaning up iptables transparent proxy rules."
cleanup_iptables
log Warning "Cleaning up iptables transparent proxy rules done."
;;
help|-h|--help|"")
echo "Usage: $0 {enable|disable|renew}"
echo
echo "Commands:"
echo " enable - Enable iptables rules"
echo " disable - Disable iptables rules"
echo " renew - Reapply or refresh iptables rules"
echo
echo "Example:"
echo " $0 enable"
;;
*)
echo "$0: '$1' not found"
echo "Run '$0 help' for usage."
;;
esac
else
case "$1" in
enable|renew)
box_etc
log Info "$IPV + $IP6V"
log Info "Using Tun: tcp + udp."
probe_user_group || {
log Error "Failed to check BOX user group. Please ensure ${bin_name} kernel is started."
}
cleanup_iptables
probe_tun_device || log Error "tun device: (${tun_device}) not found"
[ $1 = "renew" ] && log Warning "Cleaning up tun rules."
iptables="$IPV"
[ -n "${packages_list}" ] && log Debug "proxy mode: $proxy_mode (${packages_list[*]})"
if forward -I; then
log Info "Create iptables tun rules done."
else
log Error "Create iptables tun rules failed."
forward -D >/dev/null 2>&1
fi
if [ "${ipv6}" = "true" ]; then
log Debug "Using IPv6."
ipv6_enable
iptables="$IP6V"
if forward -I; then
log Info "Create ip6tables tun rules done."
else
log Error "Create ip6tables tun rules failed."
forward -D >/dev/null 2>&1
fi
else
disable_ipv6
log Warning "Disable IPv6."
fi
[ $1 = "renew" ] && log Info "Restart iptables tun rules done."
bin_alive && log Info "${bin_name} connected."
;;
disable)
ipv6_enable
probe_user_group || log Error "Failed to check BOX user group. Please ensure ${bin_name} kernel is started."
log Warning "Cleaning up tun rules."
cleanup_iptables
log Warning "Cleaning up tun rules done."
;;
help|-h|--help|"")
echo "Usage: $0 {enable|disable|renew}"
echo
echo "Commands:"
echo " enable - Enable iptables rules"
echo " disable - Disable iptables rules"
echo " renew - Reapply or refresh iptables rules"
echo
echo "Example:"
echo " $0 enable"
;;
*)
echo "$0: '$1' not found"
echo "Run '$0 help' for usage."
;;
esac
fi