萌约 MoeYork

一个普通的博客站点

0%

OpenWRT 配置教程

写在前面

笔者最近为一台设备刷写了最新的 OpenWRT 24.10.1(虽然没过几天 24.10.2 版本已出 Orz)。为了让这台路由器符合笔者的需求,笔者研究了负载均衡、无线 mesh 组网和 IPv6 等配置。然而现有的简中互联网并没有详细的配置教程或说明文档,很多配置只知其然不知其所以然,甚至以讹传讹,让人迷惑甚至误解。笔者在现有资料的基础上对部分国内网络环境下的常见需求进行了探索和分析,给出了设置项位置和相关设置说明,力求简明准确。虽然笔者写到最后仍是一知半解,但还是记录下来,希望能给读者和未来的自己节省一些时间。

配置细节

自定义构建

通过在线构建可以保证安装的软件包是最新的,避免在 overlayfs 上重复安装浪费存储空间

  • 启用 dnsmasq-full
  • 启用 kmod-tcp-bbr
  • 启用 wpad-mbedtls 为 802.11s 做准备,这个就是完整版的 hostapd / wpad,按加密套件不同分为openssl / wolfssl / bedtls 三个版本
  • 安装 luci-i18n-upnp-zh-cn 添加 upnp 管理
  • 安装 luci-i18n-wol-zh-cn 添加 WOL 支持
  • 安装 block-mount 为 luci 添加系统 / 挂载点选项

IPv6 网络配置

  • 允许扩展前缀分配: 网络 / 接口 / 接口选项卡 wan6 端口 编辑 / 常规设置 打开 扩展前缀 (端口协议应为DHCPv6 客户端
  • IPv6 中继相关配置 网络 / 接口 / 接口选项卡 选择端口 编辑 / DHCP 服务器
  • IPv6 前缀相关配置: 网络 / 接口 / 接口选项卡 选择端口 编辑 / 高级设置
  • 开启MSS钳制: 网络 / 防火墙 / 常规设置选项卡 / 区域 选择防火墙区域(不清楚该选哪个) 编辑 / 常规设置 打开 MSS 钳制

IPv6 地址分配

IPv6地址由网络前缀+接口标识组成,有以下几种类型:

类型名称前缀标识
单播地址全球单播地址 GUA2000::/3
唯一本地地址 ULAfc00::/7
站点本地地址 SLAfec0::/10
链路本地地址 LLAfe80::/10
特殊地址环回地址::1/128
未指定地址::/128
组播地址ff00::/8

IPv6 地址分配有两种方式:

  • 有状态:由 DHCPv6 服务器分配
  • 无状态:由 SLAAC 过程设备自行分配

两种方式可同时存在,注意安卓设备只支持 SLAAC,SLAAC 过程使用 NDP(邻居发现协议)的 RS / RA 消息;NDP 还有类似 ARP 的功能,使用 NS / NA 消息

实际分配时,路由器通过 RA(路由器通告)消息应答设备的 RS (路由器请求)消息,下发配置和 SLAAC 前缀。RA 消息是定期发送的,但主机发送 RS 消息后,可立即得到响应:

  • A标志(Autonomous Address Configuration,每个前缀独立配置)
    • A=1:客户端可在该前缀范围内进行 SLAAC,获取 IPv6 地址和 DNS 等参数
    • A=0:客户端不应当在该前缀范围内进行 SLAAC
  • M标志(Managed Address Configuration):
    • M=1:客户端可使用 DHCPv6 获取 IPv6 地址和 DNS 等参数
    • M=0:判断O标志
  • O标志(Other Configuration,仅当M=0时有效):
    • O=1:当有A=1(全部 A=0 没有 IP 地址)时,客户端可通过 DHCPv6 获取 DNS 等参数
    • O=0:不通过 DHCPv6 获取 DNS 等参数

要注意单个 IPv6 地址与子网的区别,获取到一个 IPv6 地址≠获取了一条路由:

假设 2001:1:2:3:a:b:c:d/64 是一个 IPv6 地址,IPv6 地址本身不包含前缀信息,但在网络配置中,前缀长度(如 /64)类似子网掩码,用于决定该地址所属的子网范围。网络接口使用前缀(2001:1:2:3)判断一个 IPv6 地址是否与自己同属一个子网(即在本地链路上)。网络接口在没有明确路由的情况下,只能在本地链路上通信。在内核根据路由表判断某个报文应从接口 B 发出时,首先要通过 NDP 在本地链路上确定目的 IPv6 地址对应的 MAC 地址,然后在链路层向该 MAC 地址转发 IPv6 报文

路由转发操作在同一个路由器的不同接口之间进行,由 Linux 内核遵循路由表进行转发。例如路由器有两个接口A、B,IPv6 地址分别为 2001:1:a::1/48 和 2001:1:b::1/48,其网络前缀不同,分属两个不同的子网,且假设具有一条路由(将2001:1:b:6789::/64转发到2001:1:b:6789::1)。

  • 当接口 A 收到目的 IPv6 地址为 2001:1:b:abcd::1 的 IPv6 报文时,Linux 内核判断应由接口 B 发出,在接口 B 的本地链路上执行 NDP 查找 2001:1:b:abcd::1 对应的 MAC 地址,成功后在其本地链路上向 2001:1:b:abcd::1 发送 IPv6 报文。
  • 而当接口A 收到目的 IPv6 地址为 2001:1:b:6789::1234 的 IPv6 报文时,Linux 内核根据路由表向 2001:1:b:6789::1 转发该报文,2001:1:b:6789::1 位于接口 B,因此在接口 B 的本地链路上执行 NDP 查找 2001:1:b:6789::1 对应的 MAC 地址,成功后在其本地链路上向 2001:1:b:6789::1 发送 IPv6 报文

在该例子下的一个 IPv6 地址分配过程示例(IPv6 地址中的/64代表其前缀长度为 /64):

  • 新设备 C 生成链路本地地址
  • 新设备 C 连接到路由器接口 B,发送 RS 消息,获取 RA 通告为 M=1,O=1,一条可用前缀(2001:1:b:0,A=1,前缀长度64),一个可用 DNS 2001:1:b::1,源 MAC 地址 aa:bb:cc:dd:ee:ff,MTU=1500
  • 设备通过 SLAAC 为自己分配 IPv6 地址 2001:1:b:6666:8888::abcd/64 和 2001:1:b:6666:8888::cdef/64,网关 IPv6 地址为 fe80:xxxx(链路本地地址)
  • 设备通过 DHCPv6 (IA_NA)获得地址 2001:1:b:6666::abcd/128
  • 设备通过 DHCPv6-PD(IA_PD)申请到子网前缀 2001:1:b:6789::/64,路由器添加新的路由条目(将2001:1:b:6789::/64 转发到 2001:1:b:6666:::abcd)

DHCPv6-PD

DHCPv6 前缀代理(PD,Prefix Delegation)允许下一级路由器通过 DHCPv6 协议获得可自行分配的子网前缀,以下内容参考IPV6 DHCPv6-PD 前缀子网简单拆解

  • 所有全球单播地址(公网 IPv6 地址)中网络前缀最长只能是64bit,不能再划分,因此路由器 wan 口至少会占一个 /64 子网
  • SLAAC只能使用64位网络前缀

SLAAC、有状态 DHCPv6 、无状态 DHCPv6 和 DHCPv6-PD 可同时存在,但是只有 DHCPv6-PD 可以申请到新的子网路由。在获取到 PD 前缀后,路由器可以将其下发给 LAN 口使用,这样局域网中的设备也可以有全球单播地址。

DHCPv6-PD 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
上级 LAN 口

- fdba:c244:5433::1/60

本级 WAN 口

- fdba:c244:5433:4::/62(DHCPv6-PD)
- fdba:c244:5433:0:::d26/128(DHCPv6)
- fdba:c244:5433:0:2276:93ff:fe57:647d/64(SLAAC)

本级 LAN 口

- fdba:c244:5433:4::1/62

下级 WAN 口

- fdba:c244:5433:6::/63(DHCPv6-PD)
- fdba:c244:5433:4::3cf/128(DHCPv6)
- fdba:c244:5433:4:cc4f:c2ff:fe67:7fa8/64(SLAAC)

子网划分情况

fdba:c244:5433::1/60(上级 LAN 口子网)
fdba:c244:5433:0::/62
fdba:c244:5433:0::/64(本级 WAN 口子网)
fdba:c244:5433:1::/64
fdba:c244:5433:2::/64
fdba:c244:5433:3::/64
fdba:c244:5433:4::/62(本级获取的 PD 前缀 && 本级 LAN 口子网)
fdba:c244:5433:4::/63
fdba:c244:5433:4::/64(下级 WAN 口子网)
fdba:c244:5433:5::/64
fdba:c244:5433:6::/63(下级获取的 PD 前缀 && 下级 LAN 口子网)
fdba:c244:5433:8::/62
fdba:c244:5433:c::/62

RFC 7278

但很糟糕的是,在笔者的校园网环境下 SLAAC 和 DHCPv6-PD 获得的 IPv6 地址前缀相同

来自RFC 6603:DHCPv6 前缀委派(DHCPv6-PD){RFC 3633} 存在第 12.1 节所述的明确限制:委派给请求路由器(本级)的前缀不能被委派路由器(上一级)使用。该限制意味着委派路由器将拥有两条(不可聚合的)指向客户的路由:一条用于请求路由器与委派路由器间的链路,另一条用于请求路由器背后的客户站点。

来自RFC 3633:the requesting router MUST NOT assign any delegated prefixes or subnets from the delegated prefix(es) to the link through which it received the DHCP message from the delegating router 请求路由器不得将任何从委派前缀(或其子网)中分配的前缀,用于分配给它通过同一链路(即从委派路由器接收 DHCP 消息的那条链路)的接口

来自RFC 6603:The OPTION_PD_EXCLUDE option allows prefix delegation where a requesting router is delegated a prefix (e.g., /56) and the delegating router uses one prefix (e.g., /64) on the link through which it exchanges DHCPv6 messages with the requesting router with a prefix out of the same delegated prefix set. OPTION_PD_EXCLUDE 选项允许在前缀委派中进行如下情况:请求路由器被委派一个前缀(例如 /56),而委派路由器在与其交换 DHCPv6 消息的链路上(即 WAN 链路)使用同一委派前缀集合中的某个前缀(例如 /64)

来自RFC 6603:The delegating router includes the prefix in the OPTION_PD_EXCLUDE option that is excluded from the delegated prefix set. The requesting router MUST NOT assign the excluded prefix to any of its downstream interfaces. 委托路由器在 OPTION_PD_EXCLUDE 选项中包含需要排除的前缀,该前缀将从所分配的前缀集合中被剔除。请求路由器不得将所排除的前缀分配给任何下游接口

当然这个问题直接打开扩展前缀选项就可以解决(RFC 7278)

实现局域网获取全球单播地址

对于有 PD 的情况,路由器会自动将可用的 PD 前缀分配给 lan 使用

对于没有 PD 的情况,要实现局域网获取全球单播地址,就需要将局域网设备暴露到上层网络中

  • 启用 RA 中继,为局域网内设备转发上层网络 RA 消息
  • 启用 DHCPv6 中继,为局域网内设备转发上层网络 DHCPv6 消息
  • 启用 NDP 中继,向上层网络转发局域网设备 IPv6地址 与 mac 对应关系

这种方法在上层网络看来,会有多个 IPv6 地址(/64 前缀)对应到同一个 MAC 地址(路由器 wan 口),因此在某些网络下不可行。在一个 MAC 地址只能对应一个 IPv6 地址(/64 前缀)的情况下,只能通过 NAT6 为局域网分配地址

桥接配置

操作要考虑清楚,否则可能连不上路由器

  • 修改路由器局域网 IP 网段(桥接不需要): 网络 / 接口 / 接口选项卡 lan 端口 编辑 / 常规设置 IPv4 地址 改为需要的路由器自身IP,配合下面的子网掩码生效
  • 在局域网上禁用 DHCP 服务器: 网络 / 接口 / 接口选项卡 lan 端口 编辑 / DHCP 服务器 / 常规设置 打开 忽略此端口
  • 将路由器自身 IP 改为 DHCP 获取: 网络 / 接口 / 接口选项卡 lan 端口 编辑 / 常规设置 协议 选择 DHCP 客户端
  • 不应当简单关闭 wan 口的 IP 动态伪装(即 NAT 功能),会造成往返非对称路由: 网络 / 防火墙 / 常规设置选项卡 / 区域 wan区域 编辑 / 常规设置 打开 IP 动态伪装

802.11s 配置

这是关于真正的 mesh 的配置,定义参考官网教程

  • WiFi可用频段受国家代码限制(不符合系统日志里会报错),可配置为CN: 网络 / 无线 选择网络 编辑 / 设备配置 / 常规设置 国家代码 选择 CN
  • 不同 mesh 节点 同页面位置 模式 信道 通道宽度 应一致
  • 启用 802.11s 模式: 网络 / 无线 选择网络 编辑 / 接口配置 / 常规设置 选择 802.11s
  • 加入 lan 网络: 同页面位置 网络 选择 lan(会自动被桥接,非桥接网络如 wan,原设备可能会被替换掉)

802.11s 为二层网络,如果配置好以后没有关掉其他节点 lan 网络的 DHCP 服务器,新加入其他节点的设备 DHCP 请求会被抢答

除此之外还可以通过 WDS 来实现无线桥接,一方设置为接入点 AP(WDS),另一方设置为客户端(WDS)即可实现二层桥接

当然 ad-hoc 也是一种组网方式,但是笔者不懂

DSA 架构下的 VLAN 配置

位置: 网络 / 接口 / 设备选项卡 选择交换机设备(br-lan)配置 / 网桥 VLAN 过滤

DSA 架构可以让 Linux 在 CPU 侧区分流量来自具体哪个交换机端口,因此在 DSA 架构下 CPU 侧与交换机连接的物理端口已经按交换机端口拆分虚拟端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# refer: https://docs.kernel.org/networking/dsa/dsa.html

+----------------------------------+
| +------------------------+|
| | br-lan ||
| +--|^-------|^-------|^--+|
|+-----+ +--v|--+ +--v|--+ +--v|--+|
|| wan | | lan1 | | lan2 | | lan3 ||
|+-----+-+------+-+------+-+------+|
| DSA驱动程序 | 软件
+----------------------------------+
|^
添加特定交换机标识 || 识别特定交换机标识
v|
+-------------+------+-------------+
| | eth0 | |
| +------+ |
| 网络接口驱动程序 | 软件
--------+----------------------------------+------------
| CPU侧网络接口 | 硬件
+----------------------------------+
|^
识别特定交换机标识 || 添加特定交换机标识
v|
+-------------+------+-------------+
| | eth0 | |
| +------+ |
| 交换机 |
|+-----+ +------+ +------+ +------+|
|| wan | | lan1 <=> lan2 <=> lan3 || # br-lan 控制桥接 lan1 lan2 lan3
++-----+-+------+-+------+-+------++

在 DSA 架构下配置 VLAN 时,首先要理清以下几点:

  • 下述 物理端口 / 交换机上的xxx端口 指物理交换机上的真实网络接口(在上图中硬件部分)
  • 下述 虚拟接口 / Linux 下的xxx接口 指在 Linux 下可见(或 Luci 中展示)的 交换机端口(在上图中软件部分)

对于虚拟接口

  • Linux 下的 eth0 是真实的 CPU 侧与交换机通信的接口,该接口由 DSA 驱动收发带有特定交换机标识(用于区分来源,不是 VLAN tag)的二层数据,不应该直接访问。
  • 所有 Linux 下可见的交换机接口(wan、lan1..3)都不是交换机上的真实端口,而是对交换机(按来源端口区分后)发往 CPU 数据抽象出的虚拟接口,区分工作由 DSA 驱动通过解析特定交换机标识完成。
  • Linux 下没有交换机上 eth0 物理端口对应的虚拟接口

对于转发策略

  • 所有 Linux 下可见的虚拟接口(br-lan,wan,lan1..3)都隐式的包含主机端(就是流量可被转发至 CPU 侧)。简单理解的话,br-lan + wan就相当于上文提到的交换机上 eth0 物理端口对应的虚拟接口,毕竟概念上这些虚拟接口就是对应的物理接口转发到 CPU 侧的抽象(可以理解为相当于eth0-from-wan,eth0-from-br-lan)
  • 所有没有被桥接Linux 下可见的虚拟接口对应的物理端口均只转发至 Linux 下的接口(即对应的虚拟接口)。如果同一台交换机上的物理端口对应的虚拟接口没有通过 Linux 下的桥接设备连接,即使它们在同一台交换机上,也不会相互转发
  • Linux 下的桥接设备唯一的控制哪些Linux 下的接口对应的物理端口在二层互通。如果桥接的物理端口在同一台交换机上,会自动开启硬件offload,流量不经过 Linux 中转。而没有在同一个物理交换机上的端口被桥接将通过 Linux 中转进行交换

理清上述几条以后,就可以解释实际的接口配置:

  • 笔者的路由器工作在 DSA 架构下,在 br-lan 中没有 eth0:没有 eth0 是因为 Linux 下的 eth0 是带有特定交换机标识(用于区分来源,不是 VLAN tag)的原始网络接口,在套了 DSA 驱动之后变成了 Linux 下的 wan、lan1..3 接口,只需要关注这些虚拟接口即可。
  • 笔者的路由器默认没有启用 VLAN:在 Linux 下 br-lan 仅桥接了 lan1..4,没有将它们与 wan 桥接,两者已经隔离开了

在了解 DSA 架构下的 Linux 下的虚拟接口的特性后,就可以关注 VLAN 相关的特性:

  • 所有 Linux 下可见的接口都丢弃带有 VLAN tag 的报文,要处理从某端口发来的带有 VLAN tag 的报文,要套一层 802.1q 设备,这个新设备将收到的带有指定 VLAN tag 的报文转为普通报文,并将发出报文加上指定 VLAN tag
  • 交换机内部所有报文都带 VLAN tag

然后就可以了解 OpenWRT 的 VLAN 配置项的含义:

开启 VLAN 过滤时,硬件必须被编程为拒绝 VLAN ID 未在已编程允许的 VLAN ID 映射/规则范围内的 802.1Q 帧。若交换机端口未配置 PVID(默认 VLAN),则必须同时拒绝未标记帧。关闭过滤时,交换机必须接受任何 VLAN ID 的 802.1Q 帧且允许未标记帧。

  • 开启 VLAN 过滤:在桥接设备上启用 VLAN 功能,让交换机按配置的 VLAN 设置进行工作
  • 已标记: 带对应的 VLAN ID 的流量可原样流入流出该端口。流入流量没有 VLAN 的将打上该端口对应的 PVID
  • 未标记: 带对应的 VLAN ID 的流量可流入流出该端口。流出该端口的流量(注意是对应 VLAN ID)将被剥离 VLAN ID。流入流量没有 VLAN 的将打上该端口对应的 PVID
  • 主 VLAN:即该VLAN ID 是 该端口的 PVID,一个端口最多设一个 PVID。
  • 禁用: 该 VLAN 下禁止流入流出
  • local:是否为该 VLAN ID 创建对应的 802.1q 设备。没有设备的话不会被转发到 CPU 侧

还有一点需要注意的边际情况,你可以点击连接查看详细的例子

  • 一个端口有两个或以上 未标记 时才需要设置 PVID,没有设置的话默认为 VID 较小的
  • 一个端口没有 未标记 时,流入的无 VLAN 流量将被丢弃。除非该端口设置了 PVID
  • 不同 VLAN 如果设置了 local,路由器将根据防火墙设置在三层上不同防火墙区域间转发

SG105 Pro MTU VLAN / 端口 VLAN / 802.11q VLAN 是三种不同的 VLAN 模式

还有链路聚合相关 kmod-bonding、proto-bonding,笔者不太了解

MultiWAN 配置

安装 luci-i18n-mwan3-zh-cn iptables-nft ip6tables-nft

后面两个用于兼容高版本 OpenWRT 使用的nftable 注意默认配置会使网络不可用

无线配置

高版本 OpenWRT 下可以为同一个 SSID 设置多个密码,并导向不同 VLAN

参考 https://forum.openwrt.org/t/individual-per-passphrase-per-mac-wifi-vlans-using-wpa-psk-file-no-radius-required/161696 有非常详细的讨论和介绍