Skip to content

Instantly share code, notes, and snippets.

@Bryan2333
Last active April 20, 2025 13:14
Show Gist options
  • Save Bryan2333/2b15adb8859440358a6e07edc14bd987 to your computer and use it in GitHub Desktop.
Save Bryan2333/2b15adb8859440358a6e07edc14bd987 to your computer and use it in GitHub Desktop.
tproxy脚本
#!/bin/bash
if [[ "$EUID" -ne 0 ]]
then
echo "This script must be run as root!"
exit 1
fi
SCRIPT_NAME="$(basename "$0")"
## 网卡
INTERFACE=$(ip route show default | awk '/default/ {print $5}')
INTERFACE="${INTERFACE:-wlan0}"
## TProxy流量标记
TPROXY_MARK="0x1"
## TProxy路由表ID
TPROXY_ROUTE_TABLE="200"
## 绕开的用户
BYPASS_USERS=('sing-box' 'naiveproxy')
## sing-box的透明代理端口
TPROXY_PORT="7894"
## 需要代理的协议类型
TPROXY_L4PROTO="{tcp, udp}"
function wait_online() {
until ping -c 1 baidu.com &> /dev/null
do
sleep 1
done
}
function start_singbox() {
systemctl is-active --quiet sing-box || systemctl start sing-box
}
function clear_firewall_rules() {
{
ip rule del fwmark "$TPROXY_MARK" table "$TPROXY_ROUTE_TABLE"
ip route del local default dev "$INTERFACE" table "$TPROXY_ROUTE_TABLE"
ip -6 rule del fwmark "$TPROXY_MARK" table "$TPROXY_ROUTE_TABLE"
ip -6 route del local default dev "$INTERFACE" table "$TPROXY_ROUTE_TABLE"
nft delete table inet singbox
} > /dev/null 2>&1
}
function wait_for_ipv6_async() {
(
local retries=5
while ((retries-- > 0))
do
mapfile -t PUBLIC_IPv6 < <(ip addr | awk '/inet6 / {print $2}' | cut -d '/' -f1 | grep -vE '^::1|^fe80::')
if (( ${#PUBLIC_IPv6[@]} > 0 ))
then
for ip in "${PUBLIC_IPv6[@]}"
do
nft add rule inet singbox tp_rule ip6 daddr \["$ip"\] meta l4proto "$TPROXY_L4PROTO" th dport != 53 accept comment \"绕开本机 IPv6 公网地址\"
done
break
fi
sleep 0.5
done
) &
}
function set_firewall_rules() {
{
ip rule add fwmark "$TPROXY_MARK" lookup "$TPROXY_ROUTE_TABLE"
ip route add local default dev "$INTERFACE" table "$TPROXY_ROUTE_TABLE"
ip -6 rule add fwmark "$TPROXY_MARK" lookup "$TPROXY_ROUTE_TABLE"
ip -6 route add local default dev "$INTERFACE" table "$TPROXY_ROUTE_TABLE"
} > /dev/null 2>&1
nft -f - <<EOF
table inet singbox {
## 保留IPv4地址
set BYPASS_IPv4 {
type ipv4_addr
flags interval
auto-merge
elements = {
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,
255.255.255.255
}
}
## 保留IPv6地址
set BYPASS_IPv6 {
type ipv6_addr
flags interval
auto-merge
elements = {
::/128,
::1/128,
64:ff9b::/96,
100::/64,
2001::/32,
2001:20::/28,
fe80::/10,
ff00::/8
}
}
chain tp_rule {
ip daddr @BYPASS_IPv4 meta l4proto $TPROXY_L4PROTO th dport != 53 accept comment "绕开私有IP"
ip6 daddr @BYPASS_IPv6 meta l4proto $TPROXY_L4PROTO th dport != 53 accept comment "绕开私有IP"
udp dport 123 accept comment "绕开NTP流量"
udp dport { netbios-ns, netbios-dgm, netbios-ssn } accept comment "绕开NBNS流量"
udp dport mdns accept comment "绕开mdns流量"
udp dport { 443, 8443 } drop comment "屏蔽Quic"
}
chain tp_pre {
type filter hook prerouting priority filter; policy accept;
fib daddr type local meta l4proto $TPROXY_L4PROTO th dport $TPROXY_PORT reject with icmpx type host-unreachable comment "直接访问tproxy端口拒绝, 防止回环"
jump tp_rule
meta l4proto $TPROXY_L4PROTO socket transparent 1 mark set $TPROXY_MARK
socket transparent 0 socket wildcard 0 return comment "跳过已经由TProxy接管的流量"
meta l4proto $TPROXY_L4PROTO meta mark set $TPROXY_MARK tproxy ip to 127.0.0.1:$TPROXY_PORT accept comment "转发给sing-box"
meta l4proto $TPROXY_L4PROTO meta mark set $TPROXY_MARK tproxy ip6 to [::1]:$TPROXY_PORT accept comment "转发给sing-box"
}
chain tp_out {
type route hook output priority filter; policy accept;
meta skuid {$(id -u "${BYPASS_USERS[@]}" | paste -sd ',')} accept comment "绕开naive和sing-box发出的连接"
jump tp_rule
meta l4proto $TPROXY_L4PROTO meta mark set $TPROXY_MARK accept comment "重路由到prerouting"
}
}
EOF
wait_for_ipv6_async
}
function start_tproxy() {
clear_firewall_rules
wait_online
start_singbox
set_firewall_rules
}
function stop_tproxy() {
clear_firewall_rules
}
function main() {
case "$1" in
"start"|"restart")
start_tproxy
;;
"stop")
stop_tproxy
;;
*)
echo "Usage: singbox-tproxy [start|restart|stop]"
exit 1
;;
esac
}
if [[ "$SCRIPT_NAME" == "singbox-tproxy" ]]
then
main "$@"
elif [[ "$SCRIPT_NAME" == "tproxy-dispatcher" ]]
then
case "$2" in
"up") main start ;;
"down") main stop ;;
esac
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment