一波三折的校园网IPv6 x 华硕 AC86U

小编注:此篇文章来自#原创新人#活动,成功参与活动将获得额外100金币奖励。详细活动规则,请猛戳此链接

购买理由

开学搬到了新宿舍,一看网口是千兆的一波三折的校园网IPv6 x 华硕 AC86U ,再加上自己之前也买了小米台灯,开关等,还有远程访问实验室NAS和游戏需求,开始寻思买一台高性能路由器。被张大妈各种毒文案例,就像四千预算进卡吧一样,路由器预算一度从200块的TP-Link路由器加钱到了AC86U。终于在双十一之前忍不住剁手。

华硕(ASUS)RT-AC86U 游戏路由 2900M双频全千兆低辐射/智能无线游戏路由器/MU-MIMO吃鸡路由/支持AiMesh1099元京东去购买

 买回来火速设置一波,第一次用这么高级的路由器有点小激动一波三折的校园网IPv6 x 华硕 AC86U 。首当其冲就是刷入梅林,设置IPv6,结果踏入了一个深坑。这里先插一句,关于梅林固件,作者的网站是。从这个网站下载的是原版梅林固件。固件代码都是开源的,可以从Github上看到。接下来步入正题。

坑爹的校园网

AC86U支持的设置IPv6方式有下图几种,理论上覆盖了绝大多数网络情况,有很多学校的同学直接选择Passthrough就可以使用。

IPv6 支持姿势IPv6 支持姿势

如果你所在学校可以通过上述任意一种方法正常使用IPv6,那很好,这篇文章就不用再看啦。(当然你也可以来感受一下这个姿势,说不定也想试试呢一波三折的校园网IPv6 x 华硕 AC86U )。但是,凡事就怕但是,如果上面的姿势都不管用,那么或许这篇文章就能帮到你啦。

我所在学校宿舍的校园网用了一种叫做DHCPv6的姿势来给大家分配校园网,而且还只给每一个客户端分配一个IP,天啊,IPv6那么多地址,为什么只给一个!于是就悲剧了,路由器自带的IPv6选项在我的网络情况下都不可用。那么第一步就是确定你的网络环境是什么。最简单的方法就是打开你的网络设置,如果你发现你的IPv6前缀是128位,又或者如果你发现路由器自带的IPv6选项在你的网络环境中都用不了,那么就很有可能是跟我一样的网络环境了。

IPv6 信息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 x 华硕 AC86U 。

原理解释

这一段较为硬核,需要一定的专业知识,可以跳过不影响你使用。方便好奇的同学了解背后的原理,并根据自己的设备因地制宜的修改设置。

接下来我会解释上面这个脚本在让路由获取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 后的行为如下

  1. 修改系统设置,启用所有端口的 IPv6 功能

  2. 添加防火墙规则,允许 IPv6 数据包通过路由器

  3. 开启 DHCPv6 客户端,为路由器申请IP地址

所以按照这些步骤,就可以自己手动设置桥接后的路由器通过HDCPv6服务申请IPv6 地址,还顺便复习了一波计算机网络的知识2333。




   

推荐关注:
购物攻略
话题:购物攻略 +关注
原创新人
话题:原创新人 +关注
家庭Wi-Fi布网
话题:家庭Wi-Fi布网 +关注
路由器
分类:路由器 +关注

提示

鼠标移到标签上方,

尝试关注标签~

评论20

发表评论请 登录
  • 最新
  • 最热
评论举报

请选择举报理由

70 20

关注数量超出限制,请先删除部分内容再尝试

关注数量超出限制,
请先删除部分内容再尝试

登录
注册
用户名/邮箱
密码
验证码
看不清?点击更换
看不清?点击更换 忘记密码?

已收藏
去我的收藏夹 >

已取消收藏
去我的收藏夹 >

小提示