首页
隐私政策
iYoRoy DN42 Network
关于
更多
友情链接
Language
简体中文
English
Search
1
Docker下中心化部署EasyTier
3,970 阅读
2
给Android 4.9内核添加KernelSU支持
2,672 阅读
3
DN42探究日记 - Ep.1 加入DN42网络
941 阅读
4
在TrueNAS上使用Docker安装1Panel
820 阅读
5
记一次为Android 4.9内核的ROM启用erofs支持
753 阅读
Android
运维
NAS
开发
网络技术
专题向研究
DN42
个人ISP
CTF
Kubernetes
网络安全
奇思妙想
物联网
登录
Search
标签搜索
网络技术
BGP
BIRD
Linux
DN42
Android
OSPF
C&C++
Web
AOSP
CTF
网络安全
Docker
iBGP
Windows
MSVC
服务
Kernel
IGP
TrueNAS
神楽悠笙
累计撰写
35
篇文章
累计收到
23
条评论
首页
栏目
Android
运维
NAS
开发
网络技术
专题向研究
DN42
个人ISP
CTF
Kubernetes
网络安全
奇思妙想
物联网
页面
隐私政策
iYoRoy DN42 Network
关于
友情链接
Language
简体中文
English
搜索到
13
篇与
的结果
家庭网络更新 - 猫棒 + 802.11k/v/r 漫游
文章开头部分会解释一下配置猫棒和 BE1000 PPPoE 的流程, 如果您只需要查阅 802.11k/v/r 相关的内容, 可跳转至 「 配置 802.11 k/v/r」 章节 起因是五一的时候在哈尔滨本地自提了一台 Xiaomi BE10000, 因为想玩玩猫棒, 看看能不能将家里的千兆网突破 1 Gbps (cc: 浙江移动烽火光猫获取超密 + G-010S-A 猫棒上网 – 米露小窝). 而且因为我在用 EasyTier 这样的组网工具, 并且需要 WakeOnLan 这样的功能, 再加上家里已有的 Xiaomi AX3600 刷了 ImmortalWRT, 所以综合考虑我还希望能刷 WRT 系列系统, 然后也许能够利用 802.11 k/v/r 来实现自动切换, 达到手动 Mesh 的效果. 找来找去发现 好像只有 BE10000 满足我的需求, 既有 SFP+ 笼, 又可以刷入 QWRT. 暑假带回家了, 是时候折腾一下了() 而且在学校的时候也已经把唯一美中不足的 NFC 碰一碰自动连接修好了 (ref: 为小米BE10000路由器的QWRT适配NFC功能 - 悠笙の开发日记 ), 这台机器已经是完全体啦(点头) 专业名词解释 1. 宽带与光通信 (Fiber & PON) 缩写 全称 中文解释 / 备注 FTTH Fiber To The Home 光纤到户. PON Passive Optical Network 无源光网络. 家庭宽带接入的主流技术. GPON Gigabit-Capable PON 千兆无源光网络. 我们使用的猫棒 (GPON Stick) 即基于此标准. OLT Optical Line Terminal 光线路终端. 运营商局端设备, 负责向下分配光信号. ONU Optical Network Unit 光网络单元. 用户端设备, 如光猫或猫棒. UPC Ultra Physical Contact 超微球面研磨. 常见的光纤接头类型(通常为蓝色端面). APC Angled Physical Contact 斜面物理接触. 常见的光纤接头类型(通常为绿色端面, 带8度斜角). LOID Logical ONU ID 逻辑光网络单元标识符. 运营商用于认证光猫身份的一串字符. PLOAM Physical Layer OAM 物理层操作、管理和维护. 也是一种光猫认证用的密码体制. SN Serial Number 序列号. 硬件设备的唯一出厂编号, 常用于光猫注册. 2. 无线局域网与漫游 (Wi-Fi & Roaming) 缩写 全称 中文解释 / 备注 AP Access Point 无线接入点. 主路由或从路由发射 Wi-Fi 信号的角色. SSID Service Set Identifier 服务集标识符. 即用户看到的 Wi-Fi 名称. BSS Basic Service Set 基础服务集. 单个 AP 及其覆盖下连接设备的集合. BSSID Basic Service Set Identifier 基础服务集标识符. 通常是 AP 无线网卡的 MAC 地址. ESS Extended Service Set 扩展服务集. 多个 BSS 组成的统一网络(同 SSID), 即漫游环境. RRM Radio Resource Management 无线电资源管理 (802.11k). 用于获取周围的 AP 邻居报告. WNM Wireless Network Management 无线网络管理 (802.11v). 允许 AP 给客户端发送漫游引导建议. FT Fast Transition 快速漫游转换 (802.11r). 减少客户端切换 AP 时的握手和认证时间. DS Distribution System 分布式系统. 有线网络主干, ft_over_ds 即通过有线主干交换漫游信息. NAS ID Network Access Server ID 网络接入服务器标识符. 用于在漫游域中唯一标识某个 BSS 节点. SAE Simultaneous Authentication of Equals 对等同时认证. WPA3 的密钥交换协议, 比 WPA2 更安全. PSK Pre-Shared Key 预共享密钥. 家庭 Wi-Fi 最常用的输入密码认证方式. 3. 网络协议与系统设置 (Network & System) 缩写 全称 中文解释 / 备注 PPPoE Point-to-Point Protocol over Ethernet 以太网上的点对点协议. 即我们常用的宽带拨号协议. VLAN / PVID Virtual Local Area Network / Port VLAN ID 虚拟局域网 / 端口默认 VLAN ID. 用于隔离网络流量, 光猫拨号必需. SFP+ Enhanced Small Form-factor Pluggable 增强型小型可插拔光模块接口. 支持最高 10 Gbps 的传输速率. UCI Unified Configuration Interface 统一配置接口. OpenWrt/QWRT 的底层命令行配置系统. LuCI Lua Configuration Interface OpenWrt/QWRT 的 Web 图形化配置界面. DHCP Dynamic Host Configuration Protocol 动态主机配置协议. 用于自动给局域网内的设备分配 IP. L2 Layer 2 数据链路层. 文中的“L2 网段”指同一广播域下的局域网. 准备材料 Xiaomi BE10000 Router Xiaomi AX3600 Router G-010S-A NOKIA 猫棒 散热片 SC/APC 转 SC/UPC 光纤 SC/UPC 光纤适配器 刷机资料 (这部分...自己找吧, 搬运别人的资料不太好. 可以参考下面刷机教程中的固件.) 关于那根 SC/APC 转 SC/UPC 光纤, 它们描述的是光纤接头的规格. 正常的家庭 FTTH 一般用的是 UPC, 是蓝色的接头, 而我们买的猫棒基本上都是 APC 的接头, 是绿色的. 它们的主要差别是末端接头的形状, UPC 的末端是向外微微凸起的, APC 的末端是斜着的切面. 如果运营商 OLT 下行光功率足够强的话直接用 UPC 接入猫棒也是能用的, 但是会有大概 3dB 左右的光衰. 因此, 我还是决定稳一点. 图片来源: 光纤连接器 PC、UPC 、APC主要区别 - 知乎 猫棒是出了名的发热量大, 所以记得贴散热片: 贴完之后我在光猫后台看到的温度大概是 60 ℃ 左右. 给 BE10000 刷机 这里就不多赘述了, 直接参考 【小米万兆路由器刷OpenWrt】小米BE10000|SSH解锁固化|UBoot|iStore商店|多拨|-小米无线路由器及小米网络设备-恩山无线论坛 - Powered by Discuz! 即可. 固件有一些已知的 bug, 比如前端配置的 WiFi 信息无法被正常写入, 会导致整个网络部分无法启动, 随后失联, 最终触发自动 Fallback. 所以下面的一些修改主路由的操作是利用 UCI 指令实现的. 获取运营商 ONU 配置 这一步没有通用教程...上闲鱼或者淘宝买个超密, 记录下 LOID, SN, LOID CheckCode(Password), PLOAM Password, 还有 Internet 部分配置的 VLAN ID. 有的地区可能还需要记录下上行的 Mac 地址. LOID和LOID CheckCode: PLOAM Password: VLAN ID: 一般而言, 运营商会选择用LOID, 配合上可能的 LOID CheckCode(Password), 或者可能使用 PLOAM Password. 请根据你的情况自行判断. 我这边是安徽联通, 只记录了 SN, VLAN ID, LOID 就可以正常工作了. (因为联通的那个猫登不进去所以拿了个移动的猫来截图xD) 有的时候不一定需要超密才能获得这些信息, 某些猫 (比如黑龙江联通, 创维的光猫) 的普通用户就能拿到上述信息. 同时, 记录下 PPPoE 登录用户名和密码: 一般而言, 用户名是公开的, 有些猫的密码经过 F12 大法去除 password 属性也会直接显示, 但是有的猫会回传一些无意义占位符, 这个时候可以打电话给运营商重置密码. 配置 GPON Stick (猫棒) 将 GPON Stick 接入 SFP+ 接口, 如果网口灯一直没有亮起来的话可以尝试在 QWRT -> 网络 -> ECM硬件加速设置 里将 SFP1 和 SFP2 接口的速率设置强制改为 Force 2.5Gbps. (我不能确定哪个对应当前SFP接口, 所以两个都改了), 随后保存并应用, 也许需要重启路由器. 默认的 br-lan IP Prefix 应该是 192.168.1.0/24, 此时保持不变, 访问 192.168.1.10 即可打开光猫的控制台: 找到 GPON ONU 设置, 填入我们刚刚记录的信息: LOID 和 SN 并且启用 VLAN 配置: 勾选互操作兼容模式, 并在 PVID 中填写 VLAN ID. 保存后可能需要重启猫棒. 然后进入状态页, 如果看到 PON认证状态/PON信号状态 为 O5, 则说明猫棒已经完成注册并且正常工作了. 配置拨号上网 BE10000 上默认 WAN 口为 eth4, 而 SFP 端口为 eth5, 因此需要将 WAN 口从 eth4 切换成 eth5. 进入 网络 -> 接口 -> 设备, 找到 br-lan 并将 eth4 加入, 将 eth5 移除; 再进入 接口 页面, 将 WAN 网卡改为 eth5, 并填入刚刚的 PPPoE 账号密码: 保存之后应该能看到 PPPoE 已经成功拨号并且能够正常上网了: (我将整个 br-lan 的网段改成了192.168.3.0/24, 这一步不是必须的) 修改 WiFi 基本信息 实测发现...主路由通过 LuCI 直接修改 WiFi 信息会修改不进去(上文已经提到了), 因此在 SSH 通过 UCI 修改信息: uci set wireless.ath0.ssid='[CENSORED]' uci set wireless.ath0.encryption='psk2+ccmp' uci set wireless.ath0.sae='1' uci set wireless.ath0.key='[CENSORED]' uci set wireless.ath1.ssid='[CENSORED]' uci set wireless.ath1.encryption='psk2+ccmp' uci set wireless.ath1.sae='1' uci set wireless.ath1.key='[CENSORED]' uci set wireless.ath2.ssid='[CENSORED]' uci set wireless.ath2.encryption='psk2+ccmp' uci set wireless.ath2.sae='1' uci set wireless.ath2.key='[CENSORED]' uci commit wireless wifi reload 此处 SSID 为 WiFi 名称, encryption 的值 psk2+ccmp 为 WPA2-PSK/WPA3-SAE Mixed Mode. 对于从路由, 因为要配置漫游, 需要保证两个 WiFi 接入的是同一个 L2 网段, 因此需要关闭从路由的 DHCP, 并且将从路由配置为主路由下的一个设备. 因此对于从路由, 做的配置如下: 将 网络 -> 接口 -> 接口 下的其他所有设备删除, 仅保留 br-lan: 在 网络 -> 接口 -> 设备 下的 br-lan 设备中添加 wan 网口: 给 网络 -> 接口 -> 设备 下的 br-lan 添加 IP 地址: IPv4 地址一定记得填掩码, IPv4 网关填写主路由的 IP. 在 网络 -> 防火墙 -> 常规设置 下, 放行对应的防火墙规则. (btw. 我因为懒得配置详细的规则再加上内网设备应该也不会有太大风险因此全部放行了, 如果有特殊需求的话请不要参考xD) 保存并应用后应该就可以连接主路由并通过设置的子路由 IP 连接了. 在从路由的 网络 -> 无线, 配置各个 SSID 接口的信息和主路由同步: 此时两个 WiFi 应该都能成功连上 (虽然 SSID 相同) 并且都是同一个信道. 配置 802.11 k/v/r 主路由 uci set wireless.ath0.ieee80211k='1' uci set wireless.ath0.rrm_neighbor_report='1' uci set wireless.ath0.rrm_beacon_report='1' uci set wireless.ath0.ieee80211v='1' uci set wireless.ath0.time_advertisement='0' uci set wireless.ath0.wnm_sleep_mode='0' uci set wireless.ath0.bss_transition='1' uci set wireless.ath0.ieee80211r='1' uci set wireless.ath0.nasid='Master_2_4G' uci set wireless.ath0.mobility_domain='cafe' uci set wireless.ath0.reassociation_deadline='1000' uci set wireless.ath0.ft_over_ds='0' uci set wireless.ath0.ft_psk_generate_local='1' uci set wireless.ath1.ieee80211k='1' uci set wireless.ath1.rrm_neighbor_report='1' uci set wireless.ath1.rrm_beacon_report='1' uci set wireless.ath1.ieee80211v='1' uci set wireless.ath1.time_advertisement='0' uci set wireless.ath1.wnm_sleep_mode='0' uci set wireless.ath1.bss_transition='1' uci set wireless.ath1.ieee80211r='1' uci set wireless.ath1.nasid='Master_5G' uci set wireless.ath1.mobility_domain='cafe' uci set wireless.ath1.reassociation_deadline='1000' uci set wireless.ath1.ft_over_ds='0' uci set wireless.ath1.ft_psk_generate_local='1' uci set wireless.ath2.ieee80211k='1' uci set wireless.ath2.rrm_neighbor_report='1' uci set wireless.ath2.rrm_beacon_report='1' uci set wireless.ath2.ieee80211v='1' uci set wireless.ath2.time_advertisement='0' uci set wireless.ath2.wnm_sleep_mode='0' uci set wireless.ath2.bss_transition='1' uci set wireless.ath2.ieee80211r='1' uci set wireless.ath2.nasid='Master_5G2' uci set wireless.ath2.mobility_domain='cafe' uci set wireless.ath2.reassociation_deadline='1000' uci set wireless.ath2.ft_over_ds='0' uci set wireless.ath2.ft_psk_generate_local='1' uci commit wireless wifi reload 需要注意的是, 802.11k/v/r 不是 AP 主动强制客户端漫游, 真正决定是否漫游的仍然是客户端. AP 只是通过这些协议给客户端提供邻居信息、漫游建议和快速重关联能力, 不同手机、电脑、IoT 设备对这些协议的支持程度不同. 上面的配置主要配置了三个无线接口: ath0: 主路由 2.4G ath1: 主路由 5G-1 ath2: 主路由 5G-2 三个接口配置逻辑相同, 只是 nasid 不同. 1. 802.11k: 无线资源测量 / 邻居报告 相关配置: uci set wireless.ath0.ieee80211k='1' uci set wireless.ath0.rrm_neighbor_report='1' uci set wireless.ath0.rrm_beacon_report='1' 1.1 ieee80211k='1' 开启 802.11k Radio Resource Management. 802.11k 的主要作用是让 AP 能够向客户端提供周围 AP 的信息. 客户端不需要盲目扫描所有信道, 而是可以根据 AP 提供的邻居列表, 更快找到适合漫游的目标 AP. 简单理解: 802.11k 让客户端知道“附近还有哪些同 SSID 的 AP 可以切过去”. 没有 802.11k 时, 客户端通常需要自己扫描信道. 扫描过程会消耗时间, 也可能导致短暂卡顿. 开启后, 支持 802.11k 的客户端可以更快获得候选 AP 信息. 1.2 rrm_neighbor_report='1' 开启 邻居报告. 这是 802.11k 中最常用、最关键的能力. AP 会向客户端提供邻近 BSS 的信息, 例如: 邻居 AP 的 BSSID 所在信道 PHY 类型 是否同属于当前 ESS 是否支持相关漫游能力 简单理解: 这个参数让 AP 可以告诉客户端: “你旁边还有这些 AP, 它们在这些信道上. ” 这对多 AP 漫游很重要, 因为客户端不需要从 1 信道扫到 165 信道, 而是可以优先扫描 AP 告诉它的候选信道. 1.3 rrm_beacon_report='1' 开启 Beacon Report 支持. Beacon Report 允许 AP 请求客户端报告它扫描到的 Beacon 信息. 也就是说, 客户端可以告诉 AP: 我看到了哪些 AP 它们的信号强度如何 它们在哪些信道 当前无线环境大概是什么样 简单理解: Neighbor Report 是 AP 告诉客户端附近有什么;Beacon Report 是客户端反过来告诉 AP 它看到了什么. 实际家庭网络里, rrm_neighbor_report 的作用通常更直观;rrm_beacon_report 属于辅助能力, 打开即可. 2. 802.11v: BSS Transition / 漫游引导 相关配置: uci set wireless.ath0.ieee80211v='1' uci set wireless.ath0.time_advertisement='0' uci set wireless.ath0.wnm_sleep_mode='0' uci set wireless.ath0.bss_transition='1' 2.1 ieee80211v='1' 开启 802.11v Wireless Network Management. 802.11v 的作用比较广, 其中和家庭 Wi-Fi 漫游最相关的是 BSS Transition Management. 简单理解: 802.11v 让 AP 可以向客户端提出“建议你切到另一个 AP”的请求. 注意, 这只是建议, 不是强制. 客户端可以接受, 也可以拒绝. 比如客户端还连在主路由上, 但它已经走到从路由附近了. 此时 AP 可以通过 802.11v 通知它: 你现在连这个 AP 信号已经一般了, 可以考虑切到旁边那个 AP. 启用 802.11v 可以改善“黏 AP”的情况, 但不能保证所有设备都会听. 2.2 bss_transition='1' 开启 BSS Transition Management. 这是 802.11v 中最关键的漫游相关功能. 启用后, AP 可以向客户端发送 BSS Transition Management Request, 里面通常会带上推荐的目标 AP 列表. 简单理解: ieee80211v 是打开 802.11v 总开关, bss_transition 是打开真正用于漫游建议的功能. 如果只开 ieee80211v, 但不开 bss_transition, 漫游引导效果可能不完整. 2.3 time_advertisement='0' 关闭 时间通告. 802.11v 里包含 Time Advertisement 功能, AP 可以向客户端广播时间信息. 家庭 Wi-Fi 漫游场景通常不需要 AP 给客户端提供时间同步, 所以这里设置为 0. 2.4 wnm_sleep_mode='0' 关闭 WNM Sleep Mode. WNM Sleep Mode 是 802.11v 的一部分, 主要用于客户端省电. 客户端可以进入一种特殊睡眠状态, AP 帮它保留一些上下文. 家庭路由、多 AP 漫游场景一般不依赖这个功能, 而且部分设备兼容性可能一般, 所以这里关闭. 3. 802.11r: Fast Transition / 快速漫游 相关配置: uci set wireless.ath0.ieee80211r='1' uci set wireless.ath0.mobility_domain='cafe' uci set wireless.ath0.reassociation_deadline='1000' uci set wireless.ath0.ft_over_ds='0' uci set wireless.ath0.ft_psk_generate_local='1' 3.1 ieee80211r='1' 开启 802.11r Fast BSS Transition. 802.11r 的作用是缩短客户端从一个 AP 切换到另一个 AP 时的认证和重关联时间. 普通漫游时, 客户端切换 AP 可能需要重新完成一套认证流程. 802.11r 会提前准备好部分密钥派生流程, 让客户端切换时更快. 它不会决定客户端什么时候漫游, 但当客户端决定要漫游时, 它可以让切换过程更快. 适合场景: 手机在房间之间移动 语音通话 视频会议 游戏 多 AP 同 SSID 环境 需要注意的是, 大多数新设备支持 802.11r 少数老旧或兼容性差的 IoT 设备可能不喜欢 802.11r 如果遇到某些设备无法连接, 可以先怀疑 802.11r 兼容性 3.2 mobility_domain='cafe' 设置 Mobility Domain. Mobility Domain 是 802.11r 的漫游域标识. 只有处在同一个 Mobility Domain 里的 AP, 客户端才会把它们视为同一个快速漫游域. 同一套 SSID 下, 所有要参与 802.11r 快速漫游的 AP, 都应该使用相同的 mobility_domain. 这里使用: uci set wireless.ath0.mobility_domain='cafe' cafe 是一个 16-bit 十六进制值, 刚好是 4 个十六进制字符, 类似于 DEADBEAF 这种 Magic Number. 这个值可以自定义, 例如: mobility_domain='1234' mobility_domain='abcd' mobility_domain='beef' 但需要注意, 同一个漫游网络内必须一致 不同独立网络可以不同 必须是 4 位十六进制字符 3.3 reassociation_deadline='1000' 设置 重关联截止时间. 这个参数表示客户端执行 Fast Transition 重关联时, 允许的最大时间窗口. 单位通常按 TU 理解, 1 TU ≈ 1.024 ms. 1000 大约就是 1 秒左右. 简单理解: 客户端发起快速漫游后, 需要在这个时间窗口内完成重关联. 家庭网络里设置 1000 是比较常见、宽松、稳妥的选择. 太短可能导致部分设备来不及完成切换;太长一般也没有明显收益. 3.4 ft_over_ds='0' 设置 802.11r 的 Fast Transition 方式. 802.11r 有两种常见方式: FT over the Air FT over DS 这里: 使用的是 FT over the Air: uci set wireless.ath0.ft_over_ds='0' (关闭了 ft_over_ds, 也就是启用 FT over the Air). FT over the Air 客户端直接和目标 AP 进行快速切换流程. 这是非专业环境里更常见、更直观的方式. FT over DS 客户端通过当前连接的 AP, 经由分布式系统和目标 AP 交换信息. 联网设备先通过旧 AP 联系新 AP, 再完成切换. 实际家庭 OpenWRT/QWRT 多 AP 环境中, 它并不能提供明显的便利, 反而可能会出现设备兼容性问题. 3.5 ft_psk_generate_local='1' 让本机根据 PSK 本地生成 802.11r 所需密钥. 在 WPA-PSK / SAE Mixed 这类家庭网络场景中, AP 可以根据 Wi-Fi 密码本地生成 Fast Transition 需要的密钥材料. 家庭网络通常没有企业级认证服务器, 所以这个参数直接开启就行. 适用场景: WPA2-PSK WPA2/WPA3 Mixed SAE mixed 视固件支持情况而定 无 RADIUS 的普通家庭网络 4. NAS ID: 每个 BSS 的唯一身份 uci set wireless.ath0.nasid='Master_2_4G' uci set wireless.ath1.nasid='Master_5G' uci set wireless.ath2.nasid='Master_5G2' 4.1 nasid='Master_2_4G' nasid 是 NAS Identifier, 也就是当前 BSS 的身份标识. 在 802.11r 场景里, 它用于区分不同 AP / 不同 BSS. 每个参与漫游的无线接口都应该有唯一的 nasid. 例如主路由: ath0 -> Master_2_4G ath1 -> Master_5G ath2 -> Master_5G2 从路由对应设置为: 2.4G -> Slave_2_4G 5G-1 -> Slave_5G 5G-2 -> Slave_5G2 5. 为什么三个 ath 都要配置同一套参数? 因为 ath0、ath1、ath2 分别是三个不同的无线 BSS, 即使它们广播同一个 SSID, 它们在系统里仍然是三个独立的无线接口, 所以 802.11k/v/r 参数需要分别写到每个接口上. 这部分参数可以概括为: 参数 协议 作用 ieee80211k 802.11k 开启无线资源测量 rrm_neighbor_report 802.11k 允许 AP 提供邻居 AP 列表 rrm_beacon_report 802.11k 允许客户端上报扫描到的 Beacon 信息 ieee80211v 802.11v 开启无线网络管理能力 bss_transition 802.11v 允许 AP 给客户端发送漫游建议 time_advertisement 802.11v 时间通告, 家庭漫游场景通常关闭 wnm_sleep_mode 802.11v WNM 省电模式, 家庭漫游场景通常关闭 ieee80211r 802.11r 开启快速漫游 nasid 802.11r / hostapd 标识当前 BSS, 建议唯一 mobility_domain 802.11r 设置快速漫游域, 所有 AP 保持一致 reassociation_deadline 802.11r 设置快速重关联时间窗口 ft_over_ds 802.11r 选择 FT 方式, 0 表示 FT over the Air ft_psk_generate_local 802.11r 根据 PSK 本地生成 FT 密钥 从路由 直接在 LuCI 启用对应配置即可, 可以参考我的配置: 三个无线接口都得做同样的设置, 同时保证 NAS ID 不同. 频段分析 我家里的网络环境比较复杂, 其中主路由位于客厅正中央, 客厅与厨房和阳台直接连通, 因此主路由的信号可以直接覆盖到这部分; 从路由位于书房, 左右和两个卧室相邻. 书房和客厅由一个很短的走廊连接, 房间 A 与客厅之间间隔卫生间: 其中两个星号分别是 主路由(客厅) 和 从路由(书房). 在各个房间的频段采集结果如下: 主卧 书房 (这里的红色高峰是由从路由发出的) 次卧 可以发现, 主卧的信道还算干净, 书房因为从路由所在所以信号足够强, 而次卧由于干扰严重, 加上信号没那么强, 并且目前我们的两个 AP (两个信号最强的红色的 WiFi) 信道全都挤在一起, 导致实际测速只能达到 80Mbps 左右. 因此, 接下来需要对信道和功率做一些小调整, 并且要配置 802.11 k/v/r. 调优功率和信道 其实主要就是为了将信道区分开, 同时让客户端不要粘在某个特定接入点. 其实这部分我也不能完全说清楚为啥, 也参考了很多 AI 给的配置, 在这里也让 AI 解释一下吧 (逃 1. 频段策略 (Channel): 完全错开, 避免同频干扰 主路由和从路由在所有频段上都选择了完全不重叠的信道, 这是多路由组网中最关键的一步. 2.4G 频段 (主 1 / 从 11): 2.4G 只有 1、6、11 这三个信道是完全互不干扰的. 主从路由分别占用 1 和 11, 确保了两台设备在发射 2.4G 信号时不会“打架”(同频干扰), 保证了智能家居等依赖 2.4G 设备的稳定性. 5G-1 频段 (主 36 / 从 52): 主路由使用了低信道 36, 从路由使用了 DFS 信道 52. 这两个信道在 80MHz 频宽下是完全独立的. 5G-2 频段 (主 149 / 从 157): 两台路由器的高频 5G 同样错开了信道. 总结: 这种空间上的信道隔离, 意味着当设备在主路由和从路由之间移动时, 背景底噪最低, 网络吞吐量能达到最大化. 2. 频宽策略 (Bandwidth): 兼顾稳定性与极致速度 2.4G 采用 20MHz: 这是非常明智的做法. 虽然 40MHz 理论速度更快, 但在 2.4G 这个极度拥挤的频段(微波炉、蓝牙都在用), 开启 40MHz 会成倍增加干扰, 导致频繁断流. 锁定 20MHz 牺牲了极速, 但换来了最大的穿墙稳定性和覆盖范围, 非常适合对速度要求不高的 IoT 智能设备. 5G-1 采用 80MHz: 80MHz 是目前绝大多数手机、电脑的主流甜点频宽, 能够提供极高的内网传输和外网下载速度, 是主力上网频段. 5G-2 采用 40MHz: 这个策略很有意思. 将第二个 5G 频段限制在 40MHz, 一方面可以节省宝贵的无线频谱资源(减少对周围邻居的干扰), 另一方面可以作为高稳定性的备用高速网络, 适合一些不支持 80MHz 频宽的老旧设备, 或者专门用来给某些特定设备做隔离连接. 3. 功率策略 (Tx Power): “弱 2.4G, 强 5G” 这套功率配置是这组参数中最亮眼的地方, 它完美解决了多路由环境下的“设备粘连”(Sticky Client)问题. 2.4G 功率调低 (18 dBm / 20 dBm): 2.4G 信号波长长, 穿墙能力极强. 如果不降低功率, 手机在家里走动时, 会一直死死“咬”住远处的 2.4G 信号不放, 导致网速极慢. 调低主从路由的 2.4G 功率, 可以人为缩小 2.4G 的覆盖圈, 促使设备在信号变弱时尽早断开并寻找更好的信号. 5G 功率拉满 (23 dBm / 24 dBm): 5G 信号穿墙能力弱, 衰减快. 保持高功率可以尽量弥补它的穿墙劣势, 扩大 5G 的高速覆盖范围. 总结: 这种功率差配置, 在物理层面实现了一种天然的“频段引导 (Band Steering)”. 当手机同时收到 2.4G 和 5G 信号时, 高功率的 5G 信号强度很容易超过低功率的 2.4G 信号, 从而让手机心甘情愿地优先连接到速度更快的 5G 网络. 4. 漫游配合 (Roaming) 在上述物理层的频段、频宽和功率都调优到位的基础上, 配合 802.11k/v/r 协议, 形成了一个完美的漫游闭环: 11k (邻居报告) + 11v (BSS 转换管理): 路由器会主动告诉手机“附近还有哪个节点信号更好”, 并建议手机切换. 由于 2.4G 功率被压制, 手机在移动时很容易触发 11v 的切换阈值. 11r (快速转换): 结合统一的 SSID (PINer) 和相同的加密方式, 省去了手机切换路由器时重新输入密码认证的几百毫秒时间, 实现了真正的“无缝”体验(比如微信语音不断线). 相同的 mobility_domain 和唯一的 nasid 也是标准的 11r 配置要求. Ref: Gemini 最终的频段, 频宽, 功率的配置是: 主路由 192.168.3.1 2.4G: channel 1 / 20 MHz / 18 dBm 5G-1: channel 36 / 80 MHz / 24 dBm 5G-2: channel 149 / 40 MHz / 24 dBm 从路由 192.168.3.2 2.4G: channel 11 / 20 MHz / 20 dBm 5G-1: channel 52 / 80 MHz / 23 dBm 5G-2: channel 157 / 40 MHz / 24 dBm 效果 & 后续 最终经过测试, 在客厅, 阳台, 厨房, 卫生间中都是默认连接到主路由, 在次卧中会自动切换到从路由连接, 在书房中关上门也会从主路由切换到从路由. 从信号强度下降到实际切换大概需要 5s 左右. 主卧不能确定, 有的时候会切换有的时候不确定, 但是在 5 次测试中有 3 次都切换了. 插 5Gbps 网口之后测速: 实际测速下行速率能跑到 1336.31 Mbps, 上行速率能跑到 158.16 Mbps. 应该是成功了 (? 但是实际使用体感不算太大, 因为当时测试了很多平台, 很少有平台能够跑满的, 加上现在的一些运营商策略比如 QoS 什么的, 速度很难跑满. 不过如果有 IDM 之类的多线程下载工具应该能发挥更大的作用?
2026年07月11日
13 阅读
0 评论
1 点赞
从零构建跨地域 K3s 集群 - Calico 无封装 CNI
前言 其实老早就想玩玩 K8s 集群了,一直觉得没有足够的知识支撑,玩起来比较的费劲就没尝试。 前段时间好好研究了一下 DN42 和 BGP, OSPF 之类的组网协议,发现现在理解起来不那么费劲了,于是果断上手 K3s( 选择 K3s 而不是 K8s 主要原因还是其轻量化:资源要求低,部署不需要拉一大堆镜像,有国内镜像……总之就是,觉得 K3s 比较符合我的需求。 咱是刚开始研究 K3s 的小白,若有错误还请各位大佬手下留情~ 分析 CNI 组件的选择 我目前的网络架构是这样的: graph TD subgraph ZeroTier Domestic subgraph WDS Gateway <--> VM1 Gateway <--> VM2 end NGB <--> Gateway HFE-NAS <--> Gateway NGB <--> HFE-NAS end subgraph IEPL Global-NIC <==OSPF==> CN-NIC end subgraph ZeroTier Global HKG02 <--> HKG04 TYO <--> HKG04 TYO <--> HKG02 end CN-NIC <--> NGB CN-NIC <--> HFE-NAS CN-NIC <--OSPF--> Gateway Global-NIC <--OSPF--> TYO Global-NIC <--OSPF--> HKG02 Global-NIC <--OSPF--> HKG04 %% 样式定义:设置为橘色背景、加粗边框以代表路由器 classDef router fill:#f96,stroke:#333,stroke-width:2px,font-weight:bold; class Global-NIC,CN-NIC,Gateway router; 其中, WDS 节点是个 ProxmoxVE,下挂多个 VM ,通过 OSPF 广播其 VM 的 IPv4 Prefix 地址,香港节点需要访问到 WDS 节点下挂 VM 时便可以通过加入 OSPF 内网实现多跳可达。这样封装层数也只有1层,不需要担心 MTU 消消乐。 我打算在 WDS 下新开两个 VM 分别用作主控和一个节点(暂且称其为 KubeMaster 、KubeNode-WDS1),然后 HKG04 (暂且称为KubeNode-HKG04) 也当作一个节点接入 K3s。 最简单的方式其实是直接通过 K3s 默认的 Flannel 作为 CNI,但是 Flannel 是基于 VXLAN 的,再套一层我现有的内网的话就会产生如下 MTU 消消乐的情况: 数据包 -> Flannel VXLAN封装 -> ZeroTier封装 -> 物理链路 实际容器间通信可用 MTU 大概得压缩到 1350 甚至更低。因此,我尝试寻找一个能直接基于这套内网工作的 CNI 方案,然后就找到了 Calico。了解下来知道 Calico 是以 BGP 作为底层寻路协议,支持通过 No-Encapsulated 即无封装模式启动,数据包直接交由上层路由器处理路由,因此选择 Calico 作为 CNI 组件。 路由设计 为了保证中间节点的路由器可以知道如何路由 Pod 的 IP,而 KubeMaster 和 KubeNode-WDS1 在 ProxmoxVE 主机下,他们需要跨越整个内网与 HKG04 建立 BGP, 因此这就意味着中间每一级路由都需要学习到完整的 BGP 路由,这样才能打通这样的路由路径: graph LR subgraph WDS KubeMaster KubeNode-WDS1 Gateway end subgraph IEPL CN-Namespace Global-Namespace end KubeNode-WDS1 <--> Gateway KubeMaster <--> Gateway <--> CN-Namespace <--> Global-Namespace <--> HKG04 %% 样式定义:突出显示具备路由功能的节点 classDef router fill:#f96,stroke:#333,stroke-width:2px,font-weight:bold; class Gateway,CN-Namespace,Global-Namespace router; 否则,中间的任何一跳都会因为不认识来源/目标 IP 导致丢包。同时,由于 iBGP 从邻居学到的路由,不能继续传递给下一个 iBGP 邻居的特性,Gateway、CN-Namespace、Global-Namespace 与节点间的 BGP Session 都需要启用 Route Reflector, 否则节点无法正确互相学习到路由。 虽然但是,其实这种架构更适合做 BGP Confederation ( BGP 联邦),但是我现有的网络已经很复杂,再加 BGP 联邦会让后期维护起来比较麻烦,而且我的节点数量也不多,iBGP Full Mesh 的开销还能接受。 绝对不是因为我懒( 所以最终网络路由结构是这样的: graph TB subgraph WDS VM1 VM2 Gateway end subgraph IEPL CN-Namespace Global-Namespace end VM1 <-.Calico iBGP Full Mesh.-> VM2 VM1 <--iBGP Route Reflector--> Gateway VM2 <--iBGP Route Reflector--> Gateway <--iBGP--> CN-Namespace <--iBGP--> Global-Namespace Gateway <--iBGP--> Global-Namespace HKG04 <-.Calico iBGP Full Mesh.-> VM1 Global-Namespace <--iBGP Route Reflector--> HKG04 VM2 <-.Calico iBGP Full Mesh.-> HKG04 classDef router fill:#f96,stroke:#333,stroke-width:2px,font-weight:bold; class Gateway,CN-Namespace,Global-Namespace router; 虚线部分的 BGP Session 是 Calico 自动创建的,实现部分是需要我们手动指派创建的 保留 Calico 自己的 iBGP Full Mesh 是为了后续可扩展性考虑,使得各个节点之间可以尽量通过 ZeroTier P2P 优先建立直连网络,而不是从 Route Reflector 汇聚路由器转发绕一圈。 部署 理清了结构之后部署就很简单了。 开启内核转发并关闭 rp_filter 老生常谈。 echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf echo "net.ipv6.conf.default.forwarding=1" >> /etc/sysctl.conf echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.conf echo "net.ipv4.conf.default.rp_filter=0" >> /etc/sysctl.conf echo "net.ipv4.conf.all.rp_filter=0" >> /etc/sysctl.conf sysctl -p 安装 K3s Master 因为 KubeMaster 主控节点在境内,所以最好配置一下镜像加速: mkdir -p /etc/rancher/k3s cat <<EOF > /etc/rancher/k3s/registries.yaml mirrors: docker.io: endpoint: - "https://docker.m.daocloud.io" quay.io: endpoint: - "https://quay.m.daocloud.io" EOF 使用镜像源安装: curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \ INSTALL_K3S_MIRROR=cn INSTALL_K3S_EXEC=" \ --flannel-backend=none \ --disable-network-policy \ --cluster-cidr=10.42.0.0/16" sh - 需要注意的是要指定 --flannel-backend=none 和 --disable-network-policy 来禁用默认 CNI 组件。 使用 cat /var/lib/rancher/k3s/server/node-token 查看 Token ,并记录下来。 WorkerNode 境内节点配置镜像加速: mkdir -p /etc/rancher/k3s cat <<EOF > /etc/rancher/k3s/registries.yaml mirrors: docker.io: endpoint: - "https://docker.m.daocloud.io" quay.io: endpoint: - "https://quay.m.daocloud.io" EOF 然后使用镜像源安装 K3s 并加入集群: export INSTALL_K3S_MIRROR=cn export K3S_URL=https://<主控节点 IP>:6443 # 换成你的主节点实际IP export K3S_TOKEN=K10...你的TOKEN...::server:xxx # 换成第一步获取的完整TOKEN curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | sh - 这个时候各个节点的状态应该是 NotReady 的,因为缺少 CNI 组件。 安装 Calico 并配置 No-Encap 模式 在主控上手动下载下来 https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/tigera-operator.yaml ,安装 Calico 算子: kubectl create -f tigera-operator.yaml 配置自定义资源,创建一个 custom-resource.yaml 文件: apiVersion: operator.tigera.io/v1 kind: Installation metadata: name: default spec: # 添加镜像注册表配置 registry: quay.m.daocloud.io calicoNetwork: ipPools: - blockSize: 26 cidr: 10.42.0.0/16 encapsulation: None natOutgoing: Enabled nodeSelector: all() 此处通过指定 encapsulation: None 来设置 No-Encap 模式。想要修改 IPv4 CIDR 也可以在这里改。随后 kubectl apply -f custom-resource.yaml 执行安装。使用: kubectl get pods -A -o wide 查看 Pod 状态,等待各个节点拉取完成即可。 配置 BGP 拓扑 节点打标 通过给节点打标来指定 WDS 下的节点全都连接到 WDS 节点的 Gateway 的 BGP,境外节点全部连接 Global Namespace 的 BGP: kubectl label nodes kubemaster region=WDS kubectl label nodes kubenode-wds-1 region=WDS kubectl label nodes kubenode-hkg04 region=Global Calico 配置 编写 yaml 配置文件: apiVersion: crd.projectcalico.org/v1 kind: BGPPeer metadata: name: route-reflector-domestic spec: nodeSelector: region == 'Domestic' # 这部分其实没用上,我原来设计的是 Domestic 区域有个总体的汇聚路由 peerIP: 100.64.0.108 asNumber: 64512 --- apiVersion: crd.projectcalico.org/v1 kind: BGPPeer metadata: name: route-reflector-wds spec: nodeSelector: region == 'WDS' peerIP: 192.168.100.1 asNumber: 64512 --- apiVersion: crd.projectcalico.org/v1 kind: BGPPeer metadata: name: route-reflector-global spec: nodeSelector: region == 'Global' peerIP: 100.64.1.106 asNumber: 64512 这部分的意思是: 所有 region 标签为 Domestic 的节点都添加一个连接到 100.64.0.108 (即境内汇聚路由)的 BGP Session,使用 AS 64512 所有 region 标签为 WDS 的节点都添加一个连接到 192.168.100.1 (即 WDS 节点所有 VM 的 Gateway)的 BGP Session,使用 AS 64512 所有 region 标签为 Global 的节点都添加一个连接到 100.64.1.106 (即境外汇聚路由)的 BGP Session,使用 AS 64512 借此实现上文图示的,所有 WDS 节点下的 VM,包括主控和 KubeNode-WDS1 都接入到 WDS 节点的 Gateway 汇聚路由,境外区域的所有节点都接入到境外部分的汇聚路由。 配置汇聚路由 iBGP 这部分直接写 Bird 配置文件就行了,简单( 这里举几个例子: k3s/ibgp.conf: function is_insider_as(){ if bgp_path.len > 0 && !(bgp_path ~ [= 64512 =]) then { return false; } if net ~ [ 10.42.0.0/16{16,32} ] then { return true; } return false; } template bgp k3sbackbone{ local as K3S_AS; router id INTRA_ROUTER_ID; neighbor as K3S_AS; ipv4{ table intra_table_v4; import filter{ if is_insider_as() then accept; reject; }; export filter{ if is_insider_as() then accept; reject; }; next hop self; extended next hop; }; ipv6{ table intra_table_v6; import filter{ if is_insider_as() then accept; reject; }; export filter{ if is_insider_as() then accept; reject; }; next hop self; }; }; template bgp k3speers{ local as K3S_AS; neighbor as K3S_AS; router id INTRA_ROUTER_ID; rr client; rr cluster id INTRA_ROUTER_ID; ipv4{ table intra_table_v4; import filter{ if is_insider_as() then accept; reject; }; export filter{ if is_insider_as() then accept; reject; }; next hop self; }; ipv6{ table intra_table_v6; import filter{ if is_insider_as() then accept; reject; }; export filter{ if is_insider_as() then accept; reject; }; next hop self; }; }; include "ibgpeers/*"; ibgpeers/backbone-cn.conf: protocol bgp 'k3s_backbone_cn_v4' from k3sbackbone{ neighbor fd18:3e15:61d0:cafe:f001::1; }; ibgpeers/master.conf: protocol bgp 'k3s_master_v4' from k3speers{ neighbor 192.168.100.251; }; 主要是几个汇聚路由之间最好不要开 Route Reflector,以及记得开 next hop self。 全部完成之后使用 kubectl get nodes 应该能看到节点状态都 Ready 了: NAME STATUS ROLES AGE VERSION kubemaster Ready control-plane 2d23h v1.34.5+k3s1 kubenode-hkg04 Ready <none> 11h v1.34.6+k3s1 kubenode-wds-1 Ready <none> 2d7h v1.34.5+k3s1 使用 kubectl get pods -A -o wide 查看 Pods: NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES calico-system calico-kube-controllers-64fc874957-6bdlz 1/1 Running 0 5h38m 10.42.253.136 kubenode-hkg04 <none> <none> calico-system calico-node-2qz82 1/1 Running 0 4h24m 10.2.5.7 kubenode-hkg04 <none> <none> calico-system calico-node-dhl2c 1/1 Running 0 4h24m 192.168.100.251 kubemaster <none> <none> calico-system calico-node-nbpkj 1/1 Running 0 4h23m 192.168.100.252 kubenode-wds-1 <none> <none> calico-system calico-typha-7bb5db4bdc-rfpwg 1/1 Running 0 5h38m 10.2.5.7 kubenode-hkg04 <none> <none> calico-system calico-typha-7bb5db4bdc-rwwr5 1/1 Running 0 5h38m 192.168.100.251 kubemaster <none> <none> calico-system csi-node-driver-jglwp 2/2 Running 0 5h38m 10.42.64.68 kubenode-wds-1 <none> <none> calico-system csi-node-driver-jqjsc 2/2 Running 0 5h38m 10.42.253.137 kubenode-hkg04 <none> <none> calico-system csi-node-driver-vk26s 2/2 Running 0 5h38m 10.42.141.16 kubemaster <none> <none> kube-system coredns-695cbbfcb9-8fx4p 1/1 Running 1 (7h27m ago) 2d23h 10.42.141.14 kubemaster <none> <none> kube-system helm-install-traefik-crd-5bkwx 0/1 Completed 0 2d23h <none> kubemaster <none> <none> kube-system helm-install-traefik-m9fgj 0/1 Completed 1 2d23h <none> kubemaster <none> <none> kube-system local-path-provisioner-546dfc6456-dmn4g 1/1 Running 1 (7h27m ago) 2d23h 10.42.141.15 kubemaster <none> <none> kube-system metrics-server-c8774f4f4-2wkwh 1/1 Running 1 (7h27m ago) 2d23h 10.42.141.12 kubemaster <none> <none> kube-system svclb-traefik-999cddce-hpmcm 2/2 Running 6 (7h26m ago) 11h 10.42.253.134 kubenode-hkg04 <none> <none> kube-system svclb-traefik-999cddce-q4225 2/2 Running 2 (7h27m ago) 2d22h 10.42.141.9 kubemaster <none> <none> kube-system svclb-traefik-999cddce-xmd64 2/2 Running 2 (7h26m ago) 2d6h 10.42.64.66 kubenode-wds-1 <none> <none> kube-system traefik-788bc4688c-vbbhj 1/1 Running 1 (7h27m ago) 2d22h 10.42.141.13 kubemaster <none> <none> tigera-operator tigera-operator-6b95bbf4db-vl46l 1/1 Running 1 (7h27m ago) 2d23h 192.168.100.251 kubemaster <none> <none> 使用 kubectl exec -it -n calico-system <calico-node-xxxx> -- birdcl s p 可查看 Bird 的状态: root@KubeMaster:~/kube/calico# kubectl exec -it -n calico-system calico-node-2qz82 -- birdcl s p Defaulted container "calico-node" out of: calico-node, flexvol-driver (init), install-cni (init) BIRD v0.3.3+birdv1.6.8 ready. name proto table state since info static1 Static master up 08:58:17 kernel1 Kernel master up 08:58:17 device1 Device master up 08:58:17 direct1 Direct master up 08:58:17 Mesh_192_168_100_251 BGP master up 08:58:33 Established Mesh_192_168_100_252 BGP master up 08:59:00 Established Node_100_64_1_106 BGP master up 12:57:44 Established ip r 可查看系统路由表: root@KubeMaster:~/kube/calico# ip r default via 192.168.100.1 dev eth0 proto static 10.42.64.64/26 proto bird nexthop via 192.168.100.1 dev eth0 weight 1 nexthop via 192.168.100.252 dev eth0 weight 1 blackhole 10.42.141.0/26 proto bird 10.42.141.9 dev caliac6501d3794 scope link 10.42.141.12 dev calib07c23291bb scope link 10.42.141.13 dev caliab16e60bd19 scope link 10.42.141.14 dev calid5959219080 scope link 10.42.141.15 dev cali026d8f1ddb7 scope link 10.42.141.16 dev califa657ba417a scope link 10.42.253.128/26 via 192.168.100.1 dev eth0 proto bird 192.168.100.0/24 dev eth0 proto kernel scope link src 192.168.100.251 找一个Pod 的地址 Ping 一下,如果没啥问题的话应该就能直接通了: root@KubeMaster:~/kube/calico# ping 10.42.253.137 PING 10.42.253.137 (10.42.253.137) 56(84) bytes of data. 64 bytes from 10.42.253.137: icmp_seq=1 ttl=60 time=33.7 ms 64 bytes from 10.42.253.137: icmp_seq=2 ttl=60 time=33.5 ms ^C --- 10.42.253.137 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1002ms rtt min/avg/max/mdev = 33.546/33.632/33.718/0.086 ms 调优 MTU 这一步其实是为了稳定性……? 测试下来发现虽然我的 ZeroTier MTU 是 1420,但是实际上包大小到达 1392 左右就会开始触发分片(可用 ping -M do -s <包大小> <Pod_IP> 测试),因此强制指定 Pod MTU 为 1370: root@KubeMaster:~/kube/calico# cat patch-mtu.yaml apiVersion: operator.tigera.io/v1 kind: Installation metadata: name: default spec: calicoNetwork: mtu: 1370 nodeAddressAutodetectionV4: firstFound: true root@KubeMaster:~/kube/calico# kubectl apply -f patch-mtu.yaml installation.operator.tigera.io/default configured
2026年04月05日
103 阅读
0 评论
6 点赞
[整活向] 跨越 20km 的局域网: 在 OpenWrt 上使用 ZeroTier + OSPF 实现异地内网无感融合
起因 本来在配自己的 ZeroTier 大内网, 因为网络结构比较复杂, 所以采用 OSPF 而不是静态路由来配置内部路由. 之前尝试给自家 OpenWrt 上配置 ZeroTier 但是一直没成功, 这两天重新拿出来折腾了一下发现是 OpenWrt 的配置问题, 修好之后和好朋友闲聊的时候就想到: 说干就干, 开整( 基本信息 本地 路由器系统: OpenWrt, X-WRT 26.04_b202601250827 局域网 IPv4 前缀: 192.168.3.0/24 运营商: 合肥联通 NAT 环境: NAT1 对端 路由器系统: OpenWrt, X-WRT 25.04_b202510240128 局域网 IPv4 前缀: 192.168.1.0/24 运营商: 合肥移动 NAT 环境: NAT1 安装 ZeroTier 并使用自托管 Planet 我使用了 ZTNet 作为自托管 Controller , 搭建过程这里就不过多赘述了, 上网找一下就能找到. 我使用的 OpenWrt 版本已经开始使用 apk 代替 opkg 作为包管理器. 使用 apk 可直接安装 zerotier-one: apk add zerotier 完成后打开 /etc/config/zerotier 可找到默认配置文件. config zerotier 'global' # Sets whether ZeroTier is enabled or not option enabled 0 # Sets the ZeroTier listening port (default 9993; set to 0 for random) #option port '9993' # Client secret (leave blank to generate a secret on first run) option secret '' # Path of the optional file local.conf (see documentation at # https://docs.zerotier.com/config#local-configuration-options) #option local_conf_path '/etc/zerotier.conf' # Persistent configuration directory (to perform other configurations such # as controller mode or moons, etc.) #option config_path '/etc/zerotier' # Copy the contents of the persistent configuration directory to memory # instead of linking it, this avoids writing to flash #option copy_config_path '1' # Network configuration, you can have as many configurations as networks you # want to join (the network name is optional) config network 'earth' # Identifier of the network you wish to join option id '8056c2e21c000001' # Network configuration parameters (all are optional, if not indicated the # default values are set, see documentation at # https://docs.zerotier.com/config/#network-specific-configuration) option allow_managed '1' option allow_global '0' option allow_default '0' option allow_dns '0' # Example of a second network (unnamed as it is optional) #config network # option id '1234567890123456' # option allow_managed '1' # option allow_global '0' # option allow_default '0' # option allow_dns '0' 按照需求修改一下: config zerotier 'global' option enabled '1' # 启用 ZeroTier 客户端服务 option config_path '/etc/zerotier' # 持久化目录: 用于存放身份秘钥(identity)、Moon节点定义和网络设置 option secret '' # 秘钥留空: 首次启动会自动生成身份并存入 identity.secret 文件 option copy_config_path '1' # 保护闪存策略: 启动时将配置考入内存运行. 若设为 0, 则直接在 Flash 上读写 config network 'earth' option id '<network ID>' # 16位 ZeroTier 网络标识符 option allow_managed '1' # 允许接收控制器分配的 IP 地址、路由和标签 option allow_global '1' # 允许通过 ZeroTier 分配全球单播 IPv6 地址 (GUA) option allow_default '0' # 允许 ZeroTier 接管默认网关(实现类似全局代理的效果) option allow_dns '1' # 允许接收并设置 ZeroTier 控制面板中配置的 DNS 服务器 关于 copy_config_path '1' 因为 ZeroTier 工作目录 /var/lib/zerotier-one 在OpenWrt下属于 tmpfs , 重启后这里的内容会被清空, 因此需要将 planet ,identity, network 等配置放到路由器的 Flash 存储中, 即 config_path 配置的路径. 默认逻辑是启动的时候将配置的 config_path 软链接到 /var/lib/zerotier-one 实现配置持久化, 一切 /var/lib/zerotier-one 下的读写操作都会被写入到 Flash. 但是问题就是 ZeroTier 的频繁读写会导致 Flash 寿命折损比较快. 而开启 copy_config_path '1' 则会指定当 ZeroTier 启动的时候, 将 config_path 中的配置直接复制到 /var/lib/zerotier-one, 极大延长了路由器内部 Flash 的寿命, 但是问题是通过 zerotier-cli 做的一些修改默认不会直接同步到 Flash, 因此不适合需要经常调整配置的使用场景. 完成修改后使用 /etc/init.d/zerotier start /etc/init.d/zerotier enable 来启动 ZeroTier 并开启开机自启. 第一次启动时若上面 secret 配置项留空, 则会自动生成. 启动完成后将 /var/lib/zerotier-one 下的所有文件复制到 /etc/zerotier. 将 Planet 文件下载到上面设置的 config_path 中, 即 /etc/zerotier. 完成后重启 ZeroTier: /etc/init.d/zerotier restart 即可. 接着去 ZeroTier Controller 控制台, 就能看到新设备接入了. 接着可能需要允许 ZeroTier 流量通过防火墙, 这一步可参考网上其他教程. 我选择直接放行所有, NAT1下应该不会有太大问题. 安装并配置 Bird2 没想到 apk 里的 Bird2 是非常新的版本, 截止本文写作时间 2026-02-10, apk 里的 Bird2 版本为 2.18 使用如下指令安装: apk add bird2 # bird daemon 本体 apk add bird2c # birdc 指令 因为 OpenWrt 默认的 bird 配置文件存放在 /etc/bird.conf, 而我习惯按照不同的功能分不同的文件夹实现模块化引用, 因此我选择将默认配置文件改到 /etc/bird/bird.conf, 并在该文件夹下存放不同配置文件. 打开 /etc/init.d/bird: #!/bin/sh /etc/rc.common # Copyright (C) 2010-2017 OpenWrt.org USE_PROCD=1 START=70 STOP=10 BIRD_BIN="/usr/sbin/bird" BIRD_CONF="/etc/bird.conf" BIRD_PID_FILE="/var/run/bird.pid" start_service() { mkdir -p /var/run procd_open_instance procd_set_param command $BIRD_BIN -f -c $BIRD_CONF -P $BIRD_PID_FILE procd_set_param file "$BIRD_CONF" procd_set_param stdout 1 procd_set_param stderr 1 procd_set_param respawn procd_close_instance } reload_service() { procd_send_signal bird } 修改 BIRD_CONF 值为 /etc/bird/bird.conf: - BIRD_CONF="/etc/bird.conf" + BIRD_CONF="/etc/bird/bird.conf" 然后新建 /etc/bird文件夹, 之后的 OSPF 配置文件全都放在这里. 配置 OSPF 我的配置文件结构遵循如下规则: 由 /etc/bird/bird.conf 作为唯一入口点, 在这里定义一些基础的配置项, 如 Router ID, 过滤器网段, 接着由该文件引用其他子项的配置 不同网络的配置放在不同的文件夹下, 如公网部分放在 /etc/bird/inet/, DN42 部分放在 /etc/bird/dn42/, 自己的内网部分放在 /etc/bird/intra/ 不同的网络都由一个 defs.conf 处理那些公共的函数 (类似于 Golang 开发时写的 utils? ) 因此最终的配置文件结构如下: /etc/bird/bird.conf: 配置文件入口点 define INTRA_ROUTER_ID = 100.64.0.100; define INTRA_PREFIX_V4 = [ 100.64.0.0/16+, 192.168.0.0/16+ ]; # 允许被 OSPF 传递的 IPv4 前缀 define INTRA_PREFIX_V6 = [ fd18:3e15:61d0::/48+ ]; # 允许被 OSPF 传递的 IPv6 前缀 protocol device { scan time 10; }; ipv4 table intra_table_v4; # 定义内部路由 IPv4 路由表 ipv6 table intra_table_v6; # 定义内部路由 IPv6 路由表 include "intra/defs.conf"; include "intra/kernel.conf"; include "intra/ospf.conf"; 这里的 RouterID 我直接拿的这台机器在 ZeroTier 内网的 IPv4 地址. 分表是为了后期如果要为这台机器接入 DN42, 分表会比较安全. /etc/bird/intra/defs.conf: 过滤器所用的函数 function is_intra_net4() { return net ~ INTRA_PREFIX_V4; } function is_intra_net6(){ return net ~ INTRA_PREFIX_V6; } function is_intra_dn42_net4(){ return net ~ [ 172.20.0.0/14+ ]; } function is_intra_dn42_net6(){ return net ~ [ fd00::/8+ ]; } /etc/bird/intra/kernel.conf: 将 OSPF 学习到的路由写入系统路由表 protocol kernel intra_kernel_v4 { kernel table 254; scan time 20; ipv4 { table intra_table_v4; import none; export filter { if source = RTS_STATIC then reject; accept; }; }; }; protocol kernel intra_kernel_v6 { kernel table 254; scan time 20; ipv6 { table intra_table_v6; import none; export filter { if source = RTS_STATIC then reject; accept; }; }; }; /etc/bird/intra/ospf.conf: OSPF 模块 protocol ospf v3 intra_ospf_v4 { router id INTRA_ROUTER_ID; # 指定 RouterID ipv4 { table intra_table_v4; # 指定路由表 import where is_intra_dn42_net4() || is_intra_net4() && source != RTS_BGP; export where is_intra_dn42_net4() || is_intra_net4() && source != RTS_BGP; }; include "ospf/*"; }; protocol ospf v3 intra_ospf_v6 { router id INTRA_ROUTER_ID; # 指定 RouterID ipv6 { table intra_table_v6; # 指定路由表 import where is_intra_dn42_net6() || is_intra_net6() && source != RTS_BGP; export where is_intra_dn42_net6() || is_intra_net6() && source != RTS_BGP; }; include "ospf/*"; }; /etc/bird/intra/ospf/backbone.conf: OSPF 区域配置 area 0.0.0.0 { interface "br-lan" { stub; }; # 本地内网网卡 interface "zta7oqfzy6" { # ZeroTier 网卡 type broadcast; cost 100; hello 20; }; }; 完成后使用: /etc/init.d/bird start /etc/init.d/bird enable 来启动 Bird 并开启开机自启. 如果没问题的话便可以使用 birdc s p 查看 Bird 状态. 如果不出意外的话等对方配置好应该能看到 OSPF 状态是 Running 了: root@X-WRT:/etc/bird# birdc s p BIRD 2.18 ready. Name Proto Table State Since Info device1 Device --- up 14:28:02.410 intra_kernel_v4 Kernel intra_table_v4 up 14:28:02.410 intra_kernel_v6 Kernel intra_table_v6 up 14:28:02.410 intra_ospf_v4 OSPF intra_table_v4 up 14:28:02.410 Running intra_ospf_v6 OSPF intra_table_v6 up 14:31:38.389 Running 在朋友那边也按照这套流程走一遍, 等双方都是 Running 状态, 就可以通过 birdc s r protocol intra_ospf_v4 查看 OSPF 学到的路由. 发现已经可以正常学习到通过 ZeroTier 的通往对方的路由: root@X-WRT:/etc/bird# birdc s r protocol intra_ospf_v4 BIRD 2.18 ready. Table intra_table_v4: ... 192.168.1.0/24 unicast [intra_ospf_v4 23:20:21.398] * I (150/110) [100.64.0.163] via 100.64.0.163 on zta7oqfzy6 ... 192.168.3.0/24 unicast [intra_ospf_v4 14:28:02.511] * I (150/10) [100.64.0.100] dev br-lan 在 PC 上 Ping 朋友家的服务器也可以 Ping 通: iyoroy@iYoRoy-PC:~$ ping 192.168.1.103 PING 192.168.1.103 (192.168.1.103) 56(84) bytes of data. 64 bytes from 192.168.1.103: icmp_seq=1 ttl=63 time=54.3 ms 64 bytes from 192.168.1.103: icmp_seq=2 ttl=63 time=10.7 ms 64 bytes from 192.168.1.103: icmp_seq=3 ttl=63 time=15.2 ms ^C --- 192.168.1.103 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 1998ms rtt min/avg/max/mdev = 10.678/26.717/54.279/19.576 ms iyoroy@iYoRoy-PC:~$ traceroute 192.168.1.103 traceroute to 192.168.1.103 (192.168.1.103), 30 hops max, 60 byte packets 1 100.64.0.163 (100.64.0.163) 10.445 ms 9.981 ms 9.892 ms 2 192.168.1.103 (192.168.1.103) 11.621 ms 10.994 ms 10.948 ms 正常打开网页测速都没有问题: 总结 这一系列操作实际上实现了如下的网络结构: flowchart TB %% === 样式定义 === classDef phyNet fill:#e3f2fd,stroke:#1565c0,stroke-width:2px classDef virNet fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,stroke-dasharray: 5 5 classDef router fill:#333,stroke:#000,stroke-width:2px,color:#fff classDef ztCard fill:#f57c00,stroke:#e65100,stroke-width:2px,color:#fff,shape:rect classDef bird fill:#a5d6a7,stroke:#2e7d32,stroke-width:1px,color:#000 classDef invisibleContainer fill:none,stroke:none,color:none %% === 物理层容器 === subgraph Top_Physical_Layer [" "] direction LR subgraph Left_Side ["我家 (Node A)"] direction TB L_Router[X-WRT Router A]:::router L_LAN[内网: 192.168.3.0/24] L_LAN <--> L_Router end subgraph Right_Side ["朋友家 (Node B)"] direction TB R_Router[X-WRT Router B]:::router R_LAN[内网: 192.168.1.0/24] R_LAN <--> R_Router end end %% === 虚拟层容器 === subgraph Middle_Side [ZeroTier Virtual L2 Network] direction LR subgraph ZT_Stack_A [我家 ZT接入] direction TB L_NIC(zt0: 100.64.0.x):::ztCard L_Bird(Bird OSPF):::bird L_NIC <-.- L_Bird end subgraph ZT_Stack_B [朋友家 ZT接入] direction TB R_NIC(zt0: 100.64.0.y):::ztCard R_Bird(Bird OSPF):::bird R_NIC <-.- R_Bird end L_NIC <==P2P Tunnel==> R_NIC end %% === 跨层连接 === L_Router === L_NIC R_Router === R_NIC %% === 样式应用 === class Left_Side,Right_Side phyNet class Middle_Side virNet class Top_Physical_Layer invisibleContainer 最底层的 P2P 网络还是依靠 ZeroTier实现的, 不过使用 OSPF 内部寻路来让两边都可以直接路由到对方网段下的设备, 同时因为双方都能完整学习到对方的路由, 因此不需要使用任何的NAT, 双方也都能直接获取到对方的来源地址. 朋友视角 Linux 运维 - 新版 OpenWrt 基于 Bird 的 OSPF 组网实现 » Nanamiの电波发射塔
2026年02月10日
524 阅读
1 评论
3 点赞
记一次手动安装Proxmox VE, 配置多路iSCSI与NAT转发的经历
起因是租了一台物理机, 然后IDC那边没有提供PVE和Debian系统镜像, 只有Ubuntu、CentOS、Windows系列. 同时数据盘是通过多路iSCSI提供的. 我希望使用PVE来对不同使用场景进行隔离, 因此尝试重装并迁移上述配置. 备份配置 首先对系统做个大致的检查, 可发现: 系统存在两张网卡, 一张enp24s0f0接入了公网地址, 用于外部访问; 一张enp24s0f1接入了192.168.128.153的私网地址 数据盘被映射为了/dev/mapper/mpatha /etc/iscsi下存在两个iSCSI Node的配置, 分别为192.168.128.250:3260、192.168.128.252:3260, 但是二者都对应iqn.2024-12.com.ceph:iscsi. 不难推断出, 数据盘挂载是通过同时配置两个iSCSI Node, 再使用多路的方式合并成一个设备. 查看一下系统的网络配置: network: version: 2 renderer: networkd ethernets: enp24s0f0: addresses: [211.154.[数据删除]/24] routes: - to: default via: [数据删除] match: macaddress: ac:1f:6b:0b:e2:d4 set-name: enp24s0f0 nameservers: addresses: - 114.114.114.114 - 8.8.8.8 enp24s0f1: addresses: - 192.168.128.153/17 match: macaddress: ac:1f:6b:0b:e2:d5 set-name: enp24s0f1 发现就是非常简单的静态路由, 内网网卡甚至没有默认路由, 直接绑定IP即可. 然后将/etc/iscsi下iSCSI的配置文件保存一下, 其中包含了账户密码等信息 重装Debian 此次使用的是bin456789/reinstall重装脚本. 下载脚本: curl -O https://cnb.cool/bin456789/reinstall/-/git/raw/main/reinstall.sh || wget -O ${_##*/} $_ 重装成Debian 13: bash reinstall.sh debian 13 然后根据提示输入你想要设置的密码即可 如果不出意外的话, 等待10分钟左右, 会自动完成并重装成一个纯净的Debian 13. 中途可以通过ssh配合设置的密码连接, 查看安装进度. 重装完成后按照惯例进行一个换源和apt upgrade, 即可得到一个纯净的Debian 13啦 换源直接参考USTC镜像站的教程即可 安装Proxmox VE 这一步主要参考Proxmox官方的教程即可 需要注意,上述脚本安装的Debian会将主机名设置为localhost,你如果想要修改的话请在配置Hostname前修改并将hosts中的主机名改成你修改的主机名而非localhost 配置Hostname Proxmox VE要求为当前的主机名配置一个指向非回环地址的Hosts: The hostname of your machine must be resolvable to an IP address. This IP address must not be a loopback one like 127.0.0.1 but one that you and other hosts can connect to. 比如此处我的服务器IP为211.154.[数据删除], 我需要在/etc/hosts中添加如下的一条记录: 127.0.0.1 localhost +211.154.[数据删除] localhost ::1 localhost ip6-localhost ip6-loopback ff02::1 ip6-allnodes ff02::2 ip6-allrouters 保存后, 使用hostname --ip-address检查是否会输出设置的非回环地址: ::1 127.0.0.1 211.154.[数据删除] 添加Proxmox VE软件源 Debian 13使用了Deb822格式当然你想用sources.list也可以, 因此直接参考USTC的Proxmox镜像站即可: cat > /etc/apt/sources.list.d/pve-no-subscription.sources <<EOF Types: deb URIs: https://mirrors.ustc.edu.cn/proxmox/debian/pve Suites: trixie Components: pve-no-subscription Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg EOF 此处需要同步迁移一个keyring过来但是我上网找了一圈没找到, 因此我选择从我现有的一个Proxmox VE服务器上拉过来一份. 放在这里了: proxmox-keyrings.zip 将公钥文件解压放在/usr/share/keyrings/中, 然后运行 apt update apt upgrade -y 即可同步Proxmox VE软件源 安装Proxmox VE内核 使用如下命令安装PVE内核并重启以应用新内核: apt install proxmox-default-kernel reboot 之后通过uname -r应该能看到当前使用的是类似于6.17.2-2-pve这样以pve结尾的内核版本, 代表新内核应用成功. 安装Proxmox VE相关软件包 使用apt安装对应软件包: apt install proxmox-ve postfix open-iscsi chrony 配置过程中会需要设置postfix邮件服务器, 官方解释: 如果您的网络中有邮件服务器, 则应将postfix配置为Satellite system. 然后, 您现有的邮件服务器将成为中继主机, 将Proxmox VE发送的电子邮件路由到其最终收件人. 如果您不知道在此处输入什么, 请仅选择Local only, 并保持系统hostname不变. 之后应该能访问https://<你的服务器地址>:8006来打开Web控制台了, 账户为root, 密码为你的root密码, 即重装Debian时配置的密码. 删除旧的Debian内核和os-prober 使用以下命令: apt remove linux-image-amd64 'linux-image-6.1*' update-grub apt remove os-prober 来移除旧的Debian内核, 更新grub并移除os-prober. 移除os-prober不是必须的, 但是官方建议这么做, 因为它可能会误将虚拟机的引导文件认成多系统的引导文件, 导致将不该加的一些东西加到引导列表中. 至此, Proxmox VE的安装就完成了, 已经可以正常使用啦! 配置内网网卡 因为IDC那边iSCSI网卡和公网走的不是同一张, 而重装的时候丢失了这部分的配置, 因此需要手动配置一下内网的网卡. 打开Proxmox VE的Web后台, 找到Datacenter-localhost(主机名)-Network, 编辑内网网卡, 如我这里的是ens6f1, 填入上面备份的IPv4的CIDR格式: 192.168.128.153/17并勾选Autostart, 接着保存即可. 接着使用命令来设置网卡状态为UP: ip link set ens6f1 up 现在能Ping通内网iSCSI服务器的IP了. 配置数据盘 iSCSI 在上一步中, 我们应该已经安装了iscsiadm所需的软件包open-iscsi, 我们只需要按照之前备份的配置重新设置node即可. 首先发现一下iSCSI存储: iscsiadm -m discovery -t st -p 192.168.128.250:3260 可以得到原先存在的两个LUN Target: 192.168.128.250:3260,1 iqn.2024-12.com.ceph:iscsi 192.168.128.252:3260,2 iqn.2024-12.com.ceph:iscsi 将备份的配置文件传到服务器上, 覆盖掉原先的/etc/iscsi中的配置, 同时, 在我备份的配置中可以找到验证方面的配置: # /etc/iscsi/nodes/iqn.2024-12.com.ceph:iscsi/192.168.128.250,3260,1/default # BEGIN RECORD 2.1.5 node.name = iqn.2024-12.com.ceph:iscsi ... # 略去一些不重要的配置 node.session.auth.authmethod = CHAP node.session.auth.username = [数据删除] node.session.auth.password = [数据删除] node.session.auth.chap_algs = MD5 ... # 略去一些不重要的配置 # /etc/iscsi/nodes/iqn.2024-12.com.ceph:iscsi/192.168.128.252,3260,2/default # BEGIN RECORD 2.1.5 node.name = iqn.2024-12.com.ceph:iscsi ... # 略去一些不重要的配置 node.session.auth.authmethod = CHAP node.session.auth.username = [数据删除] node.session.auth.password = [数据删除] node.session.auth.chap_algs = MD5 ... # 略去一些不重要的配置 按照这些配置文件写入新的系统: iscsiadm -m node -T iqn.2024-12.com.ceph:iscsi -p 192.168.128.250:3260 -o update -n node.session.auth.authmethod -v CHAP iscsiadm -m node -T iqn.2024-12.com.ceph:iscsi -p 192.168.128.250:3260 -o update -n node.session.auth.username -v [数据删除] iscsiadm -m node -T iqn.2024-12.com.ceph:iscsi -p 192.168.128.250:3260 -o update -n node.session.auth.password -v [数据删除] iscsiadm -m node -T iqn.2024-12.com.ceph:iscsi -p 192.168.128.250:3260 -o update -n node.session.auth.chap_algs -v MD5 iscsiadm -m node -T iqn.2024-12.com.ceph:iscsi -p 192.168.128.252:3260 -o update -n node.session.auth.authmethod -v CHAP iscsiadm -m node -T iqn.2024-12.com.ceph:iscsi -p 192.168.128.252:3260 -o update -n node.session.auth.username -v [数据删除] iscsiadm -m node -T iqn.2024-12.com.ceph:iscsi -p 192.168.128.252:3260 -o update -n node.session.auth.password -v [数据删除] iscsiadm -m node -T iqn.2024-12.com.ceph:iscsi -p 192.168.128.250:3260 -o update -n node.session.auth.chap_algs -v MD5 (我不知道为什么验证信息需要单独写入一次, 但是实测下来不重写它无法登录) 接着, 使用: iscsiadm -m node -T iqn.2024-12.com.ceph:iscsi -p 192.168.128.250:3260 --login iscsiadm -m node -T iqn.2024-12.com.ceph:iscsi -p 192.168.128.252:3260 --login 登录Target, 接着使用: iscsiadm -m node -T iqn.2024-12.com.ceph:iscsi -p 192.168.128.250:3260 -o update -n node.startup -v automatic iscsiadm -m node -T iqn.2024-12.com.ceph:iscsi -p 192.168.128.252:3260 -o update -n node.startup -v automatic 开启开机自动挂载. 这个时候通过lsblk之类的工具查看磁盘应该就能发现多了两块硬盘, 我这里多出来了sdb和sdc. 配置multipath多路径 关于如何识别出是多路径设备, 我尝试通过: /usr/lib/udev/scsi_id --whitelisted --device=/dev/sdb /usr/lib/udev/scsi_id --whitelisted --device=/dev/sdc 查看两个磁盘设备的scsi_id, 发现二者是相同的, 因此可推断二者是同一块盘, 使用了多路来实现类似负载均衡和故障转移的效果 使用apt安装multipath: apt install multipath-tools 接着, 创建/etc/multipath.conf并填入: defaults { user_friendly_names yes find_multipaths yes } 配置multipathd开机自启: systemctl start multipathd systemctl enable multipathd 接着, 使用如下指令扫描并自动配置多路设备: multipath -ll 会输出: mpatha(360014056229953ef442476e85501bfd7)dm-0LIO-ORG,TCMU device size=500G features='1 queue_if_no_path' hwhandler='1 alua'wp=rw |-+- policy='service-time 0' prio=50 status=active | `- 14:0:0:152 sdb 8:16 active ready running `-+- policy='service-time 0' prio=50 status=active `- 14:0:0:152 sdc 8:16 active ready running 可看到已经将两块盘识别成同一个多路设备了. 此时, 可以在/dev/mapper/下找到多路设备的磁盘: root@localhost:/dev/mapper# ls control mpatha mpatha即为多路聚合的磁盘. 如果没扫描到,可尝试使用: rescan-scsi-bus.sh 重新扫描SCSI总线后再次尝试,若提示找不到这个指令可以用apt install sg3-utils安装. 实在不行咱重启一下 配置Proxmox VE使用数据盘 因为我们使用了多路, 因此不能直接选择添加iSCSI类型的存储. 使用如下指令创建PV和VG: pvcreate /dev/mapper/mpatha vgcreate <vg名称> /dev/mapper/mpatha 此处我将整块盘都配置成了PV, 你也可以单独划分出来一个分区来做这件事 完成后, 打开Proxmox VE的后台管理, 找到Datacenter-Storage, 点击Add-LVM, Volume group选择刚刚创建的VG的名称, ID自己给他命个名, 然后点击Add即可. 自此, 所有原系统的配置应该已经都迁移过来了 配置NAT NAT地址转换 因为只买了一个IPv4地址, 所以需要配置一下NAT来让所有虚拟机都能正常上网. 打开/etc/network/interfaces, 添加如下内容: auto vmbr0 iface vmbr0 inet static address 192.168.100.1 netmask 255.255.255.0 bridge_ports none bridge_stp off bridge_fd 0 post-up echo 1 > /proc/sys/net/ipv4/ip_forward post-up iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -o ens6f0 -j MASQUERADE post-up iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1 post-up iptables -A FORWARD -i vmbr0 -j ACCEPT post-down iptables -t nat -D POSTROUTING -s 192.168.100.0/24 -o ens6f0 -j MASQUERADE post-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1 post-down iptables -D FORWARD -i vmbr0 -j ACCEPT 其中, vmbr0为NAT网桥, 网桥IP段为192.168.100.0/24 , 该网段流量会被转换为ens6f0外网网卡的IP发出, 并在收到回复时转换为原始IP, 实现共享外部IP. 接着, 使用: ifreload -a 重载配置. 到此, 虚拟机就已经能实现上网了, 只需要安装的时候配置静态地址为192.168.100.0/24内的地址, 默认网关设置为192.168.100.1, 并配置DNS地址即可. 端口转发 懒了, 直接拷打AI 让AI写了个配置脚本/usr/local/bin/natmgr: #!/bin/bash # =================配置区域================= # 公网网卡名称 (请根据实际情况修改) PUB_IF="ens6f0" # ========================================= ACTION=$1 ARG1=$2 ARG2=$3 ARG3=$4 ARG4=$5 # 检查是否为 root 用户 if [ "$EUID" -ne 0 ]; then echo "请使用 root 权限运行此脚本" exit 1 fi # 生成随机 ID (6位字符) generate_id() { # 引入纳秒和随机盐以确保即使脚本执行速度很快,ID也不会重复 echo "$RANDOM $(date +%s%N)" | md5sum | head -c 6 } # 显示帮助信息 usage() { echo "用法: $0 {add|del|list|save} [参数]" echo "" echo "命令:" echo " add <公网端口> <内网IP> <内网端口> [协议] 添加转发规则" echo " [协议] 可选: tcp, udp, both (默认: both)" echo " del <ID> 通过 ID 删除转发规则" echo " list 查看当前所有转发规则" echo " save 保存当前规则,使其在重启后仍然存在 (必须运行!)" echo "" echo "示例:" echo " $0 add 8080 192.168.100.101 80 both" echo " $0 save" echo "" } # 内部函数:添加单条协议规则 _add_single_rule() { local PROTO=$1 local L_PORT=$2 local T_IP=$3 local T_PORT=$4 local RULE_ID=$(generate_id) local COMMENT="NAT_ID:${RULE_ID}" # 1. 添加 DNAT 规则 (PREROUTING 链) iptables -t nat -A PREROUTING -i $PUB_IF -p $PROTO --dport $L_PORT -j DNAT --to-destination $T_IP:$T_PORT -m comment --comment "$COMMENT" # 2. 添加 FORWARD 规则 (允许数据包通过) iptables -A FORWARD -p $PROTO -d $T_IP --dport $T_PORT -m comment --comment "$COMMENT" -j ACCEPT # 输出结果 printf "%-10s %-10s %-10s %-20s %-10s\n" "$RULE_ID" "$PROTO" "$L_PORT" "$T_IP:$T_PORT" "成功" # 提醒用户保存 echo "请运行 '$0 save' 命令以确保规则在重启后仍然存在。" } # 主添加函数 add_rule() { local L_PORT=$1 local T_IP=$2 local T_PORT=$3 local PROTO_REQ=${4:-both} # 默认为 both if [[ -z "$L_PORT" || -z "$T_IP" || -z "$T_PORT" ]]; then echo "错误: 参数缺失" usage exit 1 fi # 转换为小写 PROTO_REQ=$(echo "$PROTO_REQ" | tr '[:upper:]' '[:lower:]') echo "正在添加规则..." printf "%-10s %-10s %-10s %-20s %-10s\n" "ID" "协议" "公网端口" "目标地址" "状态" echo "------------------------------------------------------------------" if [[ "$PROTO_REQ" == "tcp" ]]; then _add_single_rule "tcp" "$L_PORT" "$T_IP" "$T_PORT" elif [[ "$PROTO_REQ" == "udp" ]]; then _add_single_rule "udp" "$L_PORT" "$T_IP" "$T_PORT" elif [[ "$PROTO_REQ" == "both" ]]; then _add_single_rule "tcp" "$L_PORT" "$T_IP" "$T_PORT" _add_single_rule "udp" "$L_PORT" "$T_IP" "$T_PORT" else echo "错误: 不支持的协议 '$PROTO_REQ'。请使用 tcp, udp 或 both。" exit 1 fi echo "------------------------------------------------------------------" } # 删除规则 (基于行号倒序删除) del_rule() { local RULE_ID=$1 if [[ -z "$RULE_ID" ]]; then echo "错误: 请提供规则 ID" usage exit 1 fi echo "正在搜索 ID 为 [${RULE_ID}] 的规则..." local FOUND=0 # --- 清理 NAT 表 (PREROUTING) --- LINES=$(iptables -t nat -nL PREROUTING --line-numbers | grep "NAT_ID:${RULE_ID}" | awk '{print $1}' | sort -rn) if [[ ! -z "$LINES" ]]; then for line in $LINES; do iptables -t nat -D PREROUTING $line echo "已删除 NAT 表 PREROUTING 链第 $line 行" FOUND=1 done fi # --- 清理 Filter 表 (FORWARD) --- LINES=$(iptables -t filter -nL FORWARD --line-numbers | grep "NAT_ID:${RULE_ID}" | awk '{print $1}' | sort -rn) if [[ ! -z "$LINES" ]]; then for line in $LINES; do iptables -t filter -D FORWARD $line echo "已删除 Filter 表 FORWARD 链第 $line 行" FOUND=1 done fi if [[ $FOUND -eq 0 ]]; then echo "未找到 ID 为 $RULE_ID 的规则。" else echo "删除操作完成。" echo "请运行 '$0 save' 命令以更新持久化配置文件。" fi } # 保存规则到磁盘 (新增功能) save_rules() { echo "正在保存当前的 iptables 规则..." # netfilter-persistent 是 Debian/Proxmox 中管理 iptables-persistent 的服务 if command -v netfilter-persistent &> /dev/null; then netfilter-persistent save if [ $? -eq 0 ]; then echo "✅ 规则已成功保存到 /etc/iptables/rules.v4,将在系统重启后自动恢复。" else echo "❌ 规则保存失败。请检查 'netfilter-persistent' 服务状态。" fi else echo "警告: 未找到 'netfilter-persistent' 命令。" echo "请确保已安装 'iptables-persistent' 软件包。" echo "安装命令: apt update && apt install iptables-persistent" fi } # 列出规则 list_rules() { echo "当前端口转发规则列表:" printf "%-10s %-10s %-10s %-20s %-10s\n" "ID" "协议" "公网端口" "目标地址" "目标端口" echo "------------------------------------------------------------------" # 解析 iptables 输出 iptables -t nat -nL PREROUTING -v | grep "NAT_ID:" | while read line; do id=$(echo "$line" | grep -oP '(?<=NAT_ID:)[^ ]*') # 提取协议 if echo "$line" | grep -q "tcp"; then proto="tcp"; else proto="udp"; fi # 提取 dpt: 之后的端口 l_port=$(echo "$line" | grep -oP '(?<=dpt:)[0-9]+') # 提取 to: 之后的 IP:Port target=$(echo "$line" | grep -oP '(?<=to:).*') t_ip=${target%:*} t_port=${target#*:} printf "%-10s %-10s %-10s %-20s %-10s\n" "$id" "$proto" "$l_port" "$t_ip" "$t_port" done } # 主逻辑 case "$ACTION" in add) add_rule "$ARG1" "$ARG2" "$ARG3" "$ARG4" ;; del) del_rule "$ARG1" ;; list) list_rules exit 0 ;; save) save_rules ;; *) usage exit 1 ;; esac save_rules 让其自动添加/删除iptables规则实现端口转发. 记得chmod +x 通过iptables-persistent实现保存配置开机自动加载: apt install iptables-persistent 配置过程中会询问是否需要保存当前规则,Yes或者No都可以。 添加转发规则时使用natmgr add <主机监听地址> <虚拟机内网IP> <虚拟机端口> [tcp/udp/both]即可, 脚本会自动分配一个唯一ID, 删除时使用natmgr del <ID>即可. 也可使用natmgr list查看已有转发列表. 参考文章: bin456789/reinstall: 一键DD/重装脚本 (One-click reinstall OS on VPS) - GitHub Install Proxmox VE on Debian 12 Bookworm - Proxmox VE PVE连接 TrueNAS iSCSI存储实现本地无盘化_pve iscsi-CSDN博客 ProxmoxVE (PVE) NAT 网络配置方法 - Oskyla 烹茶室
2025年11月29日
378 阅读
2 评论
2 点赞
DN42&OneManISP - 共存环境下的OSPF源地址故障排除
前情提要 正如这个系列的上文所说,因为VRF方案太过于隔离,导致我部署在HKG节点(172.20.234.225)的DNS服务无法被DN42网络所访问,查阅资料得知可以通过设置veth或者NAT地址转发的方式来实现,但是因为现有的资料比较少,最终还是放弃了VRF这个方案。 结构分析 这次我打算将DN42和公网BGP的路由都放入系统的主路由表,然后再分开导出,通过过滤器来区分是否应该导出。同时,为了更加直观,我将DN42部分的配置和公网(以下简称inet)部分的配置分别单独存放,再由主配置文件引入。同时,因为kernel部分配置一个路由表只应该存在一个,因此合并DN42和inet的kernel部分,仅保留一个。 经过多次优化和修改,我最终的目录结构如下: /etc/bird/ ├─envvars ├─bird.conf: Bird主配置文件,负责定义基本信息(ASN、IP等),引入下面的子配置 ├─kernel.conf: 内核配置,负责将路由导入系统路由表 ├─dn42 | ├─defs.conf: DN42的函数定义,如is_self_dn42_net()这类 | ├─ibgp.conf: DN42 iBGP模板 | ├─rpki.conf: DN42 RPKI路由验证 | ├─ospf.conf: DN42 OSPF内网 | ├─static.conf: DN42静态路由 | ├─ebgp.conf: DN42 Peer模板 | ├─ibgp | | └<ibgp configs>: DN42 iBGP各个节点的配置 | ├─ospf | | └backbone.conf: OSPF区域 | ├─peers | | └<ibgp configs>: DN42 Peer各个节点的配置 ├─inet | ├─peer.conf: 公网Peer | ├─ixp.conf: 公网IXP接入 | ├─defs.conf: 公网部分的函数定义,如is_self_inet_v6() | ├─upstream.conf: 公网上游 | └static.conf: 公网静态路由 将定义函数的部分单独拿出来是因为我需要在kernel.conf的过滤器中引用,因此单独拿出来以便于提前include。 完成后分别填入对应配置,然后由写好include关系,birdc configure后发现也成功跑起来了。于是乎告一段落...吗? 发现问题 运行一段时间后,我突然发现通过我的内网设备Ping HKG节点无法Ping通,通过HKG节点Ping我的其他内部节点也无法Ping通。奇怪的是,外部AS可以通过我的HKG节点Ping到我的其他节点或者其他外部AS,我的内部节点也可以通过HKG节点Ping到其他不直接相连的节点(如:226(NKG)->225(HKG)->229(LAX))。 通过ip route get <内网其他节点地址>发现: root@iYoRoyNetworkHKG:/etc/bird# ip route get 172.20.234.226 172.20.234.226 via 172.20.234.226 dev dn42_nkg src 23.149.120.51 uid 0 cache 看出问题了吗?src地址本来应该是HKG节点自己的DN42地址(OSPF部分stub网卡配置的),但是这里显示的却是HKG节点的公网地址。 尝试通过birdc s r for 172.20.234.226读取bird学习到的路由: root@iYoRoyNetworkHKGBGP:/etc/bird/dn42/ospf# birdc s r for 172.20.234.226 BIRD 2.17.1 ready. Table master4: 172.20.234.226/32 unicast [dn42_ospf_iyoroynet_v4 00:30:29.307] * I (150/50) [172.20.234.226] via 172.20.234.226 on dn42_nkg onlink 看起来貌似一切正常...? 理论上来说,虽然DN42的源IP和正常的不太一样,但是DN42在导出到内核的时候改写了krt_prefsrc来告诉内核正确的源地址,理论上不应该出现这样的问题: protocol kernel kernel_v4{ ipv4 { import none; export filter { if source = RTS_STATIC then reject; + if is_valid_dn42_network() then krt_prefsrc = DN42_OWNIP; accept; }; }; } protocol kernel kernel_v6 { ipv6 { import none; export filter { if source = RTS_STATIC then reject; + if is_valid_dn42_network_v6() then krt_prefsrc = DN42_OWNIPv6; accept; }; }; } 关于krt_prefsrc,其含义是Kernel Route Preferred Source。这个属性并非直接操作路由,而是为路由附加一个元数据,它直接告诉 Linux 内核:当通过这条路由发送数据包时,应优先使用这里指定的 IP 地址作为源地址。 在这里卡了好久的说 解决方案 最终,某次无意间尝试给OSPF的导出配置中也加上了krt_prefsrc改写: protocol ospf v3 dn42_ospf_iyoroynet_v4 { router id DN42_OWNIP; ipv4 { - import where is_self_dn42_net() && source != RTS_BGP; + import filter { + if is_self_dn42_net() && source != RTS_BGP then { + krt_prefsrc=DN42_OWNIP; + accept; + } + reject; + }; export where is_self_dn42_net() && source != RTS_BGP; }; include "ospf/*"; }; protocol ospf v3 dn42_ospf_iyoroynet_v6 { router id DN42_OWNIP; ipv6 { - import where is_self_dn42_net_v6() && source != RTS_BGP; + import filter { + if is_self_dn42_net_v6() && source != RTS_BGP then { + krt_prefsrc=DN42_OWNIPv6; + accept; + } + reject; + }; export where is_self_dn42_net_v6() && source != RTS_BGP; }; include "ospf/*"; }; 之后再运行发现src地址正确了,互相Ping也都能通。 配置文件可参考:KaguraiYoRoy/Bird2-Configuration
2025年10月29日
138 阅读
0 评论
1 点赞
1
2
3