RouterOSv7通过MAC地址获取IPv6并上报至DNSPodCloudFlare
在我将家里的主路由改为RouterOS后,始终找不到一个能完美更新内网设备IPv6的脚本,要么是通过EUI64转换的,要么就是端口转发,还有引入第三个设备,用别的语言去做DDNS的更新,这些方案虽然都能实现最终的效果,但无疑是增加了别的成本,而且不够优雅。RouterOS本身就支持的东西,为什么要舍近求远并?这并不是我的风格。
在网络上搜索了一周后,还真发现了有一个相关的脚本,但是整体看着较乱,作者也是在RouterOS v6上写的,所以也不敢运行。最终各种查阅无果后,只有自己动手写这个脚本了。
那先说这个脚本的需求:
运行在RouterOS上,自动获取指定内网设备的IPv6地址,具有自动更新、定时检查等功能;
1. 开始之前
在开始写之前,我们要知道 RouterOS 脚本使用的是 MikroTik RouterOS 特定的脚本语言,它是一种专为 MikroTik 路由器设备设计的脚本语言。这个脚本语言被用于配置和自动化 MikroTik 路由器上的各种任务和功能。这种脚本语言与一般的编程语言略有不同,具体的语法和功能集是为了满足 MikroTik RouterOS 的特定需求而设计的。所以,在开始之前我们需要了解基本的语法:Scripting - RouterOS ,不过幸运的是,这个语法并不难。
2. 脚本编写
通过上面的需求,我们首先要获取到内网设备的IPv6地址,才有后续的更新操作
2.1 获取Neighbor id
通过查找文档找到了获取所有内网设备的IPv6地址的位置是:IPv6 -> Neighbors目录,那么映射在命令上就是:
:local ipAddressList [/ipv6 neighbor find]
但这段命令获取的是所有设备的IPv6地址,很明显不符合我们的要求,因为我们是指定内网设备,那指定这个内网设备用什么指定?有网络别名、IPv4地址,但以上两个有可能冲突,也有可能变化,唯一不变的就是MAC地址了,那我们指定MAC地址:
# 指定MAC地址
:local targetMac "12:34:56:69:B1:C3"
# 通过MAC地址筛选内网IPv6地址
:local ipAddressList [/ipv6 neighbor find mac-address=$targetMac]
以上,我们就可以获取到指定设备的Neighbor Id了,这是个数组,为什么是一个数组?因为设备的IPv6可能是多个。
2.2 获取设备IPv6地址
我们已经知道获取Neighbor Id,返回给我们的是一个数组,那么我们具体该上报哪一个IPv6地址,就有些说法了,因为IPv6地址的下发,是根据运营商提供的IPv6前缀,有的时候我们重启路由器,通过老前缀下发的IPv6地址是不会立马消失不见的,这个地址还会存活一段时间,但我们肯定不是上报已经不用的IPv6地址,而是新前缀下发的地址。
同时,路由器一般还会下发一个fe80
的内网地址(也可能是别的开头),那这就需要我们甄别哪一个地址才是我们需要上报的DNS的。
# 获取下发的IPv6地址前缀
:local ipv6Prefix [/ipv6 dhcp-client get [find interface=$wanInterface status=bound] prefix]
# 获取IPv6 并截取前缀
:local cidrNetwork [:pick $ipv6Prefix 0 ([:find $ipv6Prefix "::" -1] + 1)]
# 循环遍历查找对应的ipv6地址
:foreach neighborId in=$ipAddressList do={
# 获取邻居的IPv6地址和MAC地址
:local ipv6Address [/ipv6 neighbor get $neighborId address]
:local ipv6Status [/ipv6 neighbor get $neighborId status]
:local foundPosition [:find $ipv6Address $cidrNetwork]
:if ($foundPosition >= 0 && $ipv6Status = "reachable") do={
:set ipv6Report $ipv6Address;
:log info ("use: " . $ipv6Address)
} else={
:log info ("discard: " . $ipv6Address)
}
}
在上面代码中就是解决这个问题,首先去拿拨号接口的地址前缀,在循环设备IPv6地址时进行对比,只有对比成功的地址才会被暂存起来,用于后面的上报。
那么到这里,我们就拿到了后续要上报的设备IPv6地址$ipv6Address
;