Mikrotik RouterOS 防火墙配置参考---通过 MAC 地址获取IPv6并更新防火墙设置
在我将主路由改为RouterOS后,顺道开启了IPV6的直通访问,但是初始配置一直有一个坑没填完;那就是内网设备获取的V6地址如何在防火墙策略上自动更新,找了下有大神实现了在Route人OS上用脚本统一更新DDNS和防火墙策略,看着比较简洁,但是我个人倾向于网关设备最小化功能的需求,所以将DDNS服务还是部署在了内网发布服务的主机上,暂时还不完美凑活着先用吧。以后有时间再改
那先说这个脚本的需求:
- 运行在RouterOS上,自动获取指定内网设备的IPv6地址并自动配置对应防火墙策略;具有自动更新、定时检查等功能;
1.开始之前
RouterOS 脚本使用的是专为 MikroTik 路由器设计的特定脚本语言,旨在配置和自动化各种任务。这种语言与常规编程语言有所不同,其语法和功能集专为满足 RouterOS 的需求而设计。
在开始使用之前,了解基本语法是非常重要的。可以参考 Scripting - RouterOS 来获取详细信息。幸运的是,这种语法相对简单易懂。
2. 脚本思路及编写
- 首先要获取到内网设备的IPv6地址;
- 更新防火墙策略;
- 定时检查及更新数据。
2.1 获取内网指定MAC地址机器的V6地址列表
通过查找文档找到了获取所有内网设备的IPv6地址的位置是:IPv6 -> Neighbors目录。参考命令如下
首先指定MAC地址
:local targetMac "11:22:33:44:55:66"
通过MAC地址筛选内网IPv6地址
:local ipAddressList [/ipv6 neighbor find mac-address=$targetMac]
以上,我们获取到指定设备的Neighbor Id,这是个数组,因为设备的IPv6可能是多个。
2.2 获取设备公网IPv6地址
由于公网IPv6地址的下发是基于运营商提供的IPv6前缀,重启路由器后,之前通过老前缀下发的公网IPv6地址并不会立即消失,而是会存活一段时间。在这种情况下,我们需要确保上报的是新前缀下发的地址,而不是已经不再使用的IPv6地址。
此外,路由器还会下发一个以 fe80
开头的内网地址(也可能是其他前缀)。因此,我们需要仔细甄别出正确的公网地址,以避免混淆。
获取下发的IPv6地址前缀
首先定义自己的需更新的拨号接口
:local wanInterface "pppoe-out1"
: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的地址$ipv6Address;
}
3. 防火墙设置
- 定义一个对应内网设备的Address Lists;使用Comment区分设备;
- 设置了一个 forward 规则,并允许目标地址为 allow_device 的列表,action 为 accept 即为允许。
- 更新内网指定comment的IPV6地址。
参考代码如下:
# 自定义设备的注释
:local commentName "FNAS"
# 获取防火墙IPv6地址
:local firewallAddressList [/ipv6 firewall address-list find]
# 需要替换的防火墙列表序号
:local firewallAddressIdx ""
# 循环查找到需要替换的序号
:foreach idxId in=$firewallAddressList do={
# 获取邻居的IPv6地址和MAC地址
# :local ipv6Address [/ipv6 firewall address-list get $idxId address]
:local comment [/ipv6 firewall address-list get $idxId comment]
:if ($comment = $commentName && [:len $commentName] > 0 && [:len $comment] > 0) do={
#:set firewallAddressIdx $idxId;
}
}
通过以上步骤就能拿到我们刚刚配置的注释为 FNAS 的条目序号
获取更新地址时同时更新防火墙地址列表就行。
:if ([:len $firewallAddressIdx] > 0) do={
# 更新防火墙列表
/ipv6 firewall address-list set numbers=$firewallAddressIdx address=$ipv6Report
:log info ("Firewall list has been updated: " . $commentName)
}
4.完整代码
####定义区,仅需在此配置下需要定义的内容######################
# 在这里替换成你需要的内网设备的MAC地址
:local targetMac "11:22:33:44:AA:BB"
# 定义自己的需更新的拨号接口
:local wanInterface "pppoe-out1"
# 自定义设备的注释
:local commentName "FNAS"
###### 分隔符,以下不需要做任何操作 ######
# 获取防火墙IPv6地址
:local firewallAddressList [/ipv6 firewall address-list find]
# 获取下发的IPv6地址前缀
:local ipv6Prefix [/ipv6 dhcp-client get [find interface=$wanInterface status=bound] prefix]
# 获取IPv6 并截取前缀
:local cidrNetwork [:pick $ipv6Prefix 0 ([:find $ipv6Prefix "::" -1] + 1)]
# 存储最终上报的IPv6地址
:local ipv6Report ""
# 查找指定MAC地址的设备
:local ipAddressList [/ipv6 neighbor find mac-address=$targetMac]
# 循环遍历查找对应的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)
}
}
# 需要替换的防火墙列表序号
:local firewallAddressIdx ""
# 循环查找到需要替换的序号
:foreach idxId in=$firewallAddressList do={
# 获取邻居的IPv6地址和MAC地址
# :local ipv6Address [/ipv6 firewall address-list get $idxId address]
:local comment [/ipv6 firewall address-list get $idxId comment]
:if ($comment = $commentName && [:len $commentName] > 0 && [:len $comment] > 0) do={
# 获取到序号
:set firewallAddressIdx $idxId;
}
}
# 更新ip地址
:if ([:len $firewallAddressIdx] > 0 && [:len $ipv6Report] > 0) do={
# 更新防火墙列表
/ipv6 firewall address-list set numbers=$firewallAddressIdx address=$ipv6Report
:log info ("Firewall list has been updated: " . $commentName)
} else={
:log warning "No valid IPv6 address found for updating the firewall list."
}