导语

这几天发现运营商已经可以分配ipv6了,使用手上的openwrt智能路由器玩一下ipv6。

IPV6的常用模式

在网上搜了一下,ipv6常用的配置模式有Native、NAT6、6TO4、6IN4等。
6TO4与6IN4为隧道模式,需要使用公网IPV4地址,我没有公网IPV4地址呀o(╥﹏╥)o!!!
所以我尝试了配置Native(使用relay),和NAT6使PC主机获取IPV6并上网。

我使用的openwrt1806固件,wan口直接可以从ISP获取ipv6地址,执行ping6命令测试路由器使用ipv6上网,ping6通后证明路由器广域网端使用ipv6正常,接下来就是正题了,配置lan使用ipv6。

$ping6 mp.weixin.qq.com

配置Native(使用relay)使下流主机使用ipv6

ipv6 Native模式,下流所有主机都是使用由ISP分配原生ipv6进行上网。我们使用relay,使下流主机直接通过ISP获取ipv6。
目前openwrt1806已经在odhcpd中集成了relay功能,所以直接在/etc/config/dhcp配置wan、lan信息即可,值得注意的是,在/etc/config/network文件中,我们配置的ipv6 interface为wan6,所以在/etc/config/dhcp中配置wan口relay时要与之对应。

cat /etc/config/network
...
config interface 'wan'
	...
	option proto 'dhcp'

config interface 'wan6'
	...
	option proto 'dhcpv6'
...

查看/etc/config/network文件,可知ipv6 wan为wan6,接着配置/etc/config/dhcp。

...
config dhcp 'lan'
	...
	option dhcpv6 'relay'
	option ra 'relay'
	option ndp 'relay'

...

config dhcp 'wan6'
	option interface 'wan6'
	option dhcpv6 'relay'
	option ra 'relay'
	option ndp 'relay'
	option master '1'
...

修改完之后重启dhcp,下流主机就已经可以获取到ipv6了。
ps:获取得有点慢,有时候需要3~4分钟(ಥ_ಥ)

$/etc/init.d/dnsmasq restart && /etc/init.d/odhcpd restart

配置nat6使下流主机使用ipv6

ipv6 nat6与ipv4 nat相似,内部建立nat局域网,公用一个原生ipv6进行上网。
注意:如果你已经配置了relay,请恢复openwrt1806的默认情况下,在进行一下配置

1. 安装kmod-ipt-nat6

opkg update
opkg install kmod-ipt-nat6

2. 修改局域网ipv6前缀

官方教程里是直接将默认ipv6前缀的前4bits修改为1101即十六进制的d,一直没弄明白为什么要设成d,个人感觉可能d前4bits的ipv6尚未分配,所以不会造成冲突。

uci set network.globals.ula_prefix="$(uci get network.globals.ula_prefix | sed -e "s/^./d/")"
uci commit network
/etc/init.d/network restart

3. dhcp宣告默认路由

uci set dhcp.lan.ra_default="1"
uci commit dhcp
/etc/init.d/odhcpd restart

4. firewall配置

上流wan区配置ipv6地址伪装

uci set $(uci show firewall | sed -n -e "/\.name='wan'$/s//.masq6='1'/p" | sed -n -e "1p")
uci commit firewall

禁用防火墙Allow-ICMPv6-Forward规则

uci set $(uci show firewall | sed -n -e "/\.name='Allow-ICMPv6-Forward'$/s//.enabled='0'/p" | sed -n -e "1p")
uci commit firewall

保存nat6防火墙规则脚本,直接复制到shell console中执行,会在生成/etc/firewall.nat6

cat << "EOF" > /etc/firewall.nat6
# Masquerading nat6 firewall script
#
# Then you can configure in /etc/config/firewall per zone, ala where you have:
#   option masq 1
# Just drop this in beneath it:
#   option masq6 1
# For IPv6 privacy (temporary addresses used for outgoing), also add:
#   option masq6_privacy 1
#
# Hope it's useful!
#
# https://github.com/akatrevorjay/openwrt-masq6
# ~ trevorj <github@trevor.joynson.io>
 
set -eo pipefail
 
. /lib/functions.sh
. /lib/functions/network.sh
. /usr/share/libubox/jshn.sh
 
log() {
    logger -t nat6 -s "$@"
}
 
get_ula_prefix() {
    uci get network.globals.ula_prefix
}
 
validate_ula_prefix() {
    local ula_prefix="$1"
    if [ $(echo "$ula_prefix" | grep -c -E "^([0-9a-fA-F]{4}):([0-9a-fA-F]{0,4}):") -ne 1 ] ; then
        log "Fatal error: IPv6 ULA ula_prefix=\"$ula_prefix\" seems invalid. Please verify that a ula_prefix is set and valid."
        return 1
    fi
}
 
ip6t() {
    ip6tables "$@"
}
 
ip6t_add() {
    if ! ip6t -C "$@" &>/dev/null; then
        ip6t -I "$@"
    fi
}
 
nat6_init() {
    iptables-save -t nat \
    | sed -e "/\s[DS]NAT\s/d;/\sMASQUERADE$/d" \
    | ip6tables-restore -T nat
}
 
masq6_network() {
    # $config contains the ID of the current section
    local network_name="$1"
 
    local device
    network_get_device device "$network_name" || return 0
 
    local done_net_dev
    for done_net_dev in $DONE_NETWORK_DEVICES; do
        if [ "$done_net_dev" = "$device" ]; then
            log "Already configured device=\"$device\", so leaving as is."
            return 0
        fi
    done
 
    log "Found device=\"$device\" for network_name=\"$network_name\"."
 
    if [ $zone_masq6_privacy -eq 1 ]; then
        log "Enabling IPv6 temporary addresses for device=\"$device\"."
 
        log "Accepting router advertisements on $device even if forwarding is enabled (required for temporary addresses)"
        echo 2 > "/proc/sys/net/ipv6/conf/$device/accept_ra" \
          || log "Error: Failed to change router advertisements accept policy on $device (required for temporary addresses)"
 
        log "Using temporary addresses for outgoing connections on interface $device"
        echo 2 > "/proc/sys/net/ipv6/conf/$device/use_tempaddr" \
          || log "Error: Failed to enable temporary addresses for outgoing connections on interface $device"
    fi
 
    append DONE_NETWORK_DEVICES "$device"
}
 
handle_zone() {
    # $config contains the ID of the current section
    local config="$1"
 
    local zone_name
    config_get zone_name "$config" name
 
    # Enable masquerading via NAT6
    local zone_masq6
    config_get_bool zone_masq6 "$config" masq6 0
 
    log "Firewall config=\"$config\" zone=\"$zone_name\" zone_masq6=\"$zone_masq6\"."
 
    if [ $zone_masq6 -eq 0 ]; then
        return 0
    fi
 
    # IPv6 privacy extensions: Use temporary addrs for outgoing connections?
    local zone_masq6_privacy
    config_get_bool zone_masq6_privacy "$config" masq6_privacy 1
 
    log "Found firewall zone_name=\"$zone_name\" with zone_masq6=\"$zone_masq6\" zone_masq6_privacy=\"$zone_masq6_privacy\"."
 
    log "Setting up masquerading nat6 for zone_name=\"$zone_name\" with zone_masq6_privacy=\"$zone_masq6_privacy\""
 
    local ula_prefix=$(get_ula_prefix)
    validate_ula_prefix "$ula_prefix" || return 1
 
    local postrouting_chain="zone_${zone_name}_postrouting"
    log "Ensuring ip6tables chain=\"$postrouting_chain\" contains our MASQUERADE."
    ip6t_add "$postrouting_chain" -t nat \
        -m comment --comment "!fw3" -j MASQUERADE
 
    local input_chain="zone_${zone_name}_input"
    log "Ensuring ip6tables chain=\"$input_chain\" contains our permissive DNAT rule."
    ip6t_add "$input_chain" -t filter -m conntrack --ctstate DNAT \
        -m comment --comment "!fw3: Accept port forwards" -j ACCEPT
 
    local forward_chain="zone_${zone_name}_forward"
    log "Ensuring ip6tables chain=\"$forward_chain\" contains our permissive DNAT rule."
    ip6t_add "$forward_chain" -t filter -m conntrack --ctstate DNAT \
        -m comment --comment "!fw3: Accept port forwards" -j ACCEPT
 
    local DONE_NETWORK_DEVICES=""
    config_list_foreach "$config" network masq6_network
 
    log "Done setting up nat6 for zone=\"$zone_name\" on devices: $DONE_NETWORK_DEVICES"
}
 
main() {
    nat6_init
    config_load firewall
    config_foreach handle_zone zone
}
 
main "$@"
EOF

在/etc/config/firewall中添加自动执行/etc/firewall.nat6

uci -q delete firewall.nat6
uci set firewall.nat6="include"
uci set firewall.nat6.path="/etc/firewall.nat6"
uci set firewall.nat6.reload="1"
uci commit firewall
/etc/init.d/firewall restart

5.配置内核开启ra

在/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

到此,ipv6 nat6的功能就配置完了,重启以下路由。

6.测试使用

下流主机获取到一个有odhcpd分配的ipv6,前缀就是/etc/config/network里的globals.ula_prefix,我配置的前缀为ddaf:5606:0209::/48,此前缀类似ipv4的网关与掩码,如192.168.8.x。

root@GL-AR750:/# uci get network.globals.ula_prefix
ddaf:5606:0209::/48

打开浏览器测试ipv6 http://www.test-ipv6.com,可见用于上网的ipv6与路由器的wan口ipv6一样,即下流主机都是经过firewall使用同一个公网ipv6上网。

关于/etc/firewall.nat6脚本实现的内容,其实就是添加了3条firewall规则。

ip6tables -I zone_wan_postrouting -t nat -m comment --comment "!fw3" -j MASQUERADE   #ipv6出站地址伪装
ip6tables -I zone_wan_input -t filter -m conntrack --ctstate DNAT -m comment --comment "!fw3:" -j ACCEPT   #允许ipv6入站
ip6tables -I zone_wan_forward -t filter -m conntrack --ctstate DNAT -m comment --comment "!fw3: " -j ACCEPT    #允许ipv6转发