Skip to content

校园网环境下的OpenWrt NAT6配置

背景与前提

同济寝室的IPv6以前可以直接获取/64的前缀,可以支持路由器下所有支持IPv6的设备自动获取IPv6地址,非常方便。但前一阵子突然炸了,仔细检查后发现,现在寝室的IPv6只给/128的地址了,只能重新折腾一下路由器了 (又可以摸了)

基本完全参考[2]的方案,此方案是给不支持 DHCPv6-PD且设备需要认证才能联网的环境使用的,不过也没条件尝试其他情况……

假如你获取的IPv6地址不能获取前缀,即地址后有”/128“的后缀,按此文配置NAT6可能可以解决路由器下其他设备的IPv6访问问题。

正常情况家宽支持 IPv6,只要光猫处于桥接模式,直接使用 OpenWRT 默认设置即可完美支持 IPv6。

注意:根据原贴信息,需要确保使用的 OpenWRT 依然是基于 iptables 这一套做的防火墙,如果是基于 nftables 的 OpenWRT 原贴没有研究过 (我也没有) 。(比如官方 OpenWRT 从 22.03 版本开始切换为 nftables,国内修改版一般不会跟进这个修改。)

配置步骤

1. 安装必要的软件包

opkg update 
opkg install ip6tables
opkg install kmod-ipt-nat6

提示:某些第三方OpenWRT固件(如X-WRT、Lean固件)可能已预装这些软件包

2. 调整IPv6内网地址前缀

uci set network.globals.ula_prefix="$(uci get network.globals.ula_prefix | sed 's/^./d/')"
uci commit network

重要说明

  • 家庭宽带通常使用 2 开头的IPv6前缀
  • 此步骤仅适用于特殊网络环境,不推荐普通家庭网络使用

3. 配置DHCP服务器通告默认路由

uci set dhcp.lan.ra_default='1'
uci commit dhcp

4. 创建NAT6启动脚本

创建 /etc/init.d/nat6 文件:

> /etc/init.d/nat6
vi /etc/init.d/nat6

(脚本内容请参考附录

5. 设置脚本开机启动

chmod +x /etc/init.d/nat6
/etc/init.d/nat6 enable

6. 调整防火墙规则(可选)

uci set firewall.@rule["$(uci show firewall | grep 'Allow-ICMPv6-Forward' | cut -d'[' -f2 | cut -d']' -f1)"].enabled='0'
uci commit firewall

7. 修改系统内核转发配置

/etc/sysctl.conf 中添加或修改:

net.ipv6.conf.default.forwarding=2
net.ipv6.conf.all.forwarding=2
net.ipv6.conf.default.accept_ra=2
net.ipv6.conf.all.accept_ra=2

强烈建议:直接在 sysctl.conf 中修改,而非依赖其他脚本

8. 重启路由器

9. 备用方案:手动添加防火墙规则

如果重启后仍无法成功进行NAT6,可在OpenWRT防火墙规则中添加:

ip6tables -t nat -I POSTROUTING -s `uci get network.globals.ula_prefix` -j MASQUERADE

参考资料

  1. OpenWrt配置IPv6 NAT - TimeForget

  2. 再说 OpenWRT 校园网 IPv6 NAT6-OPENWRT专版-恩山无线论坛 - Powered by Discuz!


附录

file-nat6

修改后的 NAT6 文件内容如下,换行符必须是LF

Tip

在更新的OpenWRT里(如OpenWRT 21),有一处地方又发生了变化

文件的 $(uci get "network.$WAN6_NAME.ifname") 可能需要改成 $(uci get "network.$WAN6_NAME.device") 才能生效

因此作者进一步改了脚本 改成了 $(uci get "network.$WAN6_NAME.device" || uci get "network.$WAN6_NAME.ifname")

或者可以直接把 WAN6_INTERFACE=$(uci get "network.$WAN6_NAME.ifname") 替换为 WAN6_INTERFACE=wan,其中wan为WAN6对应的网络设备名

#!/bin/sh /etc/rc.common
# NAT6 init script for OpenWrt // Depends on package: kmod-ipt-nat6

# edited by Sad Pencil at 2020-02-09
# replace route command with ip command to solve issues on new OpenWRT


# edited by Sad Pencil at 2021-11-29
# update line WAN6_INTERFACE=$(uci get "network.$WAN6_NAME.device" || uci get "network.$WAN6_NAME.ifname")


START=55

# Options
# -------

# Use temporary addresses (IPv6 privacy extensions) for outgoing connections? Yes: 1 / No: 0
PRIVACY=1

# Maximum number of attempts before this script will stop in case no IPv6 route is available
# This limits the execution time of the IPv6 route lookup to (MAX_TRIES+1)*(MAX_TRIES/2) seconds. The default (15) equals 120 seconds.
MAX_TRIES=15

# An initial delay (in seconds) helps to avoid looking for the IPv6 network too early. Ideally, the first probe is successful.
# This would be the case if the time passed between the system log messages "Probing IPv6 route" and "Setting up NAT6" is 1 second.
DELAY=5

# Logical interface name of outbound IPv6 connection
# There should be no need to modify this, unless you changed the default network interface names
# Edit by Vincent: I never changed my default network interface names, but still I have to change the WAN6_NAME to "wan" instead of "wan6"
WAN6_NAME="wan6"

# ---------------------------------------------------
# Options end here - no need to change anything below

boot() {
        [ $DELAY -gt 0 ] && sleep $DELAY
        WAN6_INTERFACE=$(uci get "network.$WAN6_NAME.device" || uci get "network.$WAN6_NAME.ifname")
        logger -t NAT6 "Probing IPv6 route"
        PROBE=0
        COUNT=1
        while [ $PROBE -eq 0 ]
        do
                if [ $COUNT -gt $MAX_TRIES ]
                then
                        logger -t NAT6 "Fatal error: No IPv6 route found (reached retry limit)" && exit 1
                fi
                sleep $COUNT
                COUNT=$((COUNT+1))
                PROBE=$(ip -6 route | grep -i '^default.*via' | grep -i -F "dev $WAN6_INTERFACE" | grep -i -o 'via.*' | wc -l)
        done

        logger -t NAT6 "Setting up NAT6"

        if [ -z "$WAN6_INTERFACE" ] || [ ! -e "/sys/class/net/$WAN6_INTERFACE/" ] ; then
                logger -t NAT6 "Fatal error: Lookup of $WAN6_NAME interface failed. Were the default interface names changed?" && exit 1
        fi
        WAN6_GATEWAY=$(ip -6 route | grep -i '^default.*via' | grep -i -F "dev $WAN6_INTERFACE" | grep -i -o 'via.*' | cut -d ' ' -f 2 | head -n 1)
        if [ -z "$WAN6_GATEWAY" ] ; then
                logger -t NAT6 "Fatal error: No IPv6 gateway for $WAN6_INTERFACE found" && exit 1
        fi
        LAN_ULA_PREFIX=$(uci get network.globals.ula_prefix)
        if [ $(echo "$LAN_ULA_PREFIX" | grep -c -E "^([0-9a-fA-F]{4}):([0-9a-fA-F]{0,4}):") -ne 1 ] ; then
                logger -t NAT6 "Fatal error: IPv6 ULA prefix $LAN_ULA_PREFIX seems invalid. Please verify that a prefix is set and valid." && exit 1
        fi

        ip6tables -t nat -I POSTROUTING -s "$LAN_ULA_PREFIX" -o "$WAN6_INTERFACE" -j MASQUERADE
        if [ $? -eq 0 ] ; then
                logger -t NAT6 "Added IPv6 masquerading rule to the firewall (Src: $LAN_ULA_PREFIX - Dst: $WAN6_INTERFACE)"
        else
                logger -t NAT6 "Fatal error: Failed to add IPv6 masquerading rule to the firewall (Src: $LAN_ULA_PREFIX - Dst: $WAN6_INTERFACE)" && exit 1
        fi

        ip -6 route add 2000::/3 via "$WAN6_GATEWAY" dev "$WAN6_INTERFACE"
        if [ $? -eq 0 ] ; then
                logger -t NAT6 "Added $WAN6_GATEWAY to routing table as gateway on $WAN6_INTERFACE for outgoing connections"
        else
                logger -t NAT6 "Error: Failed to add $WAN6_GATEWAY to routing table as gateway on $WAN6_INTERFACE for outgoing connections"
        fi

        if [ $PRIVACY -eq 1 ] ; then
                echo 2 > "/proc/sys/net/ipv6/conf/$WAN6_INTERFACE/accept_ra"
                if [ $? -eq 0 ] ; then
                        logger -t NAT6 "Accepting router advertisements on $WAN6_INTERFACE even if forwarding is enabled (required for temporary addresses)"
                else
                        logger -t NAT6 "Error: Failed to change router advertisements accept policy on $WAN6_INTERFACE (required for temporary addresses)"
                fi
                echo 2 > "/proc/sys/net/ipv6/conf/$WAN6_INTERFACE/use_tempaddr"
                if [ $? -eq 0 ] ; then
                        logger -t NAT6 "Using temporary addresses for outgoing connections on interface $WAN6_INTERFACE"
                else
                        logger -t NAT6 "Error: Failed to enable temporary addresses for outgoing connections on interface $WAN6_INTERFACE"
                fi
        fi

        exit 0
}