由于我们校园网的ipv6是教育网,有500Mbps对等,本来想着搞个nas玩玩,但是只给了/128,哪怕pppoe获得的/64也只有主地址能用(相当于还是/128),不得已使用了nat6 但是nat下的v6设备完全没有公网访问性,upnp/dmz这种东西也是为了ipv4设计的,nat6如果下bt连接性会受到很大的影响 所以考虑使用nat6的同时将内网设备使用防火墙完整转发出去即可实现类似ipv4 dmz的效果 虽然设备侧仍然不是公网但是对于bt下载(tracker拿到的是发送请求的地址不是网卡绑定地址)以及nas共享类用途基本足够了
用Gemini花了老长时间整了个脚本 直接运行会自动在后台循环更新转发规则(最后加一个worker就可以手动单次运行),即使公网地址会定期变动也可正常使用
目前仅在小米路由器be3600上测试 理论上openwrt类系统都可用 想办法让它开机自启就行了

下面讲一下如何在小米路由器上完成设置
首先想办法获取ssh并固化
随后开启nat6 然后给你设备设置一个ula前缀里面的固定地址并且替换脚本里面的 如果你用的不是pppoe那记得把脚本里的端口也改掉
连接ssh 设置防火墙允许wan区域转发到lan区域(记得改地址)

# 添加一条新的防火墙规则
uci add firewall rule
# 命名规则以便识别
uci set firewall.@rule[-1].name='Allow_IPv6_DMZ_Inbound_2333'
# 源区域为 wan (公网)
uci set firewall.@rule[-1].src='wan'
# 目标区域为 lan (内网)
uci set firewall.@rule[-1].dest='lan'
# 目标 IP 是我们内网设备的固定 IP(记得改记得改记得改记得改记得改记得改记得改记得改)
uci set firewall.@rule[-1].dest_ip='fd00:6868:6868::2333'
# 规则适用于 IPv6
uci set firewall.@rule[-1].family='ipv6'
# 允许所有协议
uci set firewall.@rule[-1].proto='all'
# 动作为接受
uci set firewall.@rule[-1].target='ACCEPT'
# 提交更改
uci commit firewall
# 重启防火墙使规则生效
/etc/init.d/firewall restart

创建脚本存放目录

mkdir -p /data/v6dnat

创建脚本

vim /data/v6dnat/setup_ipv6_dnat.sh

按i进入编辑模式,粘贴以下脚本(记得改地址和接口名)

#!/bin/sh

# --- 配置变量 ---
WAN_INTERFACE="pppoe-wan"
LAN_DEVICE_ULA_IPV6="fd00:6868:6868::2333" # 你内网设备固定的 ULA IPv6 地址
STATE_FILE="/tmp/current_public_ipv6.state" # 用于存储上一次的公网IP (重启后会清除)
LOCK_FILE="/tmp/setup_ipv6_dnat.lock"       # 锁文件,防止多个工作进程实例运行
LOOP_SLEEP_SECONDS=300                      # 循环检测间隔时间 (例如 300秒 = 5分钟)
INITIAL_DELAY_SECONDS=30                    # 首次启动时的延迟

# --- 工作进程逻辑 (在循环中执行) ---
run_worker() {
    # --- 信号处理 ---
    # 关键改动:忽略 HUP (hangup) 信号,这样在启动它的父进程退出后,本进程不会被终止
    trap '' HUP
    # 在接收到其他终止信号时,确保锁文件能被删除
    trap 'rm -f "$LOCK_FILE"; exit 0' INT TERM EXIT

    # 首次运行工作进程时的初始延迟
    sleep "$INITIAL_DELAY_SECONDS"

    while true; do
        # 1. 获取当前的公网 IPv6 地址
        current_public_ipv6=$(ip -6 addr show dev "$WAN_INTERFACE" scope global | grep 'inet6' | awk '{print $2}' | cut -d'/' -f1 | head -n 1)

        # 如果未能获取到当前公网 IP
        if [ -z "$current_public_ipv6" ]; then
            if [ -f "$STATE_FILE" ]; then
                last_known_public_ipv6_on_no_ip=$(cat "$STATE_FILE")
                if [ -n "$last_known_public_ipv6_on_no_ip" ]; then
                     ip6tables -t nat -D PREROUTING -i "$WAN_INTERFACE" -d "$last_known_public_ipv6_on_no_ip" -j DNAT --to-destination "$LAN_DEVICE_ULA_IPV6" >/dev/null 2>&1
                fi
                rm -f "$STATE_FILE"
            fi
            sleep "$LOOP_SLEEP_SECONDS"
            continue
        fi

        # 2. 获取上一次记录的公网 IPv6 地址
        last_known_public_ipv6=""
        if [ -f "$STATE_FILE" ]; then
            last_known_public_ipv6=$(cat "$STATE_FILE")
        fi

        # 3. 检查 IP 是否发生变化
        ip_has_changed=false
        if [ "$current_public_ipv6" != "$last_known_public_ipv6" ]; then
            ip_has_changed=true
        fi

        # 4. 如果 IP 地址已更改,并且之前记录过一个不同的旧 IP,则删除基于旧 IP 的规则
        if [ "$ip_has_changed" = true ] && [ -n "$last_known_public_ipv6" ]; then
            ip6tables -t nat -D PREROUTING -i "$WAN_INTERFACE" -d "$last_known_public_ipv6" -j DNAT --to-destination "$LAN_DEVICE_ULA_IPV6" >/dev/null 2>&1
        fi

        # 5. 检查当前正确的 DNAT 规则是否存在。如果不存在,则添加。
        rule_exists_check=$(ip6tables -t nat -C PREROUTING -i "$WAN_INTERFACE" -d "$current_public_ipv6" -j DNAT --to-destination "$LAN_DEVICE_ULA_IPV6" 2>/dev/null; echo $?)

        if [ "$rule_exists_check" -ne 0 ]; then
            ip6tables -t nat -I PREROUTING 1 -i "$WAN_INTERFACE" -d "$current_public_ipv6" -j DNAT --to-destination "$LAN_DEVICE_ULA_IPV6"
        fi

        # 6. 如果IP已更改 或 状态文件不存在 等情况,且当前IP的规则确实存在, 则更新状态文件
        if [ "$ip_has_changed" = true ] || ! [ -f "$STATE_FILE" ] || [ "$current_public_ipv6" != "$last_known_public_ipv6" ]; then
            if ip6tables -t nat -C PREROUTING -i "$WAN_INTERFACE" -d "$current_public_ipv6" -j DNAT --to-destination "$LAN_DEVICE_ULA_IPV6" >/dev/null 2>&1; then
                echo "$current_public_ipv6" > "$STATE_FILE"
            fi
        fi
        
        sleep "$LOOP_SLEEP_SECONDS"
    done
}

# --- 脚本主入口 ---
if [ "$1" = "worker" ]; then
    run_worker
else
    # 启动器部分
    if [ -e "$LOCK_FILE" ]; then
        if kill -0 "$(cat "$LOCK_FILE")" >/dev/null 2>&1; then
            exit 0
        else
            rm -f "$LOCK_FILE"
        fi
    fi

    script_full_path="/data/v6dnat/setup_ipv6_dnat.sh"

    # 直接将工作进程放到后台运行。因为它自己会处理 HUP 信号,所以不再需要 nohup。
    "$script_full_path" worker >/dev/null 2>&1 &
    
    exit 0
fi

按esc然后:wq保存退出
给予执行权限

chmod +x /data/v6dnat/setup_ipv6_dnat.sh

使用防火墙配置启动后自动执行脚本

# 为我们的脚本创建一个 firewall include 配置节,命名为 v6dnat_service
uci set firewall.v6dnat_service=include
# 指定类型为 script
uci set firewall.v6dnat_service.type='script'
# 指定脚本的完整路径
uci set firewall.v6dnat_service.path='/data/v6dnat/setup_ipv6_dnat.sh'
# 确保它被启用
uci set firewall.v6dnat_service.enabled='1'
# 提交 firewall 配置更改
uci commit firewall

重启路由器

reboot

检查进程是否正确自启动

ps w | grep "setup_ipv6_dnat.sh worker"

检查路由规则是否正确添加

ip6tables -t nat -L PREROUTING -v -n