给懒得看的人的快速方案
首先,我采用了 NAT6 的方法。如果你用的旧系统,或新系统上装了 ebtables,则你也可以用网桥+过滤非 IPv6 数据包的方法,但笔者认为不太优雅。后者请自行在网上找方案。中继的方法在南大校园网内不可用,具体原因见下。其次,这里假定用的是最新系统,不保证旧的系统能用。特别注意这里是更新成 DSA 模式后的版本,如果你的系统还在 18.x 时代,那可能并不适用。 然而,如果你用了基于 nftables 和 firewall4 的系统,务必确保你的系统里没有传统的 ip6tables 等和相对应的 kmod-ipt-xxxx!(或者你也可以装上相关的 *-legacy 工具,手动排查冲突——祝你好运!)这些包经常会被作为依赖装进来,比如 MWAN3 就会把传统 netfilter 全家桶装进来,因此务必小心排查(显然,MWAN3 还没有适配好最新的 nftables,因此追新是有代价的)。 这里的配置文件只会保证你的 WAN 口可以获取到能用的 IPv6 地址,在此之上的防火墙配置等请按照自己的系统情况配置。具体的配置(firewall4 的配置相当简单)见后面的节。 配置文件需要这样写:/etc/config/network
# 此文件主要定义接口啥的
# 省略上面的部分
config globals 'globals'
option packet_steering '1'
option ula_prefix 'fd76:d462:c4e3::/48' # 这里可以随便写一个fd00::/8 下的 /48 前缀,用于分配给下层设备
# 中间省略
config interface 'wan6' # 这节应该是已经存在的,那只需要做修改
option device 'wan' # wan 口接口名
option proto 'dhcpv6'
option reqaddress 'try' # 重要
option vendorclass '0000013700084D5346542035102E30'
option reqprefix 'no' # 重要!!
/etc/config/dhcp
里不需要有 wan6
相关的配置。重启 WAN 口,即可获取到 IPv6 地址。如果你的系统是旧版,再参照网上的其它教程就可以了。如果是新版,请继续往下看。环境
南大的 IPv6 环境不可谓不恶劣。其地址分配采用了有状态 DHCPv6,且每个客户端只能拿到单个 IPv6 地址。这和大部分家用宽带可获得 /56 或 /64 前缀的情况完全不同,更遑论使用 DHCP-PD 方式下发前缀或利用 SLAAC 自动管理了。同时,单个 IP 也让通过 ndppd 等 ND 代理方式强行划分子网段的手法变得不可行。 更离谱的是,学校的 IPv4 与 IPv6 均仅允许单 MAC 地址使用单个 IPv4/6 地址。因此,如果单一 MAC 地址两次获取地址,前面获取的地址是不可用的,发的包会被丢掉!这造成普通的 ND/RA/DHCPv6 中继模式也不可用。 另外,南大 IPv6 不需要过 Portal 验证,即不登录也能用。但只有有线网络可以用 IPv6。详细过程和配置
在网上可以了解到,目前有三种在教育网这样的特殊环境配置 OpenWrt 的方法,按资料的量排,分别是 NAT6、ND/RA/DHCPv6 中继与桥接。我三种都折腾了,最后还是用了 NAT6。ND/RA/DHCPv6 中继
这种方式本来应该是最优雅的方式,但在南大是几乎不可用的(原因前面说了),除非伪造 MAC(实际上似乎也不是那么难)。要看懂这种方式,需要了解一下 IPv6 中主机之间、主机与路由器之间是怎么互相发现的。 与 IPv4 的 ARP 协议不同,IPv6 使用基于 ICMPv6 的 Neighbor Solicitation/Advertise (NS/NA) 方式寻找某个 IPv6 地址对应的 MAC 地址。同时路由器的寻找也并非像 IPv4 那样使用 DHCP 上的某个选项实现,而是使用 Router Solication/Advertise (RS/RA) 方式自动寻找。 这样,为了让下层的设备能获取 IPv6 地址,我们只需要把客户端的 DHCPv6 Solicitaion/Request 改写成路由器的 MAC 通过 WAN 转发出来,再将 DHCPv6 Advertise/Reply 转发回去即可。为了让上层设备能看到下层,只需要把上层发到 WAN 口的 Neighbor Solicitation 转发下去,再把下层回复的 Neighbor Advertisement 包的 MAC 改写、转发到 WAN。最后为了让下层设备能往外发包,将 LAN 收到的 Router Solicitation 的 MAC 改写发 WAN 再将上层设备的 Router Advertisement MAC 改写成路由器 LAN 口的 MAC 发给客户端即可。 因此理论上,我们只需要让路由器转发 IPv6 流量,再配置 odhcpd 的 ND/RA/DHCPv6 中继即可实现我们要的功能:# (/etc/config/dhcp)
config dhcp lan
(各种 DHCPv4 设置)
option master '1'
option dhcpv6 'relay'
option ra 'relay'
option ndp 'relay'
option ndproxy_slave '1'
config dhcp wan6
option ignore '1'
option master '1'
option dhcpv6 'relay'
option ra 'relay'
option ndp 'relay'
这样在南大确实所有设备都能获取到 IPv6 地址,但并不能上网。刚开始因为不知道南大有单 MAC 单 IP 限制,因此进行了大量的抓包检测,最后得到单 IP 限制的结论(其实 25 号晚 IPv4 相关的一件事才让此事变得确凿)。这里也分享我抓包的命令(必须在 Windows CMD 里运行,假定你已经装了 Wireshark 在下面说的路径里):ssh root@<路由器的IP> tcpdump -i <设备名 一般抓wan> -U -s0 -w - '<筛选器,可以省>' | "C:\Program Files\Wireshark\Wireshark.exe" -k -i -
其中设备名可以用 ip addr 看,注意不要 @ 后面的部分
例子:ssh root@192.168.1.1 tcpdump -i wan -U -s0 -w - 'ip6' | "C:\Program Files\Wireshark\Wireshark.exe" -k -i -
就假定 OpenWrt 在 192.168.1.1,抓取 WAN 口上的 IPv6 数据包
顺道一提,odhcpd 这个工具的风评一般。旧的 odhcpd 版本有诸多问题,而且网上的配置教程少得可怜。因此自己摸索一下是必须的。 和本方案相关的资料会放在文末的延伸阅读里(包括 ND/SA 等知识)。网桥
这是我个人认为最不优雅的方案,然而也是一个能让所有下层设备都得到 GUA 地址的手段。其基本原理就是把 WAN 也加入原来只有 LAN 口的 br-lan 网桥,相当于把 WAN 口和 LAN 口用交换机连接在一起,再利用 ebtables 过滤掉非 IPv6 的流量(否则局域网 DHCP 会污染外面,且也可能造成路由器挂掉)。这样就实现了类似于“v4 路由,v6 交换”的效果。 然而,根据一位 19 级同学(YDJSIR)的测试结果,这样做造成了巨大的性能损失。同时笔者也认为这种先把所有包串起来,再用防火墙过滤掉的方法很“妖”。无论如何,还是进行了尝试。 这里就是 nftables 第一次背刺我的地方了。这里过滤非 IPv6 使用了 ebtables 里 BROUTE 表的 BROUTING 链具有的特殊行为——在该链被 accept 的包会被转发,被 drop 的包会进入 input 被路由、NAT。因此我们只需要两条命令就可以实现全部操作:ebtables -t broute -A BROUTING -p ! ipv6 -j DROP
brctl addif br-lan wan
当然,每次启动系统都需要这两条,因此需要放启动脚本里。也可以通过配置 LuCI 里网桥的配置和防火墙配置(不知道 firewall3 是怎么配置的)来达到目的。 问题在于——nftables 并没有 BROUTING 链的特殊功能!ebtables-nft 命令的 man page 明确指出了该功能没有实现,同时 netfilter 的 mailing list 上关于此问题的 E-Mail 也最终没有得到任何有效回答。我在 Super User SE 上问了这个问题,也没有啥回答。 因此我最后的方法是——同时安装传统的 ebtables-legacy 包和 nftables 包,用 ebtables-legacy 实现此功能。然而不知道是不是配置错误或内核模块没有正确启动,在这样做后,IPv6 是通了,既可拿 IP 也可访问外网,但整个 IPv4 崩了,显然 ebtables 抽风了。 不过,根据社团网络配置来看,这种配置应当是可以实现的,可能只是我配置的问题,或 ebtables 与 nftables 冲突了。NAT6
因此 NAT6 成了我最后的手段。NAT 为何物想必不用赘述。然而,如果看网上所有有关 NAT6 的教程,会注意到他们全部假定你的路由器 WAN 口有可上网的 IPv6 地址,而这正是南大宿舍楼里直接接 OpenWrt 获取不到的。 调试这个需要了解 OpenWrt 的 DHCP 客户端。其用的是 odhcp6c,OpenWrt 项目自己搓的客户端。在我一波胡乱操作 LuCI 里的 DHCPv6 设置后,我注意到系统日志里打了大量来自 odhcp6c 的“Server returned IA_NA status: No prefix available (NoPrefixAvail)” 错误。再通过抓包发现,路由器同时请求了 IPv6 地址和前缀,而服务器的响应中,地址给了,前缀没给(NoPrefixAvail)。再排查 OpenWrt 如何应用地址,通过ps | grep odhcp6c
发现了: 2895 root 1132 S odhcp6c -s /lib/netifd/dhcpv6.script -Nforce -P0 -t120 wan
(具体的参数记不清了)
这暗示着 odhcp6c 可以直接调用,且问题可能出在 /lib/netifd/dhcpv6.script
上,它很可能有 bug。但我懒得去改这套极端复杂的 Shell 脚本。有没有办法让 odhcp6c 不获取前缀呢?通过手动调用 odhcp6c,我发现不加 -P
参数就行了,再通过 -V
拟态一下 Windows 客户端,就可以获取到 IPv6 地址。 如何在 LuCI 或 UCI(即 /etc/config/network
)中体现这一点呢?答案是:config interface 'wan6'
option device 'wan'
option proto 'dhcpv6'
option reqaddress 'try'
option vendorclass '0000013700084D5346542035102E30'
option reqprefix 'no' # 重要
如果原来没有 wan6
接口,则应直接把这段写进去。重启路由器即可。可以通过 curl 等检查下路由器是否已经可以正常用 IPv6 上外网。 接下来是 NAT6 的配置。首先我们需要配置 DHCPv6 让客户端可以获取到内网 IP 地址。同样在 /etc/config/network 中,我们在 global 段如下配置:config globals 'globals'
option packet_steering '1'
option ula_prefix 'fd76:d462:c4e3::/48'
config interface 'lan'
(省略 v4 部分)
option delegate '0'
option ip6assign '64'
其中的 ula_prefix
可以是 fd00::/8
下的任何一个 /48
前缀,不用和我一样。 再看 /etc/config/dhcp
,需要做如下的配置(如果对注释报错,手动删一下注释,UCI 不一定能正确处理):config dhcp 'lan'
(省略 v4 部分)
option ra 'server'
option dhcpv6 'server'
option ra_management '1'
option ra_default '1' # 重要
config dhcp 'wan'
option interface 'wan'
option ignore '1'
# 不需要 wan6 的部分
config odhcpd 'odhcpd' # 这里就是默认设置
option maindhcp '0'
option leasefile '/tmp/hosts/odhcpd'
option leasetrigger '/usr/sbin/odhcpd-update'
option loglevel '4'
再重启 odhcpd(service odhcpd restart
),即可看看效果。注意上述 ra_default
是必须的,否则 odhcpd 会不向下层设备发送 Router Advertisement,然后往日志里打一大堆“A default route is present but there is no public prefix on br-lan thus we don't announce a default route!”。因此需要用这个开关强制让它发 RA。 最后我们只需要配置防火墙——得益于 firewall4,这个过程在新版 OpenWrt 相当轻松。只需要在 /etc/config/firewall
里原来的 WAN Zone 加一行即可:config zone
option name 'wan'
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
option masq '1'
option mtu_fix '1'
list network 'wan'
list network 'wan6'
option masq6 '1' # 加了这一行!!!
重启防火墙,即可看看是不是客户端也可以正常访问 IPv6 外网了。如果你用的 firewall3,可以去网上找找相应的文章。 2022/06/26 upd: /etc/sysctl.conf
,在其中加上:net.ipv6.conf.wan.accept_ra = 2
重启路由器即可。一些小插曲
在配置好 NAT6 后,我遇到了路由器能正常用 IPv6,但下层的客户端不能的情况——NAT 没有正常运行。经过几小时痛苦的nft monitor trace
后(具体食用方法见 nftables wiki),我最终定位到:包能到达 nat prerouting 链,但既不可进入 mangle input 链,也无法进入 mangle forward 链(见下图)。 在此之上,我一筹莫展了。我一度怀疑我忘了装 NAT6 所必须的内核扩展,因此去编译时的环境看了眼,结果我大呼好家伙——里面不仅没有少扩展(kmod-nft-nat6
),还多了一堆传统的扩展(kmod-ipt-xxxx
)。然而,我并没有装 ip6tables-legacy 之类的旧工具,因此我甚至无法管理这些扩展的规则。这些传统扩展是跟着 ip6tables-nft 等 nftables 系管理工具装进来的,感觉是某种 OpenWrt 上游的 bug。 把这些扩展从编译的包里去掉后,/etc/sysctl.conf
多了一行:net.ipv6.conf.wan.accept_ra = 2
这样似乎才可以让内核里也收到路由器发来的 RA 报文。我这样配置后不知道为什么,还是不工作。因此又在 Super User 和 OpenWrt 论坛里发了两个帖子。SU 上一个评论也提到要用上面的这个开关,因此我晚上回去后再次尝试,居然就成功了。 另一个小插曲是那天我回宿舍发现 IPv4 又挂了。各种 ping 不通,traceroute 也没啥有效结果。最戏剧性的是我跑了一下 ip addr
,发现路由器的 WAN 口有两个 IPv4 地址。结合前面对学校做了 MAC-IP 绑定限制的猜想,用 Wireshark 确定当前发包用的 IP 后删掉这个发包的 IP(也就是切换到 secondary IP),结果居然短暂地恢复的网络。几秒后问题再次出现,再次观测到两个 IP。 显然,这说明:- 实锤了学校限制了一个 MAC 只能用一个 IP,而且是最后一个从 DHCP/DHCPv6 获取的 IP;
- 证明我的 OpenWrt 里有两个 DHCP 客户端,这造成了冲突。
延伸阅读
实际上还参考了其它大量的文章,有的没记起来……也一并致谢!- 南大相关的方案(大量参考借鉴了!)
- 和 ND/RA/DHCPv6 相关的文章
- 讲解 odhcpd 代理 ND/RA 的文章(值得一读):https://blog.icpz.dev/articles/notes/odhcpd-relay-mode-discuss/
- 讲解 ND/RA:https://cshihong.github.io/2018/01/29/IPv6%E9%82%BB%E5%B1%85%E5%8F%91%E7%8E%B0%E5%8D%8F%E8%AE%AE/
- 北大成功配置(北大未名的 BBS 好热闹……对比几乎死掉的南大小百合……羡慕):https://bbs.pku.edu.cn/v2/post-read.php?bid=35&threadid=18202802
- NAT6 配置相关(似乎均基于旧版,和我的方案均不同)
- 总述性文章
- (源站已挂,这是 archive.org 的链接)https://web.archive.org/web/20190313225725/http://blog.kompaz.win/2017/02/22/OpenWRT%20IPv6%20%E9%85%8D%E7%BD%AE/
Comments | 14 条评论
博主 ludoux
谢谢博主。话说我这边还需要给路由器添加默认网关,要不然LAN口仍然无法访问ipv6网站,具体参见 https://lwz322.github.io/2018/10/07/IPv6_NAT.html 里的 “向路由表添加默认网关”。或者执行
ip -6 r add default via
ip -6 route | grep "default from" | awk ‘NR==1{print $5,$6,$7}’“ 。我蛮好奇的,不知道博主拨号后的路由器的默认路由表是什么情况,我和上面这个博文里的情况是类似的。
博主 茶栗
@ludoux 那不应该啊,我路由器里WAN口上前三条路由长这样(应该是自动拿到的):
您可以试试用tcpdump+Wireshark在路由器的WAN口抓包看看有没有正常接受到上游的DHCPv6和Router Advertisement。同时也可能需要微调下OpenWRT里的设置。
另外我的网络是不需要拨号的,插上不需要认证就可以用v6
博主 茶栗
@茶栗 刚刚发现一个Bug导致blog的评论问题出问题了
(´_ゝ`)
看看修好了没博主 ludoux
@茶栗 谢谢博主,懒得再折腾了,我目前是用hotplug来自动插路由表,迂回了一下。今天手贱升级了一下路由器就发现弃用 iptables了,还好看到你的博文,重新把 IPv6 整起来了 :)
博主 茶栗
@ludoux
╮( ̄▽ ̄)╭
确实 折腾这个太花精力了OpenWRT从旧的iptables升级到nft确实变了很多东西,但反正我就是从nftables学起的,反倒看不懂用iptables语法的教程(・_ゝ・)
博主 littlenewton6
我用 ebtables 方案配合虚拟化,实现了 ipv6 上网。我是 esxi 环境,给 openwrt 配置两个 wan 网卡即可。性能消耗不是很大,千兆跑到 800mbps,8600k 消耗 10%
博主 茶栗
@littlenewton6 有意思……我路由器是个MT7621所以连原生上1000都很吃力。
(;´ヮ
)7`给OpenWRT配置两个WAN之后,是通过策略路由来判定v4/v6转发吗?因为如果依赖于 ebtables 的话,也不需要两个WAN口了。现在我这边的问题是nftables没有实现
brouting
表上DROP
的特殊语义,所以需要nftables和传统ebtables共存。就我当时装的版本来看,编译时配置脚本在我选上eftables包的时候会带上一大堆传统的iptables啥的包,感觉冲突的风险不小。但无论如何,后面有时间确实值得试试,因为也有另一位同学用ebtables方案配置成功,我当时配置后v4完全挂掉应该是因为我配置上的问题(
还有一个问题就是,如果通过 ebtables 方案,防火墙能正常按规则过滤包吗
(;¬_¬)
博主 多柴多艺
请问现在NJU WLAN可以获取ipv6了吗 ,以前在校的时候好像只能网线
博主 茶栗
@多柴多艺 还是不可以的,而且学校里很多区域插线也没有的
博主 多柴多艺
@茶栗 可能收费系统还没建好,不然搞个v6代理到家宽岂不是免费上网 不过现在也可以,找个有v6的地方接入设备,在学校其他地方再用v4转发到这个设备就好了,不过这个代价可能就是网速骨折吧🤣
博主 茶栗
@多柴多艺
(〜 ̄△ ̄)〜
插线没有v6是因为这些地方的设备太旧了(10年左右刚修的时候弄的),速度也只有百兆,就不指望了不过一个月20块我也懒得去折腾免费上网了
(´_ゝ`)
不也挺好吗.jpg博主 2605279752
博主好,路由器在南大拿到ipv6后,我想用手机流量访问,电信的流量有ipv6。但是怎么也ping不通,想问下外网直接访问是可以实现的吗
博主 茶栗
@2605279752 办不到。南大的防火墙默认禁止所有传入连接
博主 2605279752
@茶栗 好的谢谢,我traceroute也是显示都没到我路由器就不行了