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_chinadns_ng

#!/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(only for test) first
#
#

START=90
USE_PROCD=1

LAN_IPS="10.0.0.1/16" #局域网ip段
V2RAY_BIN="/usr/bin/v2ray"
V2RAY_CONF="/etc/config/v2ray.json"
V2RAY_PORT="1060"
V2RAY_DNS_PORTS="5354 5356 5358 5360" #5356走tcp

CHINADNSNG_PORT="5353"
CHINADNSNG_FILES_PATH="/etc/chinadns-ng/" #结尾必须带斜杠
CHINADNSNG_BIN="/usr/bin/chinadns-ng"
CHINADNSNG_CONF="/etc/config/chinadns-ng"

DEFAULT_DNS_SERVER="223.6.6.6" 
LOCAL_IP="127.0.0.1"

# 本配置文件中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"
CHAINS_NFT_NAME="chains.nftset"
RESERVEDIP_NFT_NAME="reservedip.nftset"
RESERVEDIP6_NFT_NAME="reservedip6.nftset"
GFWIP_NFT_NAME="gfwip.nftset"
GFWIP6_NFT_NAME="gfwip6.nftset"
DISABLE_CHNROUTE_NFT_NAME="disable_chnroute.nftset"
DISABLE_CHNROUTE6_NFT_NAME="disable_chnroute6.nftset"
DISABLE_GFWIP_NFT_NAME="disable_gfwip.nftset"
DISABLE_GFWIP6_NFT_NAME="disable_gfwip6.nftset"
DIRECT_GROUP_FILE="direct.txt"

#从v2ray的配置文件中读取网址,放到chinadns-ng的直接解析文件中,避免因无法解析导致无法连接到服务端
add_v2ray_domain_to_direct_group() {
    direct_file=${CHINADNSNG_FILES_PATH}${DIRECT_GROUP_FILE}
    . /usr/share/libubox/jshn.sh
    json_load_file "${V2RAY_CONF}"
    json_select outbounds
    json_select 1
    json_select settings
    json_select vnext
    json_select 1
    json_get_var addr address
    if [ -s "${direct_file}" ]; then 
        if grep -q "${addr}" "${direct_file}"; then
            echo "[+] v2ray域名已经存在于${direct_file}"
        else
            echo "[+] 将v2ray域名添加到${direct_file}"
            if [ "$(tail -c 1 ${direct_file})" != "" ]; then
                # 最后一行没有换行符,先补一个
                printf '\n' >> ${direct_file}
            fi
            echo ${addr} >> ${direct_file}
        fi
    else
        echo "[+] 创建${direct_file},将v2ray域名添加到${direct_file}"
        echo ${addr} > ${direct_file}
    fi
}

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}
        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
    echo "[*] 创建 inet global 表和 chnroute 集合"
    nft -f ${CHINADNSNG_FILES_PATH}${CHNROUTE_NFT_NAME} 2>/dev/null

    # 创建 set:chnroute6
    echo "[*] 创建 inet global 表和 chnroute6 集合"
    nft -f ${CHINADNSNG_FILES_PATH}${CHNROUTE6_NFT_NAME} 2>/dev/null

}

append_gfwip_list(){
    # 创建 set:gfwip
    echo "[*] 创建 inet global 表和 gfwip 集合"
    nft -f ${CHINADNSNG_FILES_PATH}${GFWIP_NFT_NAME} 2>/dev/null

    # 创建 set:gfwip6
    echo "[*] 创建 inet global 表和 gfwip6 集合"
    nft -f ${CHINADNSNG_FILES_PATH}${GFWIP6_NFT_NAME} 2>/dev/null

}

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(){
    # 创建 prerouting output divert 链
    echo "[*] 创建 inet global 表和 prerouting output divert链"
    nft -f ${CHINADNSNG_FILES_PATH}${CHAINS_NFT_NAME} 
      
}

create_chain_rules(){
    create_empty_chain
    echo "[*] 创建 inet global 表和 保留地址 @localnet_without_127 @localnet6 集"
    nft -f ${CHINADNSNG_FILES_PATH}${RESERVEDIP_NFT_NAME} 
    nft -f ${CHINADNSNG_FILES_PATH}${RESERVEDIP6_NFT_NAME} 
    # 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 @localnet_without_127 return

    # 4) 跳过常见的保留/内网地址(IPv6)
    nft add rule inet global prerouting ip6 daddr @localnet6 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 @localnet_without_127 return

    # 4) 跳过保留/内网地址(IPv6)
    nft add rule inet global output ip6 daddr @localnet6 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_nft_rules() {
    create_empty_chain
    nft -f ${CHINADNSNG_FILES_PATH}${DISABLE_CHNROUTE_NFT_NAME} 2>/dev/null
    nft -f ${CHINADNSNG_FILES_PATH}${DISABLE_CHNROUTE6_NFT_NAME} 2>/dev/null
    nft -f ${CHINADNSNG_FILES_PATH}${DISABLE_GFWIP_NFT_NAME} 2>/dev/null
    nft -f ${CHINADNSNG_FILES_PATH}${DISABLE_GFWIP6_NFT_NAME} 2>/dev/null
    set_multi_domestic_dns
    echo "ingfw" > /tmp/v2raymode.txt   
}

stop_service()  {
    echo "[+] 停止 v2ray 服务"
    disable_nft_rules
}

enable_nft_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_nft_rules
        prepare_work
        add_v2ray_domain_to_direct_group
        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()  {
    enable_nft_rules
    echo "[+] 启动 chinadns-ng 服务"
    procd_open_instance
    procd_set_param command $CHINADNSNG_BIN -C $CHINADNSNG_CONF
    procd_set_param respawn
    procd_set_param stdout 1
    procd_set_param stderr 1
    procd_close_instance
    sleep 2

    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
    procd_close_instance
}

service_triggers() {
    procd_add_reload_trigger "advancedconfig"
}

3.修改/etc/chinadns-ng/chains.nftset,相对于redirect,主要是增加了divert

add table inet global 

add chain inet global prerouting { type filter hook prerouting priority mangle; policy accept; } 
add chain inet global output    { type route  hook output    priority mangle; policy accept; } 
add chain inet global divert { type filter hook prerouting priority -150 ; } 

flush chain inet global prerouting
flush chain inet global output
flush chain inet global divert

4.修改/etc/chinadns-ng/,相对于redirect,主要是删除:127.0.0.0/8

add table inet global

# 定义 IPv4 本地/保留网段
define ipv4_localnet = {
    0.0.0.0/8,          # 本网络/未指定
    10.0.0.0/8,         # 私有网络
    100.64.0.0/10,      # CGNAT / Carrier-grade NAT
                        # 回环地址,删除#127.0.0.0/8,        
    169.254.0.0/16,     # 链路本地 / APIPA
    172.16.0.0/12,      # 私有网络
    192.0.0.0/24,       # IETF 协议保留
    192.0.2.0/24,       # 文档/示例地址 (TEST-NET-1)
    192.88.99.0/24,     # 6to4 中继(已废弃)
    192.168.0.0/16,     # 私有网络
    198.18.0.0/15,      # 测试网络
    198.51.100.0/24,    # 文档/示例地址 (TEST-NET-2)
    203.0.113.0/24,     # 文档/示例地址 (TEST-NET-3)
    224.0.0.0/4,        # 多播
    240.0.0.0/4         # 未来保留 已经包含 255.255.255.255
}

add set inet global localnet_without_127 { type ipv4_addr; flags interval; elements = $ipv4_localnet; }

其他配置参考上一篇《在openwrt下使用chinadns-ng搭配v2ray实现透明代理》

参考:

1.v2ray官方文档

2.chatgpt