跳转至

L3 负载均衡实现网速叠加

  • 公司有 wifi 和 网线接入。但是每条链路都严格限制成了 8 Mbps 的速度。
  • 同时连接 wifi 和 网线,修改路由表,让不同目的地址走不同链路,再结合 aria2c 的多源地址下载,可以实现网速叠加。示例命令:
    aria2c -c -x 1 -s 2 https://alist.yfycloud.site:4433/d/Guest/Aria2/grpc.tar.xz\?sign\=JzEpqFm02IlJUuYfCQY3RZdU-NxYVyMElvQShef8Wy0\=:0  http://192.168.36.254:
    8000/grpc.tar.xz
    
    • -s 2 表示文件被分成 2 份并行下载,-x 1 表示每个 host 最大创建 1 个连接。这里指定了 2 个源 url,因此意思就是从每个源各创建 1 个连接。
    • 其中源 alist.yfycloud.site 为公网地址,走以太网。源 192.168.36.254 走 wg(底层基于 wifi)

但是上面使用了两台服务器提供相同的文件,由于有两个公网地址(使用 LAN 地址的链接底层 wg 仍然需要一个公网地址),因此分流比较简单。如果要缩减为 1 台服务器,就得有让即使是只有一个公网地址,也可以走不同路由的方法(即分别从以太网和 wifi 出去)

我开始的想法是本地使用一个 nginx 反向代理远程的链接,使得请求不同本地地址 127.0.0.x bind 到不同出口出去。nginx 支持 proxy_bind 参数,支持绑定到不同源 ip 地址。但是没想到 Windows 的路由非常不灵活,无法实现源地址的路由1

后面我利用一个简单的千兆路由器(xiaomi 4A gigabit version)将多台设备连接起来,并在每台设备上开启 nginx 反代,实现了只有一个公网地址的情况下,对同一个服务器的网速叠加效果。

nginx 的方法虽然有效,但是整个 setup 有点复杂(windows 需要配置 nginx,设置路由表),并且只能针对一个目的地址叠加网速。访问网页其它内容就无法加速了。

之后又去了解了 ECMP 技术,其中 per flow 和 per packet 分流的想法看上去确实很美好。如果可以实现的话,可以真正实现针对任意协议的网速叠加。(单连接在 per flow 情况下还无法提升速度,但是能够对不同单连接负载均衡也足够了)

各方案总结

  • 单机器 wifi 有线叠加
    • 方法:aria2c 指定多个源地址,不同地址使用不同路由。
    • 优点:简单,只需要使用 aria2c
    • 缺点:
      • 需要有多个公网 ip,否则无法根据目的地址路由
      • 几个链路就需要几个公网 ip (windows 不支持源地址路由导致的)
  • 多机器使用 nginx 反代
    • 方法:对上面方法的扩展,每台机器使用 nginx 反代,类似于 CDN 节点。叠加所有机器的链路
    • 优点
      • 提升了扩展性,不同机器可以使用相同的公网 ip 反代
    • 缺点
      • 设置相对复杂
      • 只能代理 HTTP(S)
      • 只能对设置的单个公网 ip 叠加链路,没设置的 ip 仍然只能使用一个链路
  • ECMP 负载均衡
    • 方法:利用 linux 的等效多径路由,在路由器上就流量负载均衡到各节点出去
    • 优点
      • 设置好后,对所有流量都可以负载均衡到不同链路。多线程下载就可以叠加网速。
    • 缺点
      • windows 垃圾的网络能力,无法 masqerade 导致方案一度不可行(因为只有一个网段,最后回来的流量必然还是得走一个链路)
      • 利用 wg 在 openwrt 路由器和 windows 间创建额外的局域网段,将 SRC NAT 转移到 openwrt 上,规避了 windows 的限制。
      • ECMP 的不足
        • 第一个问题是不知道怎么配置 ECMP 检测失效的链路。 wg 断开连接后,ECMP 并不会删除掉坏掉的 nexthop。在这种情况下,总是导致网络不可用(虽然其它 nexthop 是可用的)
        • 第二个问题是链路利用率低,因为是 per-flow 进行负载均衡。计算 L4 hash 后,每个连接等可能分配到每个链路,并不保证不使用相同链路。而简单的数学计算我们可以得到 3 个链路,3 个连接时,使用全部链路的概率仅为 2/9,4 个连接时仅为 9/16。这也是为什么 iperf3 -P 3 每次结果可能不一样。n 个 链路 n 个连接情况下,利用率随着 n 增大,下降到极限值 \(1 - 1/e \approx 0.63\)
      • ECMP 节点作为下载机器时,只能使用本地链路。
        • 如果路由器可以作为一个反代节点就可以解决,可惜我的 mi4a 只有 16M 的 flash,应该装不下(TODO:可以试试自己编译镜像)

下载 windows11 镜像,从一小时二十分钟,下降到二十分钟 image.png image.png

利用 mi4a + 两台 windows 共 3 个路径进行 ECMP,手机播放 B 站 4K 视频时的效果,CPU 有 %30-40 的占用。 image.png

方案一: 每台机器 nginx 反代,作为 CDN 节点

问题考虑的是单台机器上如何复用多个链路,但是问题可以推广到有多台机器,复用所有节点链路的情况。

此时已经增设了一台路由器,刷了 openwrt,各节点通过以太网连接路由器,因此每台机器剩余一个 wifi 链路用于连接外网。

为了利用每台机器的无线链路。当一个节点下载时,必然要将流量路由到每台机器,并且从无线链路出去

wg 配置

为了使每台机器的无线链路能够实际上网,需要使用 wg(利用 wifi UDP 不用登录的特性)。为了使连接 wg 的流量实际走无线而不是以太网,我们需要在 windows 上添加一条 Endpoint 的静态路由

route ADD 157.0.0.0 MASK 255.0.0.0  157.55.80.1 METRIC 3 IF 2
             destination^      ^mask      ^gateway     metric^    ^

# thinkpad
route ADD 112.32.59.100/32 10.3.120.1 METRIC 1 IF 7
route ADD 114.214.236.72/32 10.3.120.1 METRIC 1 IF 7

# s3-pro
sudo route ADD 114.214.236.72/32 10.3.100.1 METRIC 1 IF 3
sudo route ADD 112.32.59.100/32 10.3.100.1 METRIC 1 IF 3

# 删除路由方法
route delete 114.214.236.72/32

连接 wg 后,每台机器相当于有以太网(到路由器,再从路由器 WAN 出)和 wg(到我的公网机器) 两个网络出口。因此我们需要进行分流。由于使用以太网访问网络更稳定,因此默认路由走以太网,我们需要加速下载的 ip 走 wg。可以通过设置 wg 的 allow ip 来为我们自动设置路由。

nginx 配置

而为了从每台机器下载,我们可以在每台机器上安装 nginx 进行进行反向代理。反代的地址为通过 wg 可以访问的内网地址。

为什么不使用公网地址反代

我的服务器有公网地址,因此也可以在反代时使用公网地址。然而我的路由器也可以提供一个无线链路,而我的路由器为 mi4a,rom 只有 8MB,安装不下太多软件。因此路由器上无法安装 nginx 反代。如果其它机器上也是用公网地址反代的话,在该机器下载时,就无法利用路由器的链路。我也研究了 Windows 是否支持根据源地址进行路由,至少我没有发现可行方法。当目的地址相同时,即使 bind 了地址(nginx proxy_bind),windows 仍然只会按按照目的地址路由。

为了使下载时指定每台机器更方便,我们可以在路由器上为每台机器设置域名。或者使用自动 dhcp 的 hostname 作为域名。

示例 nginx 配置

server {
    listen 192.168.1.2:80;
    listen localhost:80;

    location / {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header Range $http_range;
            proxy_set_header If-Range $http_if_range;
            proxy_redirect off;
            # proxy_pass https://alist.yfycloud.site:4433/;
            proxy_pass http://192.168.35.2:5244;
            # the max size of file to upload
            client_max_body_size 20000m;
    }
}

aria2c 下载

最后使用 aria2c 从不同机器下载

aria2c -c -x 1 -s 3 \
    http://s3-pro.lan/d/Guest/Aria2/SW_DVD9_Win_Pro_11_22H2.6_64BIT_ChnSimp_Pro_Ent_EDU_N_MLF_X23-39378.ISO?sign=x20IQ5x4wziM_s4fsPLl9qt3943XgQ_CkfaFrpJxoVA=:0 \
    http://thinkpad10.lan/d/Guest/Aria2/SW_DVD9_Win_Pro_11_22H2.6_64BIT_ChnSimp_Pro_Ent_EDU_N_MLF_X23-39378.ISO?sign=x20IQ5x4wziM_s4fsPLl9qt3943XgQ_CkfaFrpJxoVA=:0\
https://alist.yfycloud.site:4433/d/Guest/Aria2/SW_DVD9_Win_Pro_11_22H2.6_64BIT_ChnSimp_Pro_Ent_EDU_N_MLF_X23-39378.ISO?sign=x20IQ5x4wziM_s4fsPLl9qt3943XgQ_CkfaFrpJxoVA=:0

版本 2,路由器自身 114.214.236.72 用于 wg 连接了,因此路由到了 wifi,而未登录的 wifi TCP 是通过不了的,所以路由器上的连接会失败。(这点可以利用后面的 UDP 和 TCP 走不通路由解决)

所以要使路由器发挥作用,可以在路由器上使用服务器另一个 lan 地址。

aria2c -c -x 1 -s 3 \
    http://s3-pro.lan/d/Guest/Aria2/SW_DVD9_Win_Pro_11_22H2.6_64BIT_ChnSimp_Pro_Ent_EDU_N_MLF_X23-39378.ISO?sign=x20IQ5x4wziM_s4fsPLl9qt3943XgQ_CkfaFrpJxoVA=:0 \
    http://thinkpad10.lan/d/Guest/Aria2/SW_DVD9_Win_Pro_11_22H2.6_64BIT_ChnSimp_Pro_Ent_EDU_N_MLF_X23-39378.ISO?sign=x20IQ5x4wziM_s4fsPLl9qt3943XgQ_CkfaFrpJxoVA=:0\
    http://192.168.35.3:5244/d/Guest/Aria2/SW_DVD9_Win_Pro_11_22H2.6_64BIT_ChnSimp_Pro_Ent_EDU_N_MLF_X23-39378.ISO?sign=x20IQ5x4wziM_s4fsPLl9qt3943XgQ_CkfaFrpJxoVA=:0

实测 3 个节点速度叠加成功

image.png

方案二:等价多路径路由 multipath routing (ECMP)

ECMP 概念

前面方式相当于需要手动指定多个代理节点,如果能实现路由器访问单个地址时自动使用不同接口多好。而 multipath routing ( ECMP ) 就可以做到这一点。

weighted ECMP

虽然我们这里都叫 equal cost multipath (ECMP),但实际上使用的是 weighted ECMP。扩展的 weight 用于控制选择 nexthop 时的倾向。如果一个设置为 10 一个设置为 1,表示我们希望前者的流量是后者的 10 倍。

ECMP 概念

With multipath routing you can distribute traffic destined to a single network over several paths (routes). It is an extension of the concept of the conventional routing table, where there is just one network→next hop association (or network→interface association or both). Instead, as we will see next, you can specify multiple next hops for one destination 3

ECMP 可以用来做负载均衡 3 image.png

有 Per-Packet 和 Per-Flow 两种方式

packet 是 ip 层传输的单个分组(数据报、数据包),而 flow 是有相同特性的一系列 packet 的集合,比如单个 TCP 连接的所有 packet。(linux 好像也是按照 flow 进行路由,以及对涉及到 NAT 的防火墙规则匹配的)

per-packet 和 per-flow 分流的区别2 image.png

理论上可以实现对单个 packet 的分流,但是实际上实现的是 per-flow 的分流

It is important to keep in mind that existing Multipath Routing implementation in Linux is designed to distribute flows of packets over multiple paths, not individual packets. Selecting route in a per-packet manner does not play well with TCP, IP fragments, or Path MTU Discovery. 4

ECMP 是怎么从多个 nexthop 中路由的呢

To associate a packet with a flow, the net stack computes a hash over a subset of packet header fields. The resulting hash value is what drives the next-hop selection.

使用的 hash 根据 ipv4 和 ipv6,以及是 forward 还是 locally generated packets 都有所不同

  • for forwarded IPv4 packets (L3 hash)
    { Source Address, Destination Address }
    
  • for locally generated IPv4 packets (L4 hash)
    { Source Address, Destination Address, Protocol, Source Port, Destination Port }
    
  • for forwarded IPv6 packets (L3 hash)
    { Source Address, Destination Address, Flow Label, Next Header (protocol) }
    
  • for locally generated IPv6 packets (L4 hash)
    { Source Address, Destination Address, Flow Label, Next Header (protocol), Source Port, Destination Port }
    

IPv6分组因为有Flow Label的存在,IPv6即使只用到L3 Hash也可以实现L4负载均衡 2

IPv4 现在 forward 和 output packet 使用相同 hash,并通过 fib_multipath_hash_policy 选项控制

However, with recently released Linux v4.12 selection of fields has changed a bit for IPv4 [4]. An L3 hash is used by default for both forwarded and locally generated traffic, but the user can choose to use the L4 hash, in both forward and local output path, with a new sysctl net.ipv4.fib_multipath_hash_policy. 4

fib_multipath_hash_policy - INTEGER: Controls which hash policy to use for multipath routes. Only valid for kernels built with CONFIG_IP_ROUTE_MULTIPATH enabled. Default: 0 (Layer 3) Possible values: 5 - 0 - Layer 3 - 1 - Layer 4 - 2 - Layer 3 or inner Layer 3 if present

openwrt 启用 ECMP

内核需要开启 CONFIG_IP_ROUTE_MULTIPATH=y 选项。至少我测试的 23.05 是默认打开的。

然后就只需要添加路由即可,格式:

ip route replace TARGET nexthop via GW1 dev DEV1 weight W1 nexthop via GW2 dev DEV2 weight W2

  • 遇到报错
    Error: either "to" is a duplicate, or "nexthop" is a garbage.
    
    • 安装ip-full 解决问题
  • 测试上面命令指定 nexthop 时,是不支持提示使用 src 的,不过影响不大。
# 测试单个地址
ip ro r 192.168.35.2 dev wg_local121
ip ro r 192.168.35.2 nexthop dev wg0 weight 1 nexthop dev wg_local121 weight 1

# 测试默认路由
ip ro r default nexthop dev wg0 weight 1 nexthop dev wg_local121 weight 1
ip ro r default nexthop dev wg0 weight 1 \
  nexthop dev wg_local121 weight 1 \
  nexthop dev wg_local120 weight 1

ip ro r 192.168.35.2 nexthop dev wg0 weight 1 \
  nexthop dev wg_local121 weight 1 \
  nexthop dev wg_local120 weight 1

测速结果

Edge 浏览器默认就会多线程,因此跑出了两个节点的速度(这里用的是一个节点开热点给路由器)

image.png

使用 aria2c,单个 src 即可

aria2c -x 2 -s 2 https://alist.yfycloud.site:4433/d/Guest/Aria2/SW_DVD9_Win_Pro_11_22H2.6_64BIT_ChnSimp_Pro_Ent_EDU_N_MLF_X23-39378.ISO?sign=x20IQ5x4wziM_s4fsPLl9qt3943XgQ_CkfaFrpJxoVA=:0

10/16 10:27:36 [NOTICE] Downloading 1 item(s)
[#b37202 370MiB/5.2GiB(6%) CN:2 DL:1.7MiB ETA:46m54s]

iftop -i <interface> 可以看到不同接口上的流量

image.png

conntrack 可以看到 dst 分别是路由器的两个网络出口地址

conntrack -L -d 114.214.236.72

tcp      6 7440 ESTABLISHED src=192.168.1.2 dst=114.214.236.72 sport=21737 dport=4433 packets=40560 bytes=1686476 src=114.214.236.72 dst=10.3.103.238 sport=4433 dport=21737 packets=76481 bytes=112075471 [ASSURED] mark=0 use=1

tcp      6 7439 ESTABLISHED src=192.168.1.2 dst=114.214.236.72 sport=21736 dport=4433 packets=31094 bytes=1336706 src=114.214.236.72 dst=192.168.137.50 sport=4433 dport=21736 packets=59620 bytes=105960001 [ASSURED] mark=0 use=1

使用 wg 后

~/ conntrack -L -d 192.168.35.2 -p tcp |grep 5244
tcp      6 7440 ESTABLISHED src=192.168.1.2 dst=192.168.35.2 sport=22999 dport=5244 packets=5268 bytes=217796 src=192.168.35.2 dst=10.121.31.202 sport=5244 dport=22999 packets=11238 bytes=20115018 [ASSURED] mark=0 use=1

conntrack v1.4.8 (conntrack-tools): 7 flow entries have been shown.
tcp      6 7440 ESTABLISHED src=192.168.1.2 dst=192.168.35.2 sport=23000 dport=5244 packets=6952 bytes=283793 src=192.168.35.2 dst=10.0.31.202 sport=5244 dport=23000 packets=13575 bytes=20894778 [ASSURED] mark=0 use=1

windows 不支持 masquerade 的妥协方法

如图,因为 windows 不支持转发时进行 src nat,导致实际上并不能利用 3 个及以上节点 ECMP.drawio.png

如图可以使用如下方式创建额外的网段 image.png

这样在 op1 上设置不同 peer 时,只需要路由该设备对应网段即可。如图中的 S3-Pro 的 wg0 到 op1 wg0 的连接。额外增加 10.120.31.0/24 这个网段

config wireguard_wg0
      option route_allowed_ips '1'
      list allowed_ips '10.0.31.120/32'
      list allowed_ips '10.120.31.0/24'

mi4a 上可以设置源进源出

ip ro add default dev wg_local120 table wg120
ip ro add default dev wg_local121 table wg121
ip ro add default dev wg0 table wg0
ip ru add from 10.120.31.0/24 lookup wg120
ip ru add from 10.121.31.0/24 lookup wg121
ip ru add from 10.0.31.0/24 lookup wg0

连接到 op2 上有时延迟更低,因此选择 wg 到 op2。不过 op2 对于目的地址为 op1 lan 的地址,会通过 wg_s2s 转发到 op1 上,并且没有 NAT。这会导致 op1 上的回包无法路由。

解决办法为 op2 针对 wg0 进入的包进行 NAT

ip ru add iif wg0 lookup lan prior 4

root@op2 ➜  ~ ipt lan
192.168.35.0/24 dev wg_s2s proto static scope link

nft 规则,命令行方式

 nft add rule inet fw4 srcnat_wg_s2s iifname wg0 oifname wg_s2s counter masquerade

openwrt 持久化配置:

/etc/nftables.d/12-wg0-nat.nft

chain srcnat_wg0_from {
    type nat hook postrouting priority srcnat; policy accept;
    iifname wg0 oifname wg_s2s counter masquerade comment "Masquerade traffic from wg0 to   wg_s2s"
}

综合两种方法

ECMP 方案除了有概率用不满所有链路,作为路径的节点,自身所有流量还会直接从 wg 接口出去,不会走路由器(如果 windows 有根据源地址进行路由的功能就可以避免,可惜没有),因此每个节点仍然只能使用单条链路。只有连接路由器的其它设备如手机可以充分利用 ECMP。

因此,上面架构图中实际上还是保留一台设备 A 不作为 ECMP 路径,B 和路由器 2 条路经做 ECMP 即可。那么在有 ECMP 情况下能否使用之前方案跑满 3 条路径呢?

由于最终 aria2c 命令需要使用 3 个源地址,每个对应 1 条链路,我们需要 3 个不同地址

对于路由器来说,我们需要一个地址避免路由器上对该地址重复使用 ECMP,需要占用一个地址。可以分配设备的一个局域网地址。即路由器上默认路由走 ECMP,一个局域网地址走路由器链路。

ip ro r 192.168.35.2 dev wg0
ip ro r default nexthop dev wg0 weight 1 nexthop dev wg_local121 weight 1

对于设备 B 来说,作为 ECMP 路径,所有流量都会走自己的链路,我们可以使用 nginx 在本地反代服务器任意地址,这样使用 B 的 lan 地址即可,避免占用服务器的地址。

对于设备 A 来说,需要和路由器地址区分,这里分配服务器的另一个局域网地址。记住需要在 wg 中设置 allow ip 让该地址走 A 的链路。

因此最后得到我们的命令

url_path=/d/Guest/Aria2/SW_DVD9_Win_Pro_11_22H2.6_64BIT_ChnSimp_Pro_Ent_EDU_N_MLF_X23-39378.ISO?sign=x20IQ5x4wziM_s4fsPLl9qt3943XgQ_CkfaFrpJxoVA=:0

aria2c -c -x 1 -s 3 http://thinkpad10.lan$url_path http://192.168.35.3:5244$url_path http://192.168.35.2:5244$url_path

总结来说就是

  • 单个节点,有多少条上游链路就需要多少地址(基于 wireguard 可以使用 LAN 地址,但是连接 wg 仍然需要两个公网地址进行分流)
  • 不同节点间由于使用 nginx 反代,因此互不影响,可以使用相同的公网地址。
  • 举例来说,B 有两条链路,因此我的文件服务器就需要最少两个公网地址(我的服务器只有一个公网地址,但是好在我出租屋还有一条宽带,可以提供一个公网地址,而出租屋通过 wg 连接到我的服务器网络即可)
  • A 提供一条链路(另一条连接路由器不算),需要一个地址(wg + LAN 地址即可)
  • 路由器有一条链路,需要一个地址(同样是 wg + LAN 地址)。由于路由器无法设置反代,因此不能和非 nginx 节点(比如下载节点)使用的地址冲突,否则最后的 aria2c 命令中就会有重复的 URI。

遇到的坑

  • windows wg 接口重新连接后,需要重新开启转发
    # Powershell(admin) 中执行
    Set-NetIPInterface -Forwarding Enabled
    

aria2c 指定多个 -s 时,如果是相同 host 不同端口,那么需要把 -x 设置为 URI 数量,否则只会建立一个连接。

参考资料

[译] 现代网络负载均衡与代理导论(2017) (arthurchiao.art)

单网卡开多个虚拟无线接口

Q: openwrt wlan0 如何多次连接同一个 SSID,并获得多个 ip 地址 chatgpt:

在 OpenWrt 上,想要让同一个 Wi-Fi 网卡(wlan0)多次连接到同一个 SSID,并获取多个 IP 地址,通常可以通过创建多个虚拟无线接口(VIF,Virtual Interfaces)来实现。通过给每个虚拟接口分配不同的 MAC 地址,它们可以分别从路由器获取不同的 IP 地址

iw list | grep -A 10 "interface combinations"

mi4a

valid interface combinations:
                 * #{ IBSS } <= 1, #{ managed, AP, mesh point, P2P-client, P2P-GO } <= 8,
                   total <= 8, #channels <= 1, STA/AP BI must match, radar detect widths: { 20 MHz (no HT), 20 MHz, 40 MHz, 80 MHz }

        HT Capability overrides:
                 * MCS: ff ff ff ff ff ff ff ff ff ff
                 * maximum A-MSDU length
                 * supported channel width
                 * short GI for 40 MHz
                 * max A-MPDU length exponent
                 * min MPDU start spacing
--
        valid interface combinations:
                 * #{ IBSS } <= 1, #{ managed, AP, mesh point, P2P-client, P2P-GO } <= 4,
                   total <= 4, #channels <= 1, STA/AP BI must match
        HT Capability overrides:
                 * MCS: ff ff ff ff ff ff ff ff ff ff
                 * maximum A-MSDU length
                 * supported channel width
                 * short GI for 40 MHz
                 * max A-MPDU length exponent
                 * min MPDU start spacing
        max # scan plans: 

  • #{ IBSS } <= 1: 表示网卡最多支持一个 IBSS(Independent Basic Service Set,独立基本服务集,即自组织网络,也称为 Ad-Hoc 模式)。
  • #{ managed, AP, mesh point, P2P-client, P2P-GO } <= 8: 表示在 managed(STA,客户端模式)、AP(接入点模式)、mesh point(网状网络模式)、P2P-client(Wi-Fi Direct 客户端)、P2P-GO(Wi-Fi Direct 组所有者)这几种模式中,总共可以有最多 8 个接口运行。
  • total <= 8: 这是总接口数量的限制,表示可以同时运行最多 8 个接口(包括所有类型的接口)。
  • #channels <= 1: 这是信道限制,表示网卡只能在一个信道上工作。也就是说,即使你创建了多个接口,它们也必须在同一个 Wi-Fi 信道上工作(不能同时使用不同频段,例如 2.4GHz 和 5GHz)。
  • STA/AP BI must match: 这意味着 Station(客户端)和 AP(接入点)模式的信标间隔(Beacon Interval)必须匹配。
  • radar detect widths: { 20 MHz (no HT), 20 MHz, 40 MHz, 80 MHz }: 这表示网卡支持在雷达检测的情况下运行的频宽范围,通常与 DFS(动态频率选择)相关。这里网卡支持 20MHz 到 80MHz 的频宽。

实验可以创建多个 client,但是只有一个可以连接。2.4G 芯片可以开 4 个,状态都是 enable,但是同时只有一个 interface 可以 dhcp 获得地址。5G 芯片,只有一个显示为 enable 状态。不知道是因为硬件的问题,总之不能多开

image.png

为什么要不同 SSID 呢?

In theory, if there were multiple different SSIDs (different names), it would be possible to connect to more than one concurrently, Mixing multiple 'ap' and 'sta' interfaces on the same hardware radio - Installing and Using OpenWrt - OpenWrt Forum

Can a WiFi station be connected to more than one SSID at once? : r/networking (reddit.com)

另一个思路:bridge wifi client

如果能从 wlan1 接口发送多个 dhcp request 获得多个 ip 好像也行?这需要我创建多个虚拟接口,然后和 wifi 接口 bridge 起来。

然而看到 bridge wifi 好像是不可行的?仔细一想这里的 bridge 岂不是和 mesh 有点像了。那么参考 mesh 设置,可能需要对方的路由器也支持你才能 bridge。 Connect to WiFi, Bridge to Ethernet (DHCP) - MikroTik

In general attempting to bridge wifi clients doesn't work. If the AP is a mikrotik running 6.xx versions, it has features that do work well with mikrotik clients, just choose station bridge on the maplite, and add it to the bridge. (choose ap bridge on the mikrotik AP) Otherwise, you can try setting the maplite wifi client to be a station pseudo bridge. That sort of works, sometimes. Unless you really need it bridged, you are probably better off just making the wifi interface a wan interface, and letting the devices behind it get an ip address from the maplite's local dhcp server and get to the main network via Nat.

这才意识到以前的 bridge wifi 和 lan 是因为 wifi 工作在 AP 模式,所以才能和 lan bridge 在一起。 [OpenWrt Wiki] Wi-Fi Extender/Repeater with Bridged AP over Ethernet image.png

找到了不能 bridge 的佐证,也提到了确实可以 AP + STA。[OpenWrt-Devel] Multiple Wi-Fi client/AP interfaces on one radio (was: Change OpenWrt Wifi default settings) (infradead.org)

Also note that, if you want to bridge a STA interface onto anything else, it'll need to be in "WDS" mode and the the AP to which it's connecting will also need to be in "WDS" mode (note the "option wds '1'", in my example above), because the standard "3-address mode" of Wi-Fi isn't bridgeable (and note that WDS "4-address mode" is bridgeable, but not standardised across different platforms--so you're probably OK so long as all of your equipment is running Linux and using the same Wi-Fi driver, but don't expect to bridge a STA interface that's connected non-Linux Wi-Fi router).

macvlan

还是在那篇 ECMP 负载均衡的文章2里看到了 macvlan 的用法,好像可以实现一个网卡虚拟出多个 mac 地址,然后获得多个地址。

路由器上的RJ45网口是绑定到VLAN上的,如果是添加虚拟网卡到一个VLAN上就是单线多拨,添加到多个VLAN上就可以做多线多拨了 这里的eth0.2对应于WAN口(在Interface -> Switch可以看到WAN绑定在VLAN 2上),不同的设备可能有些许不同,这里在eth0.2上添加两个虚拟网卡

for i in `seq 1 2`
do
  ip link add link eth0.2 name veth$i type macvlan
done

但是我测试在 wlan1_0 上创建多个 veth 后,确实能在 wlan1_0 上抓到不同 mac 的 DHCP 请求,但是却获得不了 ip 地址。

如何让 udp 和 tcp 走不同路由

路由器通过 udp wg 到我的公网机器上网。这导致我的公网 ip 需要走 wifi 路由,而该 wifi 不登陆只有 UDP 能通。因此到我公网 ip 的 TCP 流量就无法连接了。有没有办法让 UDP 流量走 wifi,而 tcp 流量走 wg 建立后的路由呢?

答案是通过 nftable 给 UDP 流量设置 fwmark,然后使用策略路由对 UDP 流量进行分流。

UDPATE:有更简单方案,ip route 支持根据 ipproto 分流,一行命令就可以实现针对 UDP 和 TCP 的策略路由。不过目前 openwrt luci 不支持配置(底层的 uci 不支持)。给提了一个 issue:Missing ipproto keyword in network config rule section · Issue #7330 · openwrt/luci

ip ru add from all ipproto udp table udp_table prior 10

设置策略路由

UDP 流量使用 fwmark 2 进行路由表的选择(之后使用 nft 给 udp 包打上 mark)

# 创建额外的路由表
echo "1000 udp_table" >> /etc/iproute2/rt_tables

# 将 UDP 流量定向到 udp_table 
ip rule add fwmark 2 lookup udp_table prior 10

# 为 UDP 流量配置特定路由 
ip route add 114.214.236.72 via 10.3.100.1 dev wlan1_0 table udp_table

nft 设置 mark

p.s 刚开始无论使用 chatgpt 还是 google 搜索,都是提示在 prerouting chain 添加规则。然而我们这里是想要让 router 自身连接 wg 时,走特定的路由,因此应该在 output chain 中添加规则。打上 mark 后,会触发 rerouting6

使用 nft 打标记

nft add table ip mangle
nft add chain ip mangle output \{ type route hook output priority -150 \; \}
nft add rule ip mangle output ip daddr 114.214.236.72 udp dport 51820 counter mark set 2

nft -a list table ip mangle
nft delete rule ip mangle prerouting handle 7

使用 openwrt 配置文件方式,可以持久化

vim /etc/config/firewall

config include
        option enabled  1
        option type  'script'
        option path  '/etc/firewall.user'
        option fw4_compatible 1

~/ vim /etc/firewall.user
nft add table ip mangle
nft add chain ip mangle output \{ type route hook output priority -150 \; \}
nft add rule ip mangle output udp dport 51820 counter mark set 2

conntrak 验证结果

opkg install conntrack 

走 wlan1_0 (guest wiif)

  • contrack 输出中,17 表示是 udp 协议,tcp 是 6
  • 后面的数字是超时时间,默认 3 分钟不 active 就会断开连接
    ~/ conntrack -L -d 114.214.236.72
    udp      17 171 src=10.3.103.238 dst=114.214.236.72 sport=51820 dport=51820 packets=150 bytes=22092 src=114.214.236.72 dst=10.3.103.238 sport=51820 dport=51820 packets=149 bytes=33352 [ASSURED] mark=0 use=1
    

走 wlan0_3 (Hotspot)

udp      17 172 src=192.168.137.238 dst=114.214.236.72 sport=51820 dport=51820 packets=6 bytes=712 src=114.214.236.72 dst=192.168.137.238 sport=51820 dport=51820 packets=6 bytes=808 [ASSURED] mark=0 use=1

udp 分流成功

  • 可以看到特别的是 src 是 192.168.137.238,回包的 dst 却变成了 10.3.103.238
    ~/ conntrack -L -d 114.214.236.72
    icmp     1 29 src=192.168.137.238 dst=114.214.236.72 type=8 code=0 id=8998 packets=29 bytes=2436 src=114.214.236.72 dst=192.168.137.238 type=0 code=0 id=8998 packets=26 bytes=2184 mark=0 use=1
    udp      17 179 src=192.168.137.238 dst=114.214.236.72 sport=51820 dport=51820 packets=109 bytes=16288 src=114.214.236.72 dst=10.3.103.238 sport=51820 dport=41685 packets=106 bytes=16228 [ASSURED] mark=0 use=2
    

参考资料