找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
广告投放联系QQ68610888
查看: 6311|回复: 5

关于多线IPv6与mwan3均衡负载,正确进行源地址转换

[复制链接]
发表于 2024-2-19 19:57 | 显示全部楼层 |阅读模式
本帖最后由 阿泥基 于 2024-8-22 03:23 编辑

QQ 上看到群友在讨论爱快收费固件的一个功能——多线 IPv6 均衡负载。
刚开始感到诧异,这个也要收钱?但马上回忆起来,自己早些年也因为这个需求折腾了一段时间,确实也说得过去。
下面把当时做的笔记整理一下,希望能给大家一点解决问题的思路。

※由于当时解决了自己的需求后就再也没有深入,所以方案可能不完美或者有过时、不严谨的地方,敬请见谅!

首先要了解多线 IPv6 与 mwan3 负载均衡实现上的难点。
早期流行一个说法指,IPv6 与 mwan3 不兼容。其实不然,mwan3 很好地完成了它的分内事——负载均衡,但问题出在后面的步骤——源地址转换(SNAT)。
※参照 https://openwrt.org/docs/guide-user/network/wan/multiwan/mwan3#ipv6_support
举个例子,设备以移动宽带的源地址 2409:abcd::1234 发起连接,经过mwan3导流至电信宽带的网关 240e:abcd::1,运营商一看你这源地址我不认识,自然就会把它丢弃,导致无法预期工作。
在 IPv4 环境下,出站接口理所当然地启用了 IP 地址伪装(Masquerading),在进入运营商的网关前就已经把源地址转换为合法地址,因此无需担心上述的问题。
而早期很多使用者甚至开发者认为,IPv6 下仍然使用NAT是一种倒行逆施,对于这类需求多有不屑的态度,因此功能也迟迟未能完善。
虽然这个想法在一些使用者之间仍然根深蒂固,但至少工具已经完成了充分的实现。

最终效果如下
移动前缀 2409 的源地址发起的连接,经过 SNAT 后可正常出站至电信网络



文本就多线 IPv6 经过 mwan3 负载均衡后的关键步骤——源地址转换(SNAT)的3种实现方案进行说明与分析。
一、IP地址伪装(Masquerading)
二、1对1NAT(NAT pooling)
三、前缀转换(NETMAP)

一、IP地址伪装(Masquerading)

上文提到,IPv4 环境下出站接口(防火墙的 WAN 区域)默认启用了 IP 地址伪装,IPv6 同样可以使用这个方法解决负载均衡的源地址问题。
早期 IPv6 的 Masquerading 还只能通过命令行启用,不知哪个版本开始,WEB 管理页上添加了对应的开关。

网络 -- 防火墙 -- WAN 区域 -- 编辑 -- 高级设置--启用 “IPv6 地址伪装”



这个方案的优点是配置简单,单纯的宽带叠加及分流的话,WEB 上点几下就可以解决。
而缺点也很明显,所有设备的出口源地址都会变成相同的,这严重地限制了 IPv6 公网地址的可用性。

二、1对1NAT(NAT pooling)

为了充分发挥 IPv6 公网地址的可用性,我们开始使用基于 nftables 的防火墙脚本实现灵活的配置,请确认你所使用的框架。
在 SSH 下运行 fw4 ,若提示 -ash: fw4: not found 则表示不支持。
fw3 + iptables 或许也能实现类似的效果,但不在本文的讨论范围。

先编辑 /etc/config/firewall ,把nft脚本添加到防火墙
  1. config include
  2.     option path '/usr/nft-nat6.sh'
复制代码
每当网络接口发生变化时都会自动运行脚本,fw4 已不再使用 reload 选项
没有指定 type 选项,默认为 shell 脚本 script 。注意并不是 nft 脚本 nftables ,这可能会与其他防火墙自定义规则的教程稍有不同
/usr/nft-nat6.sh 可改成你实际的脚本路径

编辑防火墙脚本 /usr/nft-nat6.sh
  1. #!/bin/sh
  2. ct=$(ifstatus wanct_6 | jsonfilter -e '@["route"][0].source')
  3. cm=$(ifstatus wancm_6 | jsonfilter -e '@["route"][0].source')
  4. nft add table inet myrules
  5. nft delete chain inet myrules srcnat 2>/dev/null
  6. [ -z "$CT" ] && exit
  7. [ -z "$CM" ] && exit
  8. nft add chain inet myrules srcnat { type nat hook postrouting priority srcnat \; }
  9. nft add rule inet oniicyan SNAT oifname "pppoe-wanct" ip6 saddr $CMIP6 counter snat $CTIP6
  10. nft add rule inet oniicyan SNAT oifname "pppoe-wancm" ip6 saddr $CTIP6 counter snat $CMIP6
  11. nft add rule inet myrules srcnat oifname "pppoe-wanct" ip6 saddr != $ct snat ip6 to $ct
  12. nft add rule inet myrules srcnat oifname "pppoe-wancm" ip6 saddr != $cm snat ip6 to $cm
复制代码

以下对该防火墙脚本进行详细解说

ct=$(ifstatus wanct_6 | jsonfilter -e '@["route"][0].source')
cm=$(ifstatus wancm_6 | jsonfilter -e '@["route"][0].source')

先获取 IPv6 接口的前缀信息。
这里以电信(ct)和移动(cm)的双线为例,注意 wanct_6 wancm_6 要改为你实际的接口名称。可以先在 SSH 下运行小括号内的命令确认能否正确获取前缀,注意是否有前缀长度。
※这部分无法在 nft 脚本下执行,因此防火墙配置中的脚本类型必须是 shell 脚本 script ,而不是 nft 脚本 nftables

nft add table inet myrules
nft add chain inet myrules srcnat { type nat hook postrouting priority srcnat \; }

对表和链进行添加,创建或清空操作。

添加表(add table,没有使用创建(create),因此即使存在同名表也不会报错
该表名称为 myrules,地址族(family)为 inet,即 IPv4 与 IPv6 双栈——虽然本文针对 IPv6,但考虑用户可能有其他自定义规则,将其集中在一个表中方便管理,可根据实际情况修改。
当存在同名表时,该命令不会报错也不会对已有内容进行修改。


删除链(delete chain,首次执行时由于不存在该链,报错是预期情况,因此把它屏蔽。

判断接口是否存在,任意一个接口丢失时在此退出脚本
如果另一个接口丢失了,那就没必要继续后面的步骤。

添加链(create chain,没有使用创建(create),因此即使存在同名表也不会报错
该链位于表 inet myrules 下,名称为 srcnat,类型为 nat,HOOK 点为 postrouting,优先级为 srcnat。

通常网络接口发生变化时,IPv6 前缀信息也会随着改变,因此需要更新已有规则。
直接删除重新添加,简单暴力。

nft add rule inet myrules srcnat oifname "pppoe-wanct" ip6 saddr != $ct snat ip6 to $ct
nft add rule inet myrules srcnat oifname "pppoe-wancm" ip6 saddr != $cm snat ip6 to $cm

在表 inet myrules 的链 srcnat 下添加规则。
当出站设备 pppoe-wanct 的 IPv6 源地址不符合前缀 $ct 时,修改源地址为前缀 $ct 地址池中的随机地址
当出站设备 pppoe-wancm 的 IPv6 源地址不符合前缀 $cm 时,修改源地址为前缀 $cm 地址池中的随机地址

※注意这里的网络设备名称是执行 ifconfig 命令时显示的名称,而非上述 ifstatus 命令中用到的网络接口名称

这里有个重要的表述——地址池中的随机地址
也就是说,当移动宽带源地址 2409::abcd:1234 经 mwan3 导流至电信出口时,源地址可能会变成 240e::aabb:5566 或者其他电信前缀的随机地址,反之亦然。
而当移动宽带源地址 2409::abcd:1234 正常通过移动出口时,源地址则不变,保持为 2409::abcd:1234
这个规则还有一个特性,每次匹配到规则后,都会根据地址池进行随机源地址转换,也就是说每个连接的源地址都会不同,即为 1 对 1 NAT,或者有个更熟悉的叫法——对称型 NAT

另外,删除 ip6 saddr != $ctip6 saddr != $cm 的部分后,将会对该出站设备的所有 IPv6 连接都进行随机源地址转换。
也就是说,即使是移动宽带源地址 2409::abcd:1234 正常通过移动出口时,源地址仍然会变成 2409::aabb:5566 或者其他移动前缀的随机地址,反之亦然。

三、前缀转换(NETMAP)

大家应该也注意到,上面的 1 对 1 NAT 方案比起 IP 地址伪装方案,更加限制了 IPv6 公网地址的可用性。
对称型 NAT 下,连 P2P 穿透都无法实现。(没错,IPv6 也有针对防火墙的穿透场景,这里不赘述)
除非有特殊的安全需求,否则应该没人会用上面的方案。

我们使用多线 IPv6,大多数情况下都希望设备拥有的所有 IPv6 公网地址都可用。
为此,我们真正想实现的是,根据出口修改运营商的前缀,保留设备自己的后缀
为了实现这个目的,需要用到的是基于 NETMAP 的 SNAT。
nftables 的 NETMAP 支持于2020年4月提交,虽然示例中仅展示 IPv4,但实际上同样支持 IPv6。

题外话,在本文撰写时,OPENWRT 官网的 mwan3 文档中已提及 NETMAP,但仍未给出示例。而我当时查阅的时候,几乎没有任何有意义的线索。

以下是示例,与方案二的类似,只是脚本稍微不同
编辑防火墙脚本 /usr/nft-nat6.sh
  1. #!/bin/sh
  2. ct=$(ifstatus wanct_6 | jsonfilter -e '@["route"][0].source')
  3. cm=$(ifstatus wancm_6 | jsonfilter -e '@["route"][0].source')
  4. CTIP6=$(ifstatus wanct_6 | jsonfilter -e '@["ipv6-address"][0]["address"]')
  5. CMIP6=$(ifstatus wancm_6 | jsonfilter -e '@["ipv6-address"][0]["address"]')
  6. nft add table inet myrules
  7. nft delete chain inet myrules srcnat 2>/dev/null
  8. [ -z "$CT" ] && exit
  9. [ -z "$CM" ] && exit
  10. nft add chain inet myrules srcnat { type nat hook postrouting priority srcnat \; }
  11. nft add rule inet myrules srcnat oifname "pppoe-wanct" ip6 saddr $CMIP6 snat $CTIP6 2>/dev/null
  12. nft add rule inet myrules srcnat oifname "pppoe-wancm" ip6 saddr $CTIP6 snat $CMIP6 2>/dev/null
  13. nft add rule inet myrules srcnat oifname "pppoe-wanct" ip6 saddr != $ct snat ip6 saddr map { ::/60 : $ct }
  14. nft add rule inet myrules srcnat oifname "pppoe-wancm" ip6 saddr != $cm snat ip6 saddr map { ::/60 : $cm }
复制代码

以下对该防火墙脚本进行详细解说

※方案二提及的部分不再重复

nft add rule inet myrules srcnat oifname "pppoe-wanct" ip6 saddr != $ct snat ip6 saddr map { ::/60 : $ct }
nft add rule inet myrules srcnat oifname "pppoe-wancm" ip6 saddr != $cm snat to ip6 saddr map { ::/60 : $cm }

在表 inet myrules 的链 srcnat 下添加规则。
当出站设备 pppoe-wanct 的 IPv6 源地址不符合前缀 $ct 时,从网络地址映射表 { ::/60 : $ct } 选择合适的源地址
当出站设备 pppoe-wancm 的 IPv6 源地址不符合前缀 $cm 时,从网络地址映射表 { ::/60 : $cm } 选择合适的源地址

跟方案二中的随机源地址不同,这里是从网络地址映射表中选择合适的源地址进行转换。
也就是说,当移动宽带源地址 2409::abcd:1234 经 mwan3 导流至电信出口时,源地址会变成 240e::abcd:1234仅改变前缀为电信前缀 $ct,与设备相关的后缀不变,反之亦然。

这里有个注意的地方,由于我设置宽带获取的前缀长度是 /60,因此映射表左侧为 ::/60,对应的右侧也是 240e:xxxx::/602409:xxxx::/60
你可以根据自己实际的前缀长度进行修改,或者直接改为 { ::/0 : $ct } { ::/0 : $cm }我也不确定会不会占用多点内存。

跟方案二相同,删除 ip6 saddr != $ctip6 saddr != $cm 的部分后,将会对该出站设备的所有 IPv6 连接都进行源地址转换。
也就是说,当移动宽带源地址 2409::abcd:1234 正常通过移动出口时,源地址会变成 2409::abcd:1234,反之亦然。
很明显,这是多此一举的。

  1. CTIP6=$(ifstatus wanct_6 | jsonfilter -e '@["ipv6-address"][0]["address"]')
  2. CMIP6=$(ifstatus wancm_6 | jsonfilter -e '@["ipv6-address"][0]["address"]')
  3. nft add rule inet myrules srcnat oifname "pppoe-wanct" ip6 saddr $CMIP6 snat $CTIP6 2>/dev/null
  4. nft add rule inet myrules srcnat oifname "pppoe-wancm" ip6 saddr $CTIP6 snat $CMIP6 2>/dev/null
复制代码

比起方案二还多了以上部分。
由于运营商考虑到不支持前缀的设备,除了前缀之外还会提供一个(或多个)单播地址。这些单播地址与前缀不在同一网段,因此需要单独转换。

需要注意的是脚本中只考虑了一个单播地址的情况,若你运营商分配多个地址,请自行适当修改。
此部分并非必要,因为即使没有正确配置,也不影响路由器下面设备的通信。但当路由器本身使用 IPv6 发起连接时,可能会使用单播地址,因此尽可能适配。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×

评分

参与人数 1恩山币 +1 收起 理由
microka + 1 感谢大神无私分享,收藏研究! ...

查看全部评分

只谈技术、莫论政事!(点击见详情) | 恩山无线论坛欢迎您的来访,请互相尊重、友善交流,建议保持一颗平常心看待网友的评论,切勿过度反应。
发表于 2024-2-19 20:11 | 显示全部楼层
虽然我是小白,但是我依然感谢大神分享这个教程
只谈技术、莫论政事!(点击见详情) | 恩山无线论坛欢迎您的来访,请互相尊重、友善交流,建议保持一颗平常心看待网友的评论,切勿过度反应。
回复 支持 反对

使用道具 举报

发表于 2024-4-12 20:13 | 显示全部楼层
感谢大神分享,慢慢思考
只谈技术、莫论政事!(点击见详情) | 恩山无线论坛欢迎您的来访,请互相尊重、友善交流,建议保持一颗平常心看待网友的评论,切勿过度反应。
回复 支持 反对

使用道具 举报

发表于 2024-7-27 00:33 | 显示全部楼层
非常棒的帖子,我有个疑问,如果我的两个wan口 :wana6 和 wanb6 是通过4G模块获取到的ipv6地址,我试了ifstatus wanb6 | jsonfilter -e '@["route"][0].source'
终端返回值:::/0
似乎没有获取前缀,请问我应该如何设置呢? 谢谢楼主!

点评

抱歉没有及时看到回复 通常蜂窝网络只会分配单播地址,尽管地址可能可以透传到 CPE 以下的设备,但一般不会有前缀 这种情况只能使用 IP 地址伪装(Masquerading) 的方案进行负载均衡,也就是出口 IP 只能是固定的  详情 回复 发表于 2024-8-31 21:33
只谈技术、莫论政事!(点击见详情) | 恩山无线论坛欢迎您的来访,请互相尊重、友善交流,建议保持一颗平常心看待网友的评论,切勿过度反应。
回复 支持 反对

使用道具 举报

发表于 2024-8-4 19:37 | 显示全部楼层
感谢大神无私分享,收藏研究!
只谈技术、莫论政事!(点击见详情) | 恩山无线论坛欢迎您的来访,请互相尊重、友善交流,建议保持一颗平常心看待网友的评论,切勿过度反应。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-8-31 21:33 | 显示全部楼层
cyqtmxk 发表于 2024-7-27 00:33
非常棒的帖子,我有个疑问,如果我的两个wan口 :wana6 和 wanb6 是通过4G模块获取到的ipv6地址,我试了ifs ...

抱歉没有及时看到回复

通常蜂窝网络只会分配单播地址,尽管地址可能可以透传到 CPE 以下的设备,但一般不会有前缀
这种情况只能使用 IP 地址伪装(Masquerading) 的方案进行负载均衡,也就是出口 IP 只能是固定的单播地址
这方面跟 IPv4 是一样的,如果需要通过这个地址传入连接的话,需要配置端口转发

前缀转换方案的前提是有足够的地址空间进行映射
只谈技术、莫论政事!(点击见详情) | 恩山无线论坛欢迎您的来访,请互相尊重、友善交流,建议保持一颗平常心看待网友的评论,切勿过度反应。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

欢迎大家光临恩山无线论坛上一条 /1 下一条

有疑问请添加管理员QQ86788181|手机版|小黑屋|Archiver|恩山无线论坛(常州市恩山计算机开发有限公司版权所有) ( 苏ICP备05084872号 )

GMT+8, 2024-9-24 02:33

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

| 江苏省互联网有害信息举报中心 举报信箱:js12377 | @jischina.com.cn 举报电话:025-88802724 本站不良内容举报信箱:68610888@qq.com

快速回复 返回顶部 返回列表