一波三折的校园网IPv6 x 华硕 AC86U
购买理由
开学搬到了新宿舍,一看网口是千兆的 ,再加上自己之前也买了小米台灯,开关等,还有远程访问实验室NAS和游戏需求,开始寻思买一台高性能路由器。被张大妈各种毒文案例,就像四千预算进卡吧一样,路由器预算一度从200块的TP-Link路由器加钱到了AC86U。终于在双十一之前忍不住剁手。
买回来火速设置一波,第一次用这么高级的路由器有点小激动 。首当其冲就是刷入梅林,设置IPv6,结果踏入了一个深坑。这里先插一句,关于梅林固件,作者的网站是。从这个网站下载的是原版梅林固件。固件代码都是开源的,可以从Github上看到。接下来步入正题。
坑爹的校园网
AC86U支持的设置IPv6方式有下图几种,理论上覆盖了绝大多数网络情况,有很多学校的同学直接选择Passthrough就可以使用。
如果你所在学校可以通过上述任意一种方法正常使用IPv6,那很好,这篇文章就不用再看啦。(当然你也可以来感受一下这个姿势,说不定也想试试呢 )。但是,凡事就怕但是,如果上面的姿势都不管用,那么或许这篇文章就能帮到你啦。
我所在学校宿舍的校园网用了一种叫做DHCPv6的姿势来给大家分配校园网,而且还只给每一个客户端分配一个IP,天啊,IPv6那么多地址,为什么只给一个!于是就悲剧了,路由器自带的IPv6选项在我的网络情况下都不可用。那么第一步就是确定你的网络环境是什么。最简单的方法就是打开你的网络设置,如果你发现你的IPv6前缀是128位,又或者如果你发现路由器自带的IPv6选项在你的网络环境中都用不了,那么就很有可能是跟我一样的网络环境了。
在这种网络环境下,除了NAT这样比较不符合IPv6精神的做法之外,唯一可用的就是桥接模式。也就是让路由器在IPv6网络上像一台交换机一样工作,这样就能无缝接入校园网IPv6啦。网上有很多配置桥接模式的方法,AC86U也十分简单,通过ssh(关于如何ssh站内已经有很多文章介绍了,我就不重复说明啦)登陆到路由器上,在/jffs/scripts/wan-start脚本中添加下面两行命令就可以轻松搞定~
ebtables -t broute -A BROUTING -i eth0 -p ! ipv6 -j DROP
brctl addif br0 eth0
重启路由之后就可以开心的使用IPv6啦!但是!这个时候路由器就没办法获取IPv6了,你的NAS只有IPv6地址,又想在路由器上用,就很尴尬了,所以需要有一个特殊姿势让路由器在桥接模式下也获取到IPv6。由于之前在学校只学过理论知识,只知道理论可行,但是对实现不了解。恰好因为华硕路由器使用了openwrt,由于开源协议的限制,华硕自己的固件也要开源(所以才有了梅林),这样一来就可以通过围观固件源码的方法搞定这件事情了!下面就以AC86U为对象,介绍怎么在开启IPv6桥接模式的情况下为路由器获取IPv6地址。
首先要做一些准备工作:
将路由器的 IPv6 设置为 passthrough。然后通过 ssh 登陆路由器,运行下面这行命令
ps | grep odhcp6c
你应该会看到有类似这样一行输出
odhcp6c -df -R -s /tmp/dhcp6c -N try -c 000300010123d231c3a3 -r23 -r24 -r82 -r83 br0
其中有一串字符 000300010123d231c3a3,这串字符是你的设备ID,记下来这串字符,稍后要用到。
然后关闭路由器的IPv6功能。复制下面这一大长串脚本到你的/jffs/scripts/wan-start脚本中,一定要记得把最后一行的设备ID替换成你在准备工作里面获取到的ID,保存!重启!你会发现路由器自己也获取到IPv6地址了,LAN里的设备也正常获取到了IPv6地址!
#!/bin/sh
# IPv6 bridge
ebtables -t broute -A BROUTING -i eth0 -p ! ipv6 -j DROP
brctl addif br0 eth0
# enable IPv6 on eth0
echo 0 > /proc/sys/net/ipv6/conf/eth0/disable_ipv6
echo 2 > /proc/sys/net/ipv6/conf/eth0/accept_dad
echo 2 > /proc/sys/net/ipv6/conf/eth0/dad_transmits
echo 1 > /proc/sys/net/ipv6/conf/eth0/accept_ra
echo 0 > /proc/sys/net/ipv6/conf/eth0/forwarding
# see lan.c config_ipv6
echo 0 > /proc/sys/net/ipv6/conf/br0/disable_ipv6
echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6
echo 0 > /proc/sys/net/ipv6/conf/default/disable_ipv6
echo 2 > /proc/sys/net/ipv6/conf/br0/accept_dad
echo 2 > /proc/sys/net/ipv6/conf/br0/dad_transmits
# set_default_accept_ra
echo 1 > /proc/sys/net/ipv6/conf/all/accept_ra
echo 1 > /proc/sys/net/ipv6/conf/default/accept_ra
echo 0 > /proc/sys/net/ipv6/conf/all/forwarding
# Allow router get IPv6 Address
# When user disable IPv6, system will set ip6tables ALL policy to DROP
# wait after that, then set our firewall
sleep 10
# set up firewall
ip6tables -P INPUT ACCEPT
ip6tables -P OUTPUT ACCEPT
ip6tables -A OUTPUT -p tcp -j ACCEPT
ip6tables -A OUTPUT -p udp -j ACCEPT
# input rules
ip6tables -A INPUT -p ipv6-crypt -j ACCEPT
ip6tables -A INPUT -p ipv6-auth -j ACCEPT
ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
ip6tables -A INPUT -m state --state NEW -j ACCEPT
ip6tables -A INPUT -m state --state INVALID -j DROP
# allow DHCPv6
ip6tables -A INPUT -p udp --sport 547 --dport 546 -j ACCEPT
ip6tables -A INPUT -p udp --sport 500 --dport 500 -j ACCEPT
ip6tables -A INPUT -p udp --sport 4500 --dport 4500 -j ACCEPT
# allow ipv6-icmp related packet
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type router-solicitation -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type time-exceeded -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type parameter-problem -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type echo-request -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type echo-reply -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 130 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 131 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 132 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type router-solicitation -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type router-advertisement -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type neighbour-solicitation -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type neighbour-advertisement -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 141 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 142 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 143 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 148 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 149 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 151 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 152 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 153 -j ACCEPT
ip6tables -A INPUT -j DROP
# Start DHCPv6 for LAN on br0
# In my enviroment, IPv6 address spwan from a DHCPv6 server
odhcp6c -df -R -s /tmp/dhcp6c -N try -c 你的设备ID -r23 -r24 -r82 -r83 br0
张大妈没有贴代码的选项,大家凑合看吧 。
原理解释
这一段较为硬核,需要一定的专业知识,可以跳过不影响你使用。方便好奇的同学了解背后的原理,并根据自己的设备因地制宜的修改设置。
接下来我会解释上面这个脚本在让路由获取IPv6地址的部分过程中都做什么,为什么这样做。在我的网络环境里,只有把路由器设置为 IPv6 passthrough 的时候路由器能正确获取 IPv6 地址。所以需要弄清楚这个模式做了什么操作。好在华硕固件是开源的,可以直接从官网下载。下文的讨论基于写这篇文章时能拿到的最新固件 3.0.0.4.384.32797 的源码。可以到官网支持页面选择操作系统为“其他”就可以看到源码下载链接。由于梅林是基于华硕固件源码的小改版本,也可以直接看梅林仓库的代码。
拿到了源码,直接搜索 PASSTHROUGH 关键字,发现启动 IPv6 Passthrough 的代码在 asuswrt/release/src/router/rc/wan.c下面,快速过一遍 IPv6 相关的代码,发现 IPv6 的设置主要有以下几个方面。
首先是开启IPv6,设置了accept_ra, forwarding一些字段。这些字段的意义都可以 Google 到。比如下面代码中设置的字段是互斥的,一个为1另一个就得为0。这两个字段决定了系统是工作在路由状态还是工作在主机状态。ra 是 router advertisement 的缩写,是 IPv6 网络用来自动配置路由的协议。我们想让路由器在LAN的状态像主机一样获取到 IP,就需要将路由器设置为主机状态,请求路由配置。
case IPV6_PASSTHROUGH:
#endif
ipv6_sysconf(wan_ifname, "accept_ra", 1);
ipv6_sysconf(wan_ifname, "forwarding", 0);
代码中还有很多设置IPv6 相关配置的地方, 分散在各处,就不一一列出来了。总结下来就是分别在 WAN 口 和 LAN 口设置下面的相关参数。相关的代码可以在lan.c 和 wan.c 中找到。下面这段脚本就是用来设置对应参数的。
# enable IPv6 on eth0
echo 0 > /proc/sys/net/ipv6/conf/eth0/disable_ipv6
echo 2 > /proc/sys/net/ipv6/conf/eth0/accept_dad
echo 2 > /proc/sys/net/ipv6/conf/eth0/dad_transmits
echo 1 > /proc/sys/net/ipv6/conf/eth0/accept_ra
echo 0 > /proc/sys/net/ipv6/conf/eth0/forwarding
# see lan.c config_ipv6
echo 0 > /proc/sys/net/ipv6/conf/br0/disable_ipv6
echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6
echo 0 > /proc/sys/net/ipv6/conf/default/disable_ipv6
echo 2 > /proc/sys/net/ipv6/conf/br0/accept_dad
echo 2 > /proc/sys/net/ipv6/conf/br0/dad_transmits
# set_default_accept_ra
echo 1 > /proc/sys/net/ipv6/conf/all/accept_ra
echo 1 > /proc/sys/net/ipv6/conf/default/accept_ra
echo 0 > /proc/sys/net/ipv6/conf/all/forwarding
其次是开启relay功能,顺着 wan.c 再往下看,到了配置 IPv6 passthrough 的代码
case IPV6_PASSTHROUGH:
start_6relayd();
/* fall through */
#endif
case IPV6_NATIVE_DHCP:
start_dhcp6c();
这段代码做了两步操作,第一步是启动了 6relayd ,可以确认华硕是用 6relayd 做的中继,据论坛用户表示该项目不是很稳定,并且在我的网络环境无法实现中继;第二步是启动了 DHCPv6 客户端,为特定的端口分配IP。具体代码实现太长,这里贴出一小部分
int
start_dhcp6c(void)
{
char *wan_ifname = (char *) get_wan6face();
char *dhcp6c_argv[] = { "odhcp6c",
"-df",
"-R",
"-s", "/tmp/dhcp6c",
"-N", "try",
NULL, NULL, /* -c duid */
NULL, NULL, /* -FP len:iaidhex */
NULL, NULL, /* -rdns -rdomain */
NULL, NULL, /* -rsolmaxrt -r infmaxrt */
NULL, /* -v */
NULL, /* interface */
NULL };
int index = 7;
struct duid duid;
char duid_arg[sizeof(duid)*2+1];
char prefix_arg[sizeof("128:xxxxxxxx")];
int service;
...
可以看到路由器使用 odhcp6c 为自己在 WAN 口上获取IP。这个时候可以脑补这段代码获取参数值,这样比较麻烦且容易出错,所以才出现了配置部分一开头的使用ps | grep odhcp6c 查看参数的trick。经过多次验证参数是不变的,所以可以通过这个方法获取启动 odhcp6c 客户端的参数表。有一点主要注意的是默认情况下我们是为 WAN 口绑定 IP,而本文中的网络情况是 WAN 口 与 LAN 口已经被桥接在一起了,在 IPv6 的层面已经没有了内外网的区分。所以应为网桥,也就是 br0 绑定IP。
最后还需要调整防火墙设置,因为当路由器关闭了 IPv6 功能后,会默认过滤丢弃掉所有的 IPv6 数据包,所以还需要设置对应的防火墙规则。设置防火墙规则的代码散落在各处,找起来十分麻烦,且很难找全,所以祭出查看 odhcp6c 参数的方法,通过运行ip6tables -L 查看 filter 表中所有的规则,然后手写一份一样的出来就可以啦,于是就有了上面那一大长串 IPv6 的过滤规则。
最终,通过看源码可以梳理出来路由器在设置 IPv6 passthrough 后的行为如下
修改系统设置,启用所有端口的 IPv6 功能
添加防火墙规则,允许 IPv6 数据包通过路由器
开启 DHCPv6 客户端,为路由器申请IP地址
所以按照这些步骤,就可以自己手动设置桥接后的路由器通过HDCPv6服务申请IPv6 地址,还顺便复习了一波计算机网络的知识2333。
值友1008251829
校验提示文案
宝石蓝的天空
校验提示文案
Raphael韬
校验提示文案
成都刘德华
校验提示文案
值友7882436075
校验提示文案
熟悉地陌生人
校验提示文案
CN_CSQ
校验提示文案
麓仁葭
校验提示文案
米兰的小小铁匠
校验提示文案
Gatsby_Van
校验提示文案
值友8767089484
校验提示文案
Huzour
校验提示文案
Steve_S
校验提示文案
郭致兔
校验提示文案
跑着步的五花肉
校验提示文案
晨曦霞依
校验提示文案
值友8699087951
校验提示文案
constantinelee
校验提示文案
坚0775
校验提示文案
Ray磊
校验提示文案
值友1848123428
校验提示文案
值友1848123428
校验提示文案
Gatsby_Van
校验提示文案
米兰的小小铁匠
校验提示文案
不愿剁手
校验提示文案
值友6797885675
校验提示文案
跑着步的五花肉
校验提示文案
值友8834924290
校验提示文案
I0382
校验提示文案
麓仁葭
校验提示文案
郭致兔
校验提示文案
CN_CSQ
校验提示文案
熟悉地陌生人
校验提示文案
值友7882436075
校验提示文案
大肠杆菌先森
回家里发现移动给了ipv6,也给了pd,但是过了路由器后客户端就是上不了网,心塞。
校验提示文案
成都刘德华
校验提示文案
Steve_S
校验提示文案
Steve_S
校验提示文案
frank-087
校验提示文案
Huzour
校验提示文案