使用Gentoo Linux手搓路由器之四 -- 支持 IPv6

家庭网络环境下(slaac)的 Linux 路由器 IPv6 的配置

先说大概有内种方案吧

  • NAT 模式: 同 IPv4NAT 一样。这个没有什么好说的,失去 IPv6 的意义,直接不考虑。
  • Relay 模式。这个就是现在 OpenWRT 玩家采用的方案。 系统中自带的 odhcpd 是支持 ra, nd, dhcpv6 relay 。 我看网上有人说不稳定,但是也没有啥证据。 可惜 Linux 没有这个东西,我在网上也找了相关文章,没有找到资料。

    Github 上有一个历史悠久的代码 Menci/magpie 通过抓包实现的 Relay 我试着编译,报错太多了,Fix 难度太大了,直接不考虑。

  • Bridge 模式。 原理大概就是创建一个网桥 brlan (这是个名字,可以改的),把 WANLAN 放到一起,仅让 IPv6 的数据包通过这个网桥, IPv4的数据包依然走原来的 NAT
    • 基于桥接的中继(Relay)

创建网桥

  • netifrc 中创建
    1vim /etc/conf.d/net
    2### 加入创建网桥的配置
    3bridge_brlan="enp1s0f0 enp1s0f1" # enp1s0f1 为 WAN, enp1s0f0 为 LAN 口
    
  • 或者使用脚本创建
    1brctl addif brlan wan
    

脚本创建有一个麻烦,就是不好重复执行脚本,我一般把 iptables 写在脚本里,因为网络配置的需要会经常修改脚本重复执行,所以我会把创建网桥的工作放在 netifrc 启动的时候。

禁止IPv4的数据包通过网桥

1ebtables -t broute -A BROUTING -p ! IPv6 -i ${WAN} --logical-in brlan -j DROP
2ebtables -t filter -A OUTPUT -p ! IPv6 --logical-out ${LAN} -o ${WAN} -j DROP

这里要使用到 ebtables , 网桥更底层, iptables 无法控制。第一条规则: 在 broute 链上 DROP 的数据包并不真正的DROP, 而是使得这个数据包不经过网桥, 原样发到发到原来的接口上。 这样就不会影响到原来的 IPv4 的 NAT 配置。而 IPv6 的数据包会直接经过网桥与 LAN 侧的主机互通了。 第二条规则在 filter 链上, 将 brlan 要发往 WAN 的 非IPv6 的数据包全部丢弃,是为了使得 brlan 在 IPv4 泛洪时不会将 IPv4 的数据包发往 WAN

如果做到这里,你已经可以正常使用 IPv6 了,可是这样会带来一些问题。如:网桥是工作在三层以下,也就是说 IPv6 的流量不会经过路由器的三层,无法配置路由规则和防火墙。

基于网桥的 Relay

因 WAN 和 LAN 在同一个网桥上进去连通的, 所以 NDP, NS,NA 的数据包默认就能正确的被送达。这就相当于实现了一个 NDP Relay。(与手动实现的中继有一点不同,就是这种情况是在同一个网桥内部,桥会直接双向发送 slaac和NDP包,不会改变 MAC 地址, 在 WAN 和 LAN 看来, 他们是直接互通的)。 所以考虑 ICMPv6 的数据包直接网桥通过,而非 ICMPv6 的数据包让它经过三层路由。

BTW: 如果是 WAN 是光猫拨号,且光猫的 DHCPD 无法关闭的情况下,需要在增加一条规则,禁止光猫的DHCPD被广播到 LAN

1ebtables -t broute -A BROUTING -p IPv4 --ip-protocol UDP --ip-source 192.168.1.0/24 --ip-destination-port 67:68 -j DROP
2## --ip-source 就是光猫的 dhcpd 的网段

基于桥接的 Relay

1ebtables -t nat -A PREROUTING -p IPv6 --logical-in ${LAN} --ip6-proto ipv6-icmp --ip6-icmp-type router-solicitation -j ACCEPT
2ebtables -t nat -A PREROUTING -p IPv6 --logical-in ${LAN} --ip6-proto ipv6-icmp --ip6-icmp-type router-advertisement -j ACCEPT
3ebtables -t nat -A PREROUTING -p IPv6 --logical-in ${LAN} --ip6-proto ipv6-icmp --ip6-icmp-type neighbour-solicitation -j ACCEPT
4ebtables -t nat -A PREROUTING -p IPv6 --logical-in ${LAN} --ip6-proto ipv6-icmp --ip6-icmp-type neighbour-advertisement -j ACCEPT
5ebtables -t nat -A PREROUTING -p IPv6 --logical-in ${LAN} -j redirect

前四条规则是让 ICMPv6 的 slaac 和 NDP 数据包通过网桥, 实现 slaac 和 ndp的中继。 第五条规则是让 非 slaac 和 NDP 且由 brlan 进入的 IPv6 的数据包不经过网桥,而是将他们的 MAC 地址改为 brlan,使的这些数据在路由器的 brlan 口上被接收到。 所以需要增加 brlan 到 brlan 的转发

  • 内核参数
    1# net.ipv6.conf.all.forwarding=1
    2sysctl -w net.ipv6.conf.all.forwarding=1
    
  • 转发规则
    1ip6tables -t filter -A FORWARD -i ${LAN} -o ${LAN} -j ACCEPT
    

这样就可以使用防火墙去处理 IPv6 的数据包了

几个内核的参数

参数名 用法
net.ipv6.conf.all.disable_ipv6 是否禁用 IPv6, 0 禁用, 1 开启
net.ipv6.conf.all.forwarding 是否开启转发 0 不开启, 1 开启, 2意思是强制开启转发同时不要自动禁用基于Router Advertisements的Stateless Address Autoconfiguration(如果禁用了的话,远程机器就会把这台设备当作路由器而不是主机,而地址自动配置只会给主机配置,不会给路由器配置)。
net.ipv6.conf.all.accept_ra 接受RA,0 不接受 1 接受(当forwarding禁止时接受) 2 任何情况下都接受(本文作为网关的情况下应该是2,路由器本身也需要IPv6地址的)
net.ipv6.conf.all.max_addresses 故名思义,最大地址数量. 0 不限制,>0 最大值
net.ipv6.conf.all.mtu mtu 值, 一般不需要调整,是否需要调整请查看 《家用宽带IPv6开启后上网变慢的问题
net.ipv6.conf.brlan.use_tempaddr 0 不使用临时地址,1 生成临时地址 2 生成临时地址,并在使用过程中优先使用临时址 (显然2更加保护隐私)

内核参数配置注意点:

如果使用 accept_ra 来配置 Client 端的 IPv6 地址,如果仅仅是使用 net.ipv6.conf.all.accept_ranet.ipv6.conf.default.accept_ra 这两个参数来控制,是指当路由器发送RA的时候系统会根据 RA 的内容来完成地址配置。但是有一种情况,就是客户机本身的变化是需要实现获取RA来调整自身的配置与路由器的网络配置对齐。这个时候需要使用 net.ipv6.conf.${eth?}.accept_ra 来指定网卡,比如网卡被重启,客户机会主动发送 RS 来获取 RA。

使用 tcpdump 抓取 RS / RA

1tcpdump -i ${eth?} -vvvvn icmp6 and 'ip6[40] = 133 or ip6[40] = 134'

Posts in this series