v2ray使用tproxy代理udp流量
openwrt下的v2ray透明代理有redirect和tproxy两种模式,前者只能代理tcp流量,但是速度快,路由开销小,后者可以代理tcp和udp流量,但是速度慢,路由开销较高。而且redirect 会修改目标 IP,对于一些应用或 UDP 协议(比如游戏、VoIP、某些 P2P)可能无法正确工作。tproxy 能保持原始目标地址,适合复杂的分流场景,比如:UDP 透明代理、多网卡/策略路由。
如果应用场景只是 HTTP/HTTPS TCP 流量,redirect 性能最好;如果有大量 UDP 或需要透明保留原始目标 IP,tproxy 更可靠,但消耗略高。
注意:用tproxy模式会导致bt等p2p走vps流量,可能会收到版权警告,别问我怎么知道的~~
下面是相关配置:
1.修改/etc/config/v2ray.json
{
"inbounds": [
{
"protocol": "dokodemo-door",
"port": 1060,
"listen":"0.0.0.0",
"sniffing": {
"enabled": false,
"destOverride": ["http", "tls"]
},
"settings": {
"network": "tcp,udp",
"followRedirect": true
},
"streamSettings": {
"sockopt": {
"tproxy": "tproxy",
"mark":255
}
}
},
{
"protocol": "dokodemo-door",
"listen":"0.0.0.0",
"port": 5354,
"settings": {
"address": "8.8.8.8",
"port": 53,
"network": "udp",
"timeout": 0,
"followRedirect": false
},
"streamSettings": {
"sockopt": {
"mark": 255
}
}
},
{
"protocol": "dokodemo-door",
"listen":"0.0.0.0",
"port": 5356,
"settings": {
"address": "8.8.8.8",
"port": 53,
"network": "tcp",
"timeout": 0,
"followRedirect": false
},
"streamSettings": {
"sockopt": {
"mark": 255
}
}
},
{
"protocol": "dokodemo-door",
"listen":"0.0.0.0",
"port": 5358,
"settings": {
"address": "1.1.1.1",
"port": 53,
"network": "udp",
"timeout": 0,
"followRedirect": false
},
"streamSettings": {
"sockopt": {
"mark": 255
}
}
},
{
"protocol": "dokodemo-door",
"listen":"0.0.0.0",
"port": 5360,
"settings": {
"address": "8.8.4.4",
"port": 53,
"network": "udp",
"timeout": 0,
"followRedirect": false
},
"streamSettings": {
"sockopt": {
"mark": 255
}
}
}
],
"outbounds": [
{
"protocol": "vless",
"tag": "proxy",
"settings": {
"vnext": [
{
"address": "xxx.com",
"port": 443,
"users": [
{
"id": "*************hidden uuid***************",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"wsSettings": {
"path": "/mypath"
},
"tcpSettings": {
"allowInsecureCiphers": false
},
"sockopt": {
"mark": 255
}
}
}
]
}
2.修改/etc/init.d/v2ray
#!/bin/sh /etc/rc.common
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
# To use this file, install chinadns-ng,v2ray,knot-dig first
#
#
START=90
USE_PROCD=1
DOMAIN="xxx.com"
DEFAULT_DNS_SERVER="223.6.6.6" #需要是支持dot的dns服务器
DOMESTIC_DNS_SERVERS="119.29.29.29 223.5.5.5 180.76.76.76"
LOCAL_IP="127.0.0.1"
LAN_IPS="10.0.0.1/16"
RESERVED_IPS="0.0.0.0/8 10.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/32" #排除了127.0.0.0/8
RESERVED_IP6S="::1/128 ::/128 fc00::/7 fe80::/10 ff00::/8 ::ffff:0:0/96 2001:db8::/32 2002::/16 64:ff9b::/96 64:ff9b:1::/48"
CHINADNSNG_PORT="5353"
CHINADNSNG_FILES_PATH="/etc/chinadns-ng/"
# 本配置文件中NFT默认参数为:
# family:inet
# table:global
# set:chnroute,chnroute6,gfwip,gfwip6
# 上述值需与chinadnsng配置文件中的一致,否则无法生效:
# add-taggfw-ip inet@global@gfwip,inet@global@gfwip6
# ipset-name4 inet@global@chnroute
# ipset-name6 inet@global@chnroute6
# 且下列NFTSET文件中相关 family,table,set名称要也与chinadnsng配置文件中的一致,否则无法生效
CHNROUTE_NFT_NAME="chnroute.nftset"
CHNROUTE6_NFT_NAME="chnroute6.nftset"
GFWIP_NFT_NAME="gfwip.nftset"
GFWIP6_NFT_NAME="gfwip6.nftset"
V2RAY_PORT="1060"
V2RAY_DNS_PORTS="5354 5356 5358 5360" #5356走tcp
V2RAY_BIN="/usr/bin/v2ray"
V2RAY_CONF="/etc/config/v2ray.json"
set_multi_domestic_dns() {
current_dns_servers_list=`uci get dhcp.@dnsmasq[0].server 2>/dev/null`
min_len=$(( ${#DEFAULT_DNS_SERVER} < ${#current_dns_servers_list} ? ${#DEFAULT_DNS_SERVER} : ${#current_dns_servers_list} ))
if [ x${DEFAULT_DNS_SERVER:0:$min_len} != x${current_dns_servers_list:0:$min_len} ]; then
echo "[+] 设置使用国内DNS服务器"
uci -q delete dhcp.@dnsmasq[0].server
uci add_list dhcp.@dnsmasq[0].server=${DEFAULT_DNS_SERVER}
for dns_server in $DOMESTIC_DNS_SERVERS; do
uci add_list dhcp.@dnsmasq[0].server="${dns_server}"
done
uci set dhcp.@dnsmasq[0].noresolv=0
uci set dhcp.@dnsmasq[0].nohosts=0
uci commit dhcp
echo "[+] 重启dnsmasq服务"
/etc/init.d/dnsmasq restart 1>/dev/null 2>&1
fi
}
set_multi_foreign_dns() {
current_dns_servers_list=`uci get dhcp.@dnsmasq[0].server 2>/dev/null`
min_len=$(( ${#LOCAL_IP} < ${#current_dns_servers_list} ? ${#LOCAL_IP} : ${#current_dns_servers_list} ))
if [ x${LOCAL_IP:0:$min_len} != x${current_dns_servers_list:0:$min_len} ]; then
echo "[+] 设置使用ChinaDNSNG DNS服务器"
chinadnsng_addr_port=${LOCAL_IP}"#"${CHINADNSNG_PORT}
uci -q delete dhcp.@dnsmasq[0].server
uci add_list dhcp.@dnsmasq[0].server=${chinadnsng_addr_port}
uci set dhcp.@dnsmasq[0].noresolv=1
uci set dhcp.@dnsmasq[0].nohosts=1
uci commit dhcp
echo "[+] 重启dnsmasq服务"
/etc/init.d/dnsmasq restart 1>/dev/null 2>&1
fi
}
append_chnroute_list() {
# 检查和创建 set:chnroute
if ! nft list set inet global chnroute 2>/dev/null >/dev/null; then
echo "[*] 创建 inet global 表和 chnroute 集合"
nft -f ${CHINADNSNG_FILES_PATH}${CHNROUTE_NFT_NAME}
fi
# 检查和创建 set:chnroute6
if ! nft list set inet global chnroute6 2>/dev/null >/dev/null; then
echo "[*] 创建 inet global 表和 chnroute6 集合"
nft -f ${CHINADNSNG_FILES_PATH}${CHNROUTE6_NFT_NAME}
fi
# 添加私有地址段,tproxy模式下要删除
#echo "[+] 添加内网地址段到 chnroute"
#nft add element inet global chnroute { \
# 0.0.0.0/8, 10.0.0.0/8, 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 \
#}
# 加载 VPS 域名解析结果
echo "[+] 解析 $DOMAIN 并加入 chnroute/chnroute6"
# 处理 IPv4
dig +short A "$DOMAIN" @"${DEFAULT_DNS_SERVER}" \
| grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' \
| sort -u \
| while read -r ip; do
if [ "$ip" != "$DEFAULT_DNS_SERVER" ] && [ "$ip" != "0.0.0.0" ]; then
nft add element inet global chnroute { $ip } 2>/dev/null
echo "已经加入 IPv4: $ip"
else
echo "忽略无效 IPv4: $ip"
fi
done
# 处理 IPv6
dig +short AAAA "$DOMAIN" @"${DEFAULT_DNS_SERVER}" \
| grep -Eo '([0-9a-fA-F:]{2,})' \
| sort -u \
| while read -r ip6; do
if [ -n "$ip6" ] && [ "$ip6" != "::" ]; then
nft add element inet global chnroute6 { $ip6 } 2>/dev/null
echo "已经加入 IPv6: $ip6"
else
echo "忽略无效 IPv6: $ip6"
fi
done
echo " chnroute 已载入 $(nft list set inet global chnroute | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(/[0-9]+)?' | wc -l) 条目"
}
append_gfwip_list(){
# 检查和创建 set:gfwip
if ! nft list set inet global gfwip 2>/dev/null >/dev/null; then
echo "[*] 创建 inet global 表和 gfwip 集合"
nft -f ${CHINADNSNG_FILES_PATH}${GFWIP_NFT_NAME}
fi
# 检查和创建 set:gfwip6
if ! nft list set inet global gfwip6 2>/dev/null >/dev/null; then
echo "[*] 创建 inet global 表和 gfwip6 集合"
nft -f ${CHINADNSNG_FILES_PATH}${GFWIP6_NFT_NAME}
fi
}
prepare_work(){
# —— 策略路由:fwmark=1 走本地路由表 100 ——
echo "[+] 创建策略路由:fwmark=1 走本地路由表 100 "
ip route flush table 100 2>/dev/null
ip rule add fwmark 1 lookup 100 2>/dev/null
ip route add local 0.0.0.0/0 dev lo table 100 2>/dev/null
ip -6 route flush table 100 2>/dev/null
ip -6 rule add fwmark 1 lookup 100 2>/dev/null
ip -6 route add local ::/0 dev lo table 100 2>/dev/null
# —— 建议的内核参数(OpenWrt 可放 /etc/sysctl.d/*.conf)——
sysctl -w net.ipv4.ip_forward=1 1>/dev/null
sysctl -w net.ipv4.conf.all.rp_filter=0 1>/dev/null
sysctl -w net.ipv4.conf.default.rp_filter=0 1>/dev/null
# LAN 口是 br-lan:
sysctl -w net.ipv4.conf.br-lan.rp_filter=0 1>/dev/null
}
create_empty_chain(){
# 确保 table 存在
nft create table inet global 2>/dev/null
nft add chain inet global prerouting { type filter hook prerouting priority mangle\; policy accept\; } 2>/dev/null
nft add chain inet global output { type route hook output priority mangle\; policy accept\; } 2>/dev/null
nft add chain inet global divert { type filter hook prerouting priority -150 \; } 2>/dev/null
# 清空链
nft flush chain inet global prerouting
nft flush chain inet global output
nft flush chain inet global divert
}
create_chain_rules(){
create_empty_chain
# 1) 已打标的不再处理,避免递归,这里0xff 对应 v2ray 配置文件中的mark值
nft add rule inet global prerouting meta mark 0xff return
# 2) 目的为本机的流量放行(LAN 访问路由器服务)
nft add rule inet global prerouting ip daddr ${LOCAL_IP} return
nft add rule inet global prerouting ip daddr ${LAN_IPS} tcp dport 0-65535 return
nft add rule inet global prerouting ip daddr ${LAN_IPS} udp dport != \{${CHINADNSNG_PORT},${V2RAY_DNS_PORTS// /,}\} return
# 3) 跳过常见的保留/内网地址(IPv4)_127.0.0.1网段除外
nft add rule inet global prerouting ip daddr \{ ${RESERVED_IPS// /,} \} return
# 4) 跳过常见的保留/内网地址(IPv6)
nft add rule inet global prerouting ip6 daddr \{ ${RESERVED_IP6S// /,} \} return
# 1) 已打标的不再处理
nft add rule inet global prerouting meta mark 0xff return
# 2) 跳过目的为本机/本地服务
nft add rule inet global output ip daddr ${LOCAL_IP} return
nft add rule inet global output ip daddr ${LAN_IPS} tcp dport 0-65535 return
nft add rule inet global output ip daddr ${LAN_IPS} udp dport != \{${CHINADNSNG_PORT},${V2RAY_DNS_PORTS// /,}\} return
# 3) 跳过保留/内网地址(IPv4)_127.0.0.1网段除外
nft add rule inet global output ip daddr \{ ${RESERVED_IPS// /,} \} return
# 4) 跳过保留/内网地址(IPv6)
nft add rule inet global output ip6 daddr \{ ${RESERVED_IP6S// /,} \} return
# 5) divert链,避免已有连接的包二次通过 TPROXY,不用也可以
nft add rule inet global divert socket transparent 1 tcp dport 0-65535 meta mark set 1 accept
}
enable_chnroute_firewall_rules(){
create_chain_rules
# IPv4 TCP/UDP 目标不在 @chnroute 才 TPROXY 到 V2Ray
nft add rule inet global prerouting ip daddr != @chnroute tcp dport 0-65535 tproxy ip to :${V2RAY_PORT} meta mark set 1 accept
nft add rule inet global prerouting ip daddr != @chnroute udp dport 0-65535 tproxy ip to :${V2RAY_PORT} meta mark set 1 accept
# IPv6 TCP/UDP 目标不在 @chnroute6 才 TPROXY 到 V2Ray
nft add rule inet global prerouting ip6 daddr != @chnroute6 tcp dport 0-65535 tproxy ip6 to :${V2RAY_PORT} meta mark set 1 accept
nft add rule inet global prerouting ip6 daddr != @chnroute6 udp dport 0-65535 tproxy ip6 to :${V2RAY_PORT} meta mark set 1 accept
# IPv4 TCP/UDP 目标不在 @chnroute 才打标
nft add rule inet global output ip daddr != @chnroute tcp dport 0-65535 meta mark set 1 accept
nft add rule inet global output ip daddr != @chnroute udp dport 0-65535 meta mark set 1 accept
# IPv6 TCP/UDP 目标不在 @chnroute6 才打标
nft add rule inet global output ip6 daddr != @chnroute6 tcp dport 0-65535 meta mark set 1 accept
nft add rule inet global output ip6 daddr != @chnroute6 udp dport 0-65535 meta mark set 1 accept
}
enable_gfwip_firewall_rules(){
create_chain_rules
# IPv4 TCP/UDP 目标在 @gfwip 才 TPROXY 到 V2Ray
nft add rule inet global prerouting ip daddr @gfwip tcp dport 0-65535 tproxy ip to :${V2RAY_PORT} meta mark set 1 accept
nft add rule inet global prerouting ip daddr @gfwip udp dport 0-65535 tproxy ip to :${V2RAY_PORT} meta mark set 1 accept
# IPv6 TCP/UDP 目标在 @gfwip6 才 TPROXY 到 V2Ray
nft add rule inet global prerouting ip6 daddr @gfwip6 tcp dport 0-65535 tproxy ip6 to :${V2RAY_PORT} meta mark set 1 accept
nft add rule inet global prerouting ip6 daddr @gfwip6 udp dport 0-65535 tproxy ip6 to :${V2RAY_PORT} meta mark set 1 accept
# IPv4 TCP/UDP 目标在 @gfwip 才打标
nft add rule inet global output ip daddr @gfwip tcp dport 0-65535 meta mark set 1 accept
nft add rule inet global output ip daddr @gfwip udp dport 0-65535 meta mark set 1 accept
# IPv6 TCP/UDP 目标在 @gfwip6 才打标
nft add rule inet global output ip6 daddr @gfwip6 tcp dport 0-65535 meta mark set 1 accept
nft add rule inet global output ip6 daddr @gfwip6 udp dport 0-65535 meta mark set 1 accept
}
disable_v2ray_rules() {
create_empty_chain
set_multi_domestic_dns
echo "ingfw" > /tmp/v2raymode.txt
}
stop_service() {
echo "[+] 停止 v2ray 服务"
disable_v2ray_rules
}
enable_v2ray_rules(){
running_v2ray_mode=$(cat /tmp/v2raymode.txt 2>/dev/null | tr -d '\r')
v2ray_mode=`uci get advancedconfig.@rules[0].v2raymode 2>/dev/null`
if [ x${v2ray_mode} = x${running_v2ray_mode} ]; then
echo "[+] v2ray模式未变化"
else
disable_v2ray_rules
prepare_work
if [ "${v2ray_mode}" = "outlands" ]; then
echo "[+] 设置${v2ray_mode}(境外全局代理模式)模式中"
append_chnroute_list
enable_chnroute_firewall_rules
set_multi_foreign_dns
elif [ "${v2ray_mode}" = "gfwlist" ]; then
echo "[+] 设置${v2ray_mode}(白名单代理模式)模式中"
append_gfwip_list
enable_gfwip_firewall_rules
set_multi_foreign_dns
elif [ "${v2ray_mode}" = "ingfw" ]; then
echo "[+] 设置墙内访问模式"
fi
echo "${v2ray_mode}" > /tmp/v2raymode.txt
fi
}
start_service() {
# 检查 chinadns-ng 是否在运行
if ! pgrep -f chinadns-ng >/dev/null 2>&1; then
echo "[+] chinadns-ng 未运行, 启动中..."
/etc/init.d/chinadns-ng start
sleep 2 # 等几秒确保 chinadns-ng 完全启动
fi
echo "[+] 启动 v2ray 服务"
mkdir -p /var/log/v2ray
ulimit -n 65535
procd_open_instance
procd_set_param command $V2RAY_BIN run -config $V2RAY_CONF
procd_set_param file $V2RAY_CONF
procd_set_param respawn
procd_set_param stdout 1
procd_set_param stderr 1
procd_set_param pidfile /var/run/v2ray.pid
enable_v2ray_rules
procd_close_instance
}
service_triggers() {
procd_add_reload_trigger "advancedconfig"
}
参考:
1.v2ray官方文档
2.chatgpt