4-Openwrt ipv6之NAT6

在实际使用过程有时候电信宽带可以下发给Wan口使用的ipv6地址,但是不下发给lan口使用的ipv6地址前缀。

这时候可以使用NAT6的方式,跟ipv4的NAT一样,有自己的内网地址,访问外部的时候经过NAT转化。ipv6也使用内网地址,根据ipv6的规则FD::/8开始的地址为内网地址。

IPv6的NAT关键在于

  • 设置br-lan端口的ipv6的网段/前缀
  • 设置ip6tables规则,将br-lan网段的数据包通过snat地址转换后发出

1. nat6配置


  1. 开启config配置
CONFIG_PACKAGE_ip6tables=y
CONFIG_PACKAGE_kmod-ipt-nat6=y
  1. 配置给br-lan端口的ipv6地址前缀,在netifd里面已经实现了一个uci配置值ula_prefix,将/etc/config/network里面的ula_prefix设置成fd开头的内网地址
config globals 'globals'
    option ula_prefix 'fd00:eeee:eeee::/48' 
  1. odhcpd服务器需要添加两个参数配置,ra_management和ra_default
config dhcp 'lan'
    option interface 'lan'
    option start '100'
    option limit '150'
    option leasetime '12h'
    option dhcpv6 'server'
    option ra 'server'
    option ra_management '1'
    option ra_default '1'

这两个值的含义在官网有给出解释

  • ra_default设置成:总是通知默认路由器
  • ra_management设置成:“NDP-Proxy” is disabled
ra_default  integer 0           Override default route
                                0: default 
                                1: ignore no public address 
                                2: ignore all
ra_management   integer 1       RA management mode
                                0: no M-Flag but A-Flag
                                1: both M and A 
                                2: M but not A
  1. 根据对应的wan口interface添加br-lan网段的ip6tables规则
ip6tables -t nat "POSTROUTING" -s "$ula_prefix" -j MASQUERADE

对于ip6tables规则的设置,需要做一些校验等,这个在openwrt官网里面有给出一个方案就是masq6功能

原理相当于在firewall下面添加masq6开启的配置,然后添加nat6配置条目,当启动防火墙的时候会主动将/etc/firewall.nat6脚本拉起来。

# /etc/config/firewall
config zone
        option name 'wan'
        ...
        option masq6 '1'          # Enable masquerading NAT6
        option masq6_privacy '1'  # Optionally enable IPv6 privacy extensions

config include 'nat6'
        option path '/etc/firewall.nat6'
        option reload '1'

/etc/firewall.nat6脚本里面就是实现ip6tables规则的设置等判断,脚本位于https://github.com/akatrevorjay/openwrt-masq6里面

#!/bin/sh
#
# Masquerading nat6 firewall.d script.
#
# Place as: /etc/firewall.d/with_reload/90-nat6.fw and make it executable.
#
# 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 
#

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_ensure_append() {
    if ! ip6t -C "$@" >/dev/null 2>&1; then
        ip6t -A "$@"
    fi
}

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."

    if ! ip6t_ensure_append "$postrouting_chain" -t nat -s "$ula_prefix" -j MASQUERADE; then
        # Some releases of OpenWrt just leave the nat table empty for some reason (version dependent?)
        log "Could not find table=\"$postrouting_chain\", but yolo so adding to POSTROUTING directly."
        ip6t_ensure_append "POSTROUTING" -t nat -s "$ula_prefix" -j MASQUERADE
    fi

    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() {
    config_load firewall
    config_foreach handle_zone zone
}

main "$@"
  1. 为了方便启动和停止nat6,添加脚本/etc/init.d/znat6
#!/bin/sh /etc/rc.common                                             
start()
{
    local ipv6_enabled=$(uci -q get network.wan6.web_enabled)
    if [ $ipv6_enabled == 1 ]; then                                 
        # Set the DHCPv6 server to always announce default router.
        uci set dhcp.lan.ra_management='1'
        uci set dhcp.lan.ra_default="1"
        uci commit dhcp
        /etc/init.d/odhcpd restart
        
        # Enable the new masq6 option in your firewall on your upstream zone.
        uci set firewall.wan.masq6='1'
        uci set firewall.wan.masq6_privacy='1'                      
        
        # Since masquerading is enabled, disable the redundant firewall rule ...Allow-ICMPv6-Forward....
        uci set firewall.@rule["$(uci show firewall | grep 'Allow-ICMPv6-Forward' | cut -d'[' -f2 | cut -d']' -f1)"].enabled='0'                                                                             
        # Include the NAT6 firewall script in the configuration.
        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                
    fi         
}                                                                    
stop()
{
    # reset the DHCPv6 server info
    uci -q delete dhcp.lan.ra_management
    uci -q delete dhcp.lan.ra_default
    uci commit dhcp
    /etc/init.d/odhcpd restart
    
    # enable Allow-ICMPv6-Forward
    uci set firewall.@rule["$(uci show firewall | grep 'Allow-ICMPv6-Forward' | cut -d'[' -f2 | cut -d']' -f1)"].enabled='1'
    
    # disnable the new masq6 option in your firewall on your upstream zone.
    uci set firewall.wan.masq6='0'
    uci set firewall.wan.masq6_privacy='0' 
    uci commit firewall
    /etc/init.d/firewall restart      
}

查看nat6的firewall.nat6脚本可以发现最上面设置了set -eo pipefail这么一条语句,就是指令执行有出错的时候就直接返回,不支持了。

2. nat6测试


在公司网络测试,公司是pppoe-wan拨号的,所以odhcp6c设置成dhcpv6,ifname为pppoe-wan

config interface 'wan6'
        option def_ifname 'eth1'
        option dhcpv6_peerdns '1'
        option pppoev6_useipv4info '1'
        option pppoev6_peerdns '1'
        option web_enabled '1'
        option web_proto 'nat6'
        option proto 'dhcpv6'
        option ifname 'pppoe-wan'

nat6也sdtart,所有都配置好后,重启/etc/init.d/network

发现pppoe-wan口可以获取到ipv6地址,br-lan也设置了ipv6地址,但是用www.test-ipv6.com测试发现一只通过不了。

root@zihome:/# ip -6 route
default from :: via fe80::da86:8eff:febd:4 dev pppoe-wan  proto static  metric 1024 
default from 240e:fa:a8:87b5::/64 via fe80::da86:8eff:febd:4 dev pppoe-wan  proto static  metric 1024 
240e:fa:a8:87b5::/64 dev pppoe-wan  proto static  metric 256 
fd00:6885:6885::/64 dev br-lan  proto static  metric 1024 
unreachable fd00:6885:6885::/64 dev lo  proto static  metric 2147483647  error -128
fe80::/64 dev br-lan  proto kernel  metric 256 
fe80::/64 dev eth1  proto kernel  metric 256 
fe80::/10 dev pppoe-wan  metric 1 
fe80::/10 dev pppoe-wan  proto kernel  metric 256 

查看默认入网发现少了pppoe-wan默认网关的路由,这种情况下可以手动添加默认路由

route -A inet6 add default gw fe80::da86:8eff:febd:4 dev pppoe-wan

添加后www.test-ipv6.com就可以测试通过,但是使用这种方法要去维护这个默认路由ifup的时候 添加,ifdown的时候删除,总是会有一些问题。

最后发现一开始没有默认路由,但是等了5分钟后发现又有了,查看syslog发现原本一直没有获取到ipv6的dns,等了几分钟后获取到了dns信息,默认路由就有了。

syslog如下:

Wed Sep 23 18:00:32 2020 daemon.info dnsmasq[13935]: reading /tmp/resolv.conf.auto
Wed Sep 23 18:00:32 2020 daemon.info dnsmasq[13935]: using local addresses only for domain lan
Wed Sep 23 18:00:32 2020 daemon.info dnsmasq[13935]: using nameserver 202.96.134.33#53
Wed Sep 23 18:00:32 2020 daemon.info dnsmasq[13935]: using nameserver 202.96.128.86#53
Wed Sep 23 18:00:32 2020 daemon.info dnsmasq[13935]: using nameserver 240e:1f:1::1#53
Wed Sep 23 18:00:32 2020 user.notice firewall: Reloading firewall due to ifupdate of wan6 (pppoe-wan)

/tmp/resolv.conf.auto多出了ipv6的dns

root@zihome:/# cat /tmp/resolv.conf.auto 
# Interface wan
nameserver 202.96.134.33
nameserver 202.96.128.86
# Interface wan6
nameserver 240e:1f:1::1

ip -6 route多出了默认路由

root@zihome:/# ip -6 route
default from :: via fe80::da86:8eff:febd:4 dev pppoe-wan  proto static  metric 1024 
default from 240e:fa:a8:87b5::/64 via fe80::da86:8eff:febd:4 dev pppoe-wan  proto static  metric 1024 
240e:fa:a8:87b5::/64 dev pppoe-wan  proto static  metric 256 
fd00:6885:6885::/64 dev br-lan  proto static  metric 1024 
unreachable fd00:6885:6885::/64 dev lo  proto static  metric 2147483647  error -128
fe80::/64 dev br-lan  proto kernel  metric 256 
fe80::/64 dev eth1  proto kernel  metric 256 
fe80::/10 dev pppoe-wan  metric 1 
fe80::/10 dev pppoe-wan  proto kernel  metric 256 
default via fe80::da86:8eff:febd:4 dev pppoe-wan  proto ra  metric 1024  expires 1671sec

后面一切就都正常了,可是为什么要等5分钟后才可以获取到dns呢,分配ipv6 dhcp的时候咋没有呢。

测试发现两台自己的路由器相接,可以很快的获取到nds信息,应该是上级光猫没下发,获取到另一个ipv6地址的时候才下发nds成功,一开始只有一个ipv6地址

root@zihome:/# ifconfig pppoe-wan
pppoe-wan Link encap:Point-to-Point Protocol  
          inet addr:183.49.45.127  P-t-P:183.49.44.1  Mask:255.255.255.255
          inet6 addr: 240e:fa:a8:87b5:1111:c06e:8b81:b6ad/64 Scope:Global
          inet6 addr: 240e:fa:a8:87b5:1111:6841:4c33:1a88/64 Scope:Global
          inet6 addr: fe80::1d6e:c06e:8b81:b6ad/10 Scope:Link
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1480  Metric:1
          RX packets:10008038 errors:0 dropped:0 overruns:0 frame:0
          TX packets:9073006 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:3 
          RX bytes:8745492897 (8.1 GiB)  TX bytes:2913346015 (2.7 GiB)

Openwrt配置NAT6:
https://www.cnblogs.com/Arago/p/7765873.html

openwrt的官方手册如下:
https://openwrt.org/docs/guide-user/network/ipv6/ipv6.nat6#ula_prefix

深圳电信开启IPv6支持:
https://blog.yiwei.li/%E6%B7%B1%E5%9C%B3%E7%94%B5%E4%BF%A1%E5%BC%80%E5%90%AFipv6%E6%94%AF%E6%8C%81/

你可能感兴趣的:(4-Openwrt ipv6之NAT6)