阅读视图

发现新文章,点击刷新页面。
🔲 ☆

关于威联通和Tailscale的那些坑-自建Derp

前言

由于不明原因,QNAP应用商店官方源里的Tailscale版本很旧,是1.40.5

而且你似乎也没有什么很方便的办法能够直接更新它,况且威联通官方一直不更新也是个很奇怪的事情

好在Tailscale考虑到一堆人还在Win7之类的已经EOL的设备上使用旧版本,所以旧版本也能够正常登录,看起来也能够正常使用

但事情真的是这样吗?

行为

具体表现为:

在能够直接建立Direct连接的情况下,一切正常

在无法direct,需要依靠derp转发的时候,会出现这样的现象

  • NAS ->(tailscale ping)-> PC ok
  • pc ->(tailscale ping)-> NAS 请求超时
  • tailscale status看到NAS设备在线,而非offline

自建Derp的变化

Tailscale为了便于大家自建derp,在后续更新中支援了自签名证书的方式

这极大了方便国内用户的使用,因为多数人在国内没有备案域名,自然也就不能在国内的机器上使用443端口+域名跑流量(甚至有些严格的ISP会连非80/8080/443的TCP连接也一并block)

并且多数CA机构并不支持对个人用户直接签发ip的证书,或者有也很贵

在支持自签名证书之后,国内的备案域名这一最后的障碍也不复存在,随便一台开放443端口的机器都能成为Derp的搭建对象

如何信任自签名证书

自己签发的三无证书显然是不会受到操作系统本身的信任链信任的

tailscale的做法是在ACL里写入原始证书的hash用于作为验证,就像这样

{
  "derpMap": {
    "Regions": {
      "900": {
        ……,
        "Nodes": [
          {
            ……,
            "CertName": "sha256-raw:xxxxxx"
          }
        ]
      }
    }
  }
}

问题分析

在配置ACL后,tailscale会自动选择最近的derp作为转发,官方的中继都在国外,自然不可能延迟比你自己的derp还低

所以控制平面检测到你自己的derp在线,于是选择了通过你的derp进行转发

从nas到pc,derp和pc转发握手的时候,pc通过acl的hash校验,信任了证书,因此能够正常通讯

但是反过来,derp和nas握手的时候,由于nas上的老古董tailscale client并不支持自签名证书,所以信任链校验不通过

需要注意到是,假如选定的derp实际不可用的话,tailscale并没有fallback机制,也就是无法自动回到官方derp上

这里的不可用有两种情况,除了我们这里的证书不受证书链信任以外,也有种情况是这个derp做了鉴权,而你没有权限

最后,无法从pc向nas发起连接

解决方式

解决方式其实很简单,解决证书问题或者解决客户端问题

证书比较麻烦,无外乎就这么几种选择

  • 放弃自建derp,继续只用官方的
  • 换海外的节点+CA机构签发的证书建derp
  • 国内节点+备案+CA机构签发的证书建derp

客户端的话,相对就好办的多

先把官方源的1.40客户端卸掉,然后应用商店加入第三方源

https://www.myqnap.org/repo.xml

从这个源里面重新安装tailscale,这边的版本是1.92.5,已经支持自签名证书了

如果你发现这个源加载不出来的话也可以直接访问 https://www.myqnap.org/

去网页上下载qpkg安装包,然后手动上传到应用商店里安装

碎碎念

所以你QNAP官方源为啥就不肯更新Tailscale呢?

🔲 ☆

网络抓包的技巧

虽然这个系列的文章都是聚焦于如何通过分析网络抓包文件,结合网络知识,来解决实际的问题的,但是分析之前的步骤——抓包,也是同样重要!很显然,如果不会抓包,那么网络分析去分析什么呢?

抓得一手好包也是很厉害的!笔者遇到过很多次情况,虽然我们无法直接定位根因,但是同事能够精准地捕获到问题的现象,把问题描述给相关的网络专家,传给他们抓包文件,专家一看到准确的抓包文件,就可以很快解决问题了!

可惜的是,抓包的技巧无法像网络分析那样可以通过文章来出谜题,来让读者小试牛刀。所以,这篇文章就来写一下一些常用的抓包方式和技巧,希望能补齐这一块内容。

Tcpdump

tcpdump 命令是我们最常用的抓包工具了1

tcpdump -i eth0 icmp and host 1.1.1.1

这个命令就可以抓取到所有通过 eth0 去 ping 1.1.1.1 这个地址的包。

-i eth0 的意思是抓取指定的 interface,如果不指定,tcpdump 会默认选择一个。但是推荐每次都指定好这个参数,这样就没有不确定性了。如果使用 -i any 就可以抓取所有常规端口(文档的原文是 all regular network interfaces),但是什么属于「常规端口」就取决于操作系统的实现了。所以,建议也是如果要抓取多个 interface 来分析的话,就多开几个 tcpdump 进程,这样更加稳定一些。

这个参数非常有用,比如,在定位 ARP 问题的时候,我们需要确定每一个物理接口收发 ARP 的情况,就可以开多个进程分别 dump 每一个 interface 的网络;在定位 Linux 网络栈不通的情况时2,比如有 macvlan,vlan,veth 等复杂的 driver,可以用 tcpdump 对每一个接口 dump,看下包丢在哪里。

icmp and host 1.1.1.1 这个就是包过滤的表达式了,icmp 表示只抓取 icmp 协议,host 1.1.1.1 表示只抓取 src ip 或者 dst ip 是 1.1.1.1 的包。这种包过滤表达式其实是 pcap-filter(7)3 提供的,所以要想看语法是怎么定义的,看 pcap-filter 的文档就可以了。pcap-filter 支持的语法很灵活,能做的事情很多,基本上想抓什么样的包都可以写出来。但是我们没有必要把所有的语法都记住,因为常用的抓包都是比较简单的。可以找一个 tcpdump exmaple4 看一下,基本就够用了。其次,我们一般不会直接从 tcpdump 就分析出来问题原因,所以这个语法最重要的作用是把我们想要的包抓到,然后为了抓包性能更高,抓包文件更小,我们想要对抓包定义的更精确一些。其实,多抓一些包也没有什么问题,如果不确定怎么过滤出来 TCP SYN+ACK 的包,那不妨就把所有的 SYN 包全抓到,然后再用 Wireshark 这种工具来分析吧。最后,我们现在有 AI 了,用 AI 来写 pcap-filter 也是一个不错的方法,因为这种语法难写,但是很容易验证正确性。

Tcpdump 一些常用的其他参数如下:

  • -n 不解析主机名和端口号,保留原始的数字
  • -v, -vv, -vvv v 越多表示输出的信息越详细
  • -c 5 表示抓到 5 个包之后就退出
  • -e 显示二层的 link layer header,这样就可以看到 MAC 地址了
  • -Q 可以指定抓包方向,可以选的有 in, out, inout
  • -A 可以展示包的内容,tcpdump 默认是只根据不同的协议展示 header 信息的。在线上排查问题的时候,我们往往需要通过特殊请求的关键字来定位到单个请求的情况进行排查,这样 -A 展示出来包的内容就格外有用。
通过 -A 参数来抓取特定的 HTTP 请求

这里分享一个特殊的技巧,就是发标记请求来定位问题。比如 A 通过 B 代理发请求给 C,现在网络不通,我们要定位 B 收到了请求没有,才知道是 B 的问题还是 C 的问题。但是 B 本身就有很多线上流量,怎么知道 A 发送的请求到达 B 了没有呢?我们可以在 B 进行 tcpdump:tcpdump -i eth0 tcp | grep asdf123 -A 10,然后我们从 A 发送一个请求:curl http://host-C.com/asdf123asdf123 就是我们在请求里面放上的标记,如果 B 能够正常转发,我们就可以 match 到这个请求。当然了,这种技巧只适用于 HTTP 这种明文协议。

Wireshark 离线分析

有些问题很难直接在 tcpdump 的终端分析出来问题,比如涉及 sequence number 分析的,重传分析之类的,我们需要人工对比 seq number,真是一项费眼睛的工作!所以如上所说,我们也经常在机器上用 tcpudmp 抓包保存成 .pcap 文件,下载到本地用 Wireshark 分析。Wireshark 就可以自动根据 sequence number 告诉我们重传等信息了!

Wireshark 可以展示出来 Dup ACK 和 Retransmission 等信息

具体的操作方式是,用 tcpdump -i eth0 -w file.pcap icmp 来进行抓包,-w file.pcap 表示把抓包文件保存为 file.pcap,抓包结束后,就可以把这个文件用 rsync 或者 scp 下载到本地,用 Wireshark 打开了。

.pcap 文件是一种标准的二进制抓包文件5,很多抓包分析工具都支持这种格式的解析,比如 tcpdump, wireshark, scapy 等等,如果想写代码进行更加定制化的分析,也可以用已有的库6解析,就如同用 json 库来解析 json 文件一样。

使用 wireshark 的命令行工具 tshark 可以解析二进制 pcap 文件到 json 格式

使用 -w 写入文件的时候有一个小问题,就是 tcpdump 原本的到终端的输出没有了。有两种方式可以解决,第一种是用 tcpdump 自带的 --print 功能:

tcpdump -i eth0 -w file.pcap --print

--print 会让 tcpdump 把内容输出到屏幕,即使当前使用了 -w 参数。

第二种就是用 tee,在写入文件的同时,也写入到 stdout。

tcpdump -i eth0 -U -w - | tee test.pcap | tcpdump -r -

其中,第一个 tcpdump 把抓包文件写入到 stdout(-w stdout,注意其中的 -U 表示按照 packet buffer,即来一个 packet 就输出一个到 stdout,而不是等 buffer 满了才进行输出),然后 tee 这里做了分流,把 stdin(tcpdump 的 stdout)同时输出到文件和 stdout。由于这里的 stdout 是 tcdpump 输出的二进制抓包内容,所以我们需要再用 tcpdump 解析这个二进制内容,-r - 表示从 stdin 读入。

还有一个技巧是 -s 参数,默认情况下 tcpdump 会保存所有抓到的内容,但是在分析某些问题的时候,尤其是 TCP 性能问题,我们其实不需要 TCP 传输的 payload 内容,只看 TCP 包的 header(序列号部分)就知道传输的速度了,所以可以用 -s 40 来只抓取前 40 个 bytes,有了 IP header 和 TCP header,就足够分析了。(如果担心有 TCP option 的存在,可以用 -s 54

其他的一些经验

知道包是从哪里抓到的,很重要。在排查问题的时候,拿到抓包文件,应该第一时间确认抓包的位置。否则,就可能连自己看到的问题是现象还是根因都分不清楚。建议在复杂的结构中画一个拓扑图来对照分析,在定位 Linux 网络栈的问题时,如果接口拓扑非常复杂,也建议画一个拓扑图来分析。

可以从网络的多端抓包对照分析。发送端的抓包不一定等于接受端,尤其分析 TCP 问题的时候。可以同时在发送端和接收端进行抓包,然后对照分析。

在使用 tcpdump 的时候,要尤其注意,我们抓到的包已经经过了网卡驱动的处理,网卡驱动经常会帮 CPU 做一些 offload 的工作,比如把可能因网卡的 GRO/LRO 等特性,导致多个小包在抓包时被合并为一个较大的数据包,或者网卡帮助卸载了 vlan tag 等,我们用 tcpdump 抓到的包不一定是真正在网络上传输的包7。要格外注意。

注意抓包不要抓重。比如有人很喜欢用 tcpdump -i any ... 抓全部的包回来慢慢分析。然后下载下来抓包文件就吓坏了——重传率高达 50%!

抓包抓重了的情况

在 Linux 中的网卡配置有 slave 和 master 的时候很容易发生这种情况,比如有 bonding 配置8-i any 会从 slave 抓包包,从 master 又抓到一次,然后在 Wireshark 看来,所有的包都被重传了。实际是同一个包先后经过 slave 和 master 而已。

抓包的时候最好把相关 host 的 ICMP 协议包也一起抓了。因为 ICMP 是重要的 control message,TCP 在传输的时候,不光有 TCP 协议,可能还会用 ICMP 协议来传递一些信息。比如 PMTUD9,以及之前遇到过的这个问题10,都是涉及到 ICMP 包。如果只按照 TCP 协议来抓包,那这个重要的信息就错过了。

SPAN 交换机抓包和RSPAN 远程抓包

除了我们熟悉的 Linux 抓包,其实网络设备上也可以抓包的。我们一般叫它「端口镜像」技术,故名思义,原理就是把网络设备的一个端口的流量全部复制到另一个端口,而另一个端口连接的就是我们的抓包程序。

SPAN 原理,图来自 Cisco
  1. 文档的主页:https://www.tcpdump.org/manpages/tcpdump.1.html ↩
  2. Keepalived 脑裂问题排查 ↩
  3. pcap-filter 文档在这里:https://www.tcpdump.org/manpages/pcap-filter.7.html ↩
  4. 比如这一个:https://danielmiessler.com/blog/tcpdump ↩
  5. IETF 的文件规范定义:https://www.ietf.org/archive/id/draft-gharris-opsawg-pcap-01.html ↩
  6. Python 可以使用 scapy (https://scapy.readthedocs.io/en/latest/usage.html#reading-pcap-files)读取 pcap 文件,golang 可以使用这个库进行解析:https://pkg.go.dev/github.com/google/gopacket/pcap ↩
  7. 参考 有关 MTU 和 MSS 的一切 一文中,「道理我都懂,但是我的抓的包怎么大??」 ↩
  8. 数据中心网络高可用技术之从服务器到交换机:active-backup ↩
  9. 真实世界中的 PMTUD ↩
  10. 由 ICMP Redirect 消息引起的丢包问题排查 ↩

==计算机网络实用技术 目录==

这篇文章是计算机网络实用技术系列文章中的一篇,这个系列正在连载中,我计划用这个系列的文章来分享一些网络抓包分析的实用技术。这些文章都是总结了我的工作经历中遇到的问题,经过精心构造和编写,每个文件附带抓包文件,通过实战来学习网路分析。

如果本文对您有帮助,欢迎扫博客右侧二维码打赏支持,正是订阅者的支持,让我公开写这个系列成为可能,感谢!

没有链接的目录还没有写完,敬请期待……

  1. 序章
  2. 抓包技术以及技巧
  3. 理解网络的分层模型
  4. 数据是如何路由的
  5. 网络问题排查的思路和技巧
  6. 不可以用路由器?
  7. 网工闯了什么祸?
  8. 网络中的环路和防环技术
  9. 延迟增加了多少?
  10. TCP 延迟分析
  11. 压测的时候 QPS 为什么上不去?
  12. 压测的时候 QPS 为什么上不去?答案和解析
  13. 重新认识 TCP 的握手和挥手
  14. 重新认识 TCP 的握手和挥手:答案和解析
  15. TCP 下载速度为什么这么慢?
  16. TCP 长肥管道性能分析
  17. 请求为什么超时了?
  18. 请求为什么超时了?答案和解析
  19. 后记:学习网络的一点经验分享
与本博客的其他页面不同,本页面使用 署名-非商业性使用-禁止演绎 4.0 国际 协议。
🔲 ☆

Linux interface Vlan 和 Bond 配置错误问题排查

昨天同事报告了一个 Linux 机器网络问题,现象是:一台服务器无法 ping 192.168.1.253,但是可以 ping 192.168.1.252 和 192.168.1.254. 这三个 IP 都是交换机的 IP,并且和和服务器的 IP 在同一个子网下。

服务器使用了 bond1 分别连接两台交换机2,两台交换机通过 VRRP 协议提供一个高可用的网关 IP3。其中,网段的最高位一般是 VRRP 的 VIP,即 192.168.1.254,而最高位 -1 和 -2 分别是两个交换机的物理 IP,即 192.168.1.253 和 192.168.1.252 分别是两台交换机。

于是,看到这个现象,自然而然地想到是其中一台交换机有问题,192.168.1.253 已经挂了,192.168.1.252 还存活,并且担任了 192.168.1.254 的 VIP 的责任。

先去这台服务器 ping 了一下,果然是 ping 不通的,ping 显示的错误信息是 Destination Host Unreachable。然后在服务器抓包,确认下 ICMP reply 确实没有发送回来。tcpdump -i bond0 icmp. 抓包确实没有看到 ICMP reply 包,但是奇怪的是,居然连 ICMP echo 也没有抓到

之后又去检查了交换机的配置,包括 channel-group,VLAN 配置,ACL 等等,也确认了下两台交换机之间的横连状态是正常的。这时候看起来不像是交换机的问题了。使用另一台服务器 ping 了一下这三个 IP,.252, .253, .254 都是通的。那应该是服务器的问题而不是交换机的问题。

其实这部分有些走弯路,因为 ping 明确显示 Destination Host Unreachable,说明这个包并没有发出去;而且 tcpdump 也没有抓到包,也可以印证。

接下来继续在服务器上定位问题。

ICMP 发包有问题,就先检查一下发包链路。之前遇到过类似错误,是 iptables 的 OUTPUT chain 把包 drop 了,于是先检查了 iptables,确认没有相关的 DROP。

ICMP 是基于 IP 层的协议,IP 层的协议依赖 ARP 协议来找到 MAC 地址,然后封装成二层 Frame,才能发出去,接下来去检查 ARP。(其实上一步直接检查 iptables 是不合理的,ARP 是第一步,有了 ARP 才可能构造出来完整的 Frame 开始发送,应该先从 ARP 开始排查)。

检查 arp -a | grep .253,发现 ARP 的 cache 结果是 <incomplete>. 然后用 arping 192.168.1.253 验证 ARP request 是否能得到正常的 reply,发现结果都是 Timeout。

到这里已经知道为什么 ping 会失败了,因为服务器得不到这个 IP 对应的 ARP 请求,所以 ping 无法将 ICMP request 的包发送出去,直接报错了。

接下来就定位为什么 ARP 会失败。

正常来说,ARP 应该从 bond0 接口发送出去一个 request,然后收到一个 reply,刷新服务器的 ARP cache entry。

服务器的 interface 配置如下,服务器所在的 VLAN 是 1000,和交换机做了 Trunking4,发送包的路由是走 bond0.1000@bond0 这个 interface,bond0.1000@bond0 是一个虚拟 interface,主要的功能是,发包的时候对包进行 802.1Q VLAN 封装,然后通过底层的 interface——在这里是 bond0——发送出去,收包的时候对 VLAN 进行解封装。

root@ubuntu-1:/$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether b6:db:e6:76:dd:8a brd ff:ff:ff:ff:ff:ff
3: bond0.1000@bond0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether b6:db:e6:76:dd:8a brd ff:ff:ff:ff:ff:ff
4: eth0.1000@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether b6:db:e6:76:dd:8a brd ff:ff:ff:ff:ff:ff
143: eth0: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc fq_codel master bond0 state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether b6:db:e6:76:dd:8a brd ff:ff:ff:ff:ff:ff
144: eth1: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc fq_codel master bond0 state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether b6:db:e6:76:dd:8a brd ff:ff:ff:ff:ff:ff
接口的逻辑图

我首先在 bond0 抓包,确认 ARP 的发送和接收在协议上是正常的。

结果在这一步就发现问题了,bond0 抓包发现,只有发出去的包,没有收到的包。

为啥交换机不响应 ARP 了呢?

这时候又怀疑是交换机的问题,去检查了交换机的两个端口配置。没有发现问题。而且在其他机器上,ping 和 arping 都是没有问题的,交换机设备的问题可能性比较小。

也不会是服务器安全策略的问题,如果是的话,tcpdump 也会先抓到包的,在后面才会被 iptables 之类的 DROP 掉。

于是仔细想一想交换机和服务器之间经过了哪些组件,网卡收包,中断,网卡 driver,bond driver,协议栈处理。抓包都没抓到,说明问题出在协议栈之前,于是怀疑到 bond driver 头上去。

下一步,在物理 interface 上抓包,确认物理 interface 到底收到了 ARP reply 了没有。结果是,发现 eth0 这个 interface 收到了 ARP reply!

ARP reply 在 eth0 上收到了,但是 bond0 上没收到。这下感觉快要得到答案了。bond 有两个 slave,我把 eth0 shutdown 了,只留下 eth1,然后网路正常了。那要么是 bond driver 真的有问题,要么是我们的配置有问题。从经验上看,Linux driver 存在 bug 的概率要远远小于我们的配置错误。于是我去检查 bond 相关的配置。

检查 bond 状态 (/proc/net/bonding/bond0 文件), bond 配置,都没发现问题。可能是 eht0 这个接口有问题?

在重新看 interface 的时候(即上面的 ip link 命令和输出),我发现了可疑的一条 interface:

4: eth0.1000@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether b6:db:e6:76:dd:8a brd ff:ff:ff:ff:ff:ff

这里多出来一个 VLAN interface。

所以,实际上的 interface 配置应该是如下这样。由于 eth0.1000 的存在,我怀疑 eth0 收到的 ARP reply 实际上是送给了 eth0.1000@eth0 而不是 bond0,然后在 ARP 协议处理的时候,Linux 认为我们没有从 eth0.1000 发送出去 ARP request,但是却收到了 ARP 响应,属于 Gratuitous ARP5. 而发送 ARP request 的 bond0,从来没有收到 ARP reply。ARP cache 是 per interface 的,所以 bond0 无法发送 ICMP 出去。

eth0.1000 的配置

证明这个猜测很简单,只要在 eth0.1000@eth0 抓包,看是否有 ARP reply 就好了。抓包发现果然有。

并且把这个接口的 arp_accept 打开,让其接受 Gratuitous ARP,发现 ARP cache 出现了如下记录:

proot@ubuntu-1:/$ arp -a
? (192.168.1.253) at c6:34:22:fc:78:b4 [ether] on eth0.1000

说明这个结论是正确的。到这里就发现,其实问题不仅仅是 ARP 的问题,因为 bond 的两个 slave 有一个不对,收包的时候可能是从 eth0 收,也可能是从 eth1 收,取决于交换机的 hash 策略6。如果从 eth0 进来,那么协议栈的 skb 的 device 就会是 eth0.1000@eth0,所有有连接的协议处理都匹配不上。

于是我 shutdown eth0.1000@eth0 这个接口,理论上机器的配置应该都是对的了。

结果不是,问题依然存在,有点让人怀疑人生。由于接口 down 了就无法抓包了,不太好确认包是不是还在往 eth0.1000@eth0 送了。此处又花了一些时间排查,因为怀疑自己的推论是错误的,是不是有别的地方导致这个问题?一通误打误撞,决定删除这个多余的接口,然后网路就完全恢复了。从结果看,只 shutdown 这个接口不能阻止包往这个 vlan 接口送,得删除才行。

事后我们得知,这台服务器在 infra 团队交付的时候存在问题,应该配置 bonding,但是没有配置,只是在一条线(eth0)上配置了 VLAN。我们的同事拿到机器之后修复了 bonding 问题,但是并没有删除 eth0.1000@eth0 这个 VLAN 虚拟接口,导致产生了非预期的行为。

后来看了下源代码,发现 VLAN 的处理确实优先级比较高,在 __netif_receive_skb_core7 这里就会执行 vlan_do_recieve8,然后会把 device 的 id 设置在 skb 上。这个逻辑比 bond driver 的逻辑靠前,导致后续协议栈的处理,会认为这个包是从 eth0.1000@eth0 收到的,而不是从 bond0 收到的。

  1. 数据中心网络高可用技术之从服务器到交换机:802.3 ad ↩
  2. 数据中心网络高可用技术之从交换机到交换机:MLAG, 堆叠技术 ↩
  3. 数据中心网络高可用技术之从服务器到网关:首跳冗余协议 VRRP ↩
  4. VLAN Trunking Protocol ↩
  5. 特殊的 ARP 用法:Gratuitous ARP, ARP Probe 和 ARP Announce ↩
  6. 数据中心网络高可用技术之从服务器到交换机:链路聚合 (balance-xor, balance-rr, broadcast) ↩
  7. https://elixir.bootlin.com/linux/v6.12.6/source/net/core/dev.c#L5457 ↩
  8. https://elixir.bootlin.com/linux/v6.12.6/source/net/8021q/vlan_core.c#L10 ↩
❌