详见:
架构
目标:替代 bonding 功能,最终干掉 bonding。
- 内核中的代码尽可能少。把内核当成是 puppet
- 控制逻辑在用户空间实现,puppeteer
整个项目称为 team project,其中用户空间的代码项目为 libteam,含 libteam 库、teamd daemon、teamd-utils 三个组件。
teamdev driver
从 Linux 内核版本 3.3 开始支持。teamdev driver 遵循以下原则,只做报文的 rx/tx 处理,本身无业务逻辑。
If something can be done in userspace, do it in userspace.
- fast-path (1.4KLOC)
- netlink 通信(0.6KLOC)
- user space 配置、改变 teamdev 驱动的行为
- user space 从驱动获取端口状态变化等事件
- Team 模式
- 一种 mode 一个内核模块
- activebackup
- broadcast
- loadbalance
- random
- roundrobin
一般称为 teamdev,接口名为 team0 等。
libteam lib
使用 libnl,封装 Team Netlink 通信。用户无需知道任何 Netlink API 相关的信息。
所有消息都来自 driver,并缓存在用户空间,使用者不需要发任何消息就能获取到请求的数据。
同时封装 RT Netlink,用于使用者添加、删除 Teamdev 实例,或添加删除 port,或获取、设置网络接口的硬件 IP 等。
libteam teamd
即 Team daemon,为 libteam 项目的一部分,使用 libteam lib。teamd 跑在后台,一个实例对应一个 Team softdev,用于支持多种多样的逻辑 Team 行为。比如 基础的 round-robin,或更复杂的 active-backup、load-balancing。这些逻辑在 teamd 中称为 runner,每个 Team softdev 只能运行一种 runner。
Team Netlink
有两种类型的消息:Port list 和 option list。
Port list 消息只存在 driver -> lib 方向,只读数据。schema:
- port item
- interface index
- changed flag,指明该端口任何状态是否有变化。
- removed flag,指明端口被删除了
- linkup,直接从 ethtool 获取 link 状态
- speed,直接从 ethtool 获取 Mbps
- duplex,直接从 ethtool 获取 duplex
- port item
- …
- port item
- …
Option list 消息可能存在两个方向。driver -> lib 方向,暴露并传输 driver 数据到 userspace。 lib-> driver 方向,用于通告 driver 哪个 option 的值应变化,应怎么变化。schema 如下:
- option item
- name,option 名
- changed flag
- removed flag
- type,指明以面 data 域的类型
- data,动态类型
- port interface index。如果 option 是 per-port 的,那么这个值为对应接口 index。
- array index,如果 option 是 array。
- option item
- …
- option item
- …
存在以下消息:
- lib 请求 port list
- lib 请求 option list
- lib 请求 option 值变化
- driver port 状态变化事件。net core 通知 dirver 哪些端口状态变化或被删除。
- driver option 值变化事件。
第4、5点,使用 netlink multicast facility,用于支撑多用户空间实例。
- teamnl,使用 libteam,并封装 Team device Netlink 通信。
Driver
通过 ip 工具集即可添加、删除 team 设备。
1 |
创建 team 设备时,如果未指定硬件地址,将生成一个随机的。
同样通过 ip 工具集添加、移除端口。
1 | ip link set eth0 master team0 |
Team softdev 驱动使用 netdevice 通告机制获取 port 上面的事件,比如 link 状态变化、port 丢失等。当一个 team 设备实例创建时(例如 team0),它就与其他网络设备无异,用户可像普通网络设备那样配置 team0,比如配置 IP 地址、应用 iptables 规则等。不同的地方是它自己不能从收发 skb,它使用其他网络设备(物理端口)。
发送 skb 时,team softdev 通过 port 选择算法选出一个 port,然后使用该 port 的 netdevice 发送 skb。
接收端,team softdev 使用 net core rx 步骤的 rx_handle 钩子拦截报文。bonding、bridging、macvlan 和 openvswitch 使用同样的钩子。通过这个钩子,报文看起来就像是从 teamdev 中接收的。然后 Net 核心代码就将报文视为从 teamdev 中接收的,进行下一步处理。
问题的重点在于如何达到最好的性能,因此 fast path(skb 发送、接收 paths)都是无锁的,只使用 RCU。同时将数据存到内存中,实现最大的局部性和最小的指针解引用。
Options
【TBD】
Modes
每个 mode 实现成一个 module。Team core 代码使用用户空间应用配置的 mode 字符串,调用对应 mode 实现的 handler。这些 handler 定义以下行为:
- init,在选择模式后调用,与通常的 init 函数一样,用于分配内存和注册 mode 特有的 option。
- exit,反选 mode 后调用。
- receive,由接收钩子调用,这个 handler 允许 mode 查看收到的 skb,并可修改 skb。
- transmit,由 Team core 发送函数调用。在这里决策发送端口。
- port_enter,当一个端口加入 teamdev 时调用
- port_leave,当一个端口移除时调用
- port_change_mac,当检测到硬件地址发生变化时调用。
目前实现五种模式:
- broadcast,Basic mode。将报文发送给所有可能的端口。
- roundrobin,Basic mode。一种很简单的端口选择算法,基于端口列表,逐个循环选择端口。这是唯一一种可以不需要用户空间交互就可正常 运行的模式。
- random,Basic mode。随机选择端口。
- activebackup,在这种模式,只有一个端口处于 active 状态,该端口处理 skb 收发。其他端口都处于 backup 状态。用户空间通过 activeport 选项指定 active 端口。
- loadbalance,一个复杂的模式。比如用于 LACP 和用户空间控制收发均衡。使用 hash 识别相同的 skb。Hash 长度 8 比特,所以总共有 256 种类型的 skb。hash 使用 BPF (Berkeley Packet Filter) 机制计算。这个模式使用以下选项:
- bpf_hash_func,一个二进制 option,包含 BPF 函数,用于从 skb 计算 hash 值。应用空间可组合和配置 hash 计算函数。例如源、目的 IP 地址、MAC 地址等。
- lb_hash_stats 和 lb_port_stats,只读数组选项。暴露 256 种可能的 skb 类型的收发计算器,以及每个端口的收发计算器。用户空间程序可使用这个统计值判断端口的均衡程序,如果均衡程度不够,再进行 hash 算法调整。
- lb_stats_refresh_interval,下发统计值到用户空间的频率。
- lb_tx_hash_to_port_mapping,用于 hash-to-port 映射的数组选项。允许用户空间应用程序告诉驱动程序某个特定 hash 值的 skb 应选择哪个端口发送。
- lb_tx_method,字符串选择,有两个值。
- hash,告诉驱动使用 hash 值获取发送端口
- hash_to_port_mapping,使能 hash_to_port_mapping 功能,并从先前的数组选项获取发送端口。
teamd
libteam 项目的重要部分。如果可能,最好在 teamd 实现功能,而非 Team softdev Linux 驱动。把 teamdev 视为木偶,则 teamd 是操纵 teamdev 的傀儡师。
teamd 的详细命令行参数见 man 8 teamd。
teamd 负责 teamdev 的创建与删除。teamdev 在 teamd 启动的时候创建,在 teamd 退出前删除。teamdev 的名字(比如 team0)通过配置文件指定。
Teamd 主要负责:
- 配置文件解析
- 后台 forking 以及诸如 PID 创建、信号处理等相关的事情。在运行 systemd 的系统中不需要,但是 teamd 也需要运行在其他系统。
- Team softdev Linux driver 实例的创建与删除。
- Libteam 库通信初始化
- 提供主循环框架,用于 fd 或定时器等的监控与处理。
- 初始化事件机
- 端口添加、删除事件处理
- 初始化链路状态监控器(watcher)
- 初始化 runner
- 初始化 D-Bus 接口
- 添加端口到 teamdev
teamd 被设计成单进程、单线程应用,目前不需要多线程,这样代码会更简单。所有的运行时工作都在主循环中完成。所以如果 runner 或 watcher 需要做自己的事情(比如定时工作或 socker 数据接收等),则它们必须在主循环的框架中自己实现。
link-watcher
Link 监测器实现链路状态检测,可使用不同的方法检测某个端口是否有报文传输能力,即判断端口是否 Up。
目前实现的方式有:
- ethtool,使用 libteam lib 获取端口 ethtool 状态变化
- arp_ping,通过某端口发送 ARP 请求,如果有收到 ARP 请求,则认为链路为 up 状态。目标 IP 地址、发送时间间隔和其他选项都可以在 teamd 中配置。
- nsna_ping,类似 arp_ping,但是使用 IPv6 邻居请求(NS,neighbor solicitation)和邻居发现机制(NA,neighbor advertisement)。用于纯 IPv6 环境。
可全局配置,亦可每端口配置 link-watch。用户也可以为一次配置多个 link-watcher,只有当所有的 link-watcher 报告的状态都为 up,链路才是 up。
runner
Runner 决定 Team 设备的行为。他们使用想要的内核 team 模式进行操作。Runner 监测端口 link 状态变化(使用所选择的 link-watch 监测结果),并进行相应的操作。Runner 可能还实现其他功能。
有以下 Runners 可使用(Team softdev Linux 驱动模式见以下括号):
- broadcast (broadcast),几乎没做啥,只是将 teamdev 的模式置为 broadcast 模式。
- roundrobin (roundrobin),几乎没做啥,只是将 teamdev 的模式置为 roundrobin 模式。
- random (random),几乎没做啥,只是将 teamdev 的模式置为 random 模式。
- activebackup (broadcast),监测链路变化,并选择 active 端口做报文传输。每个端口可配置自己的优先级,以及是否 sticky。sticky 的含义为即使有更高优先级的端口 Link,该端口也不会被停用。
- loadbalance (loadbalance),为了做被动的负载平衡,runner 只设置 BPF 散列函数,它将确定 skb 传输的端口。为了执行主动负载平衡,runner 将不同哈希值映射到不同的以达到完美平衡。lb_hash_stats 数组选项用于获取统计信息。lb_tx_hash_to_port_mapping 数组选项用于将散列映射到 TX 端口。
- lacp (loadbalance),实现 802.3ad LACP 协议。可使用 hash 或 loadbalance runner。
teamd control API
Teamd 提供 control API 供用户控制。通过 D-Bus 和 Unix socket 实现。
teamd 启动时默认使用 Unix domain socket API,如果有带 -D
选项,则使用 D-Bus API,创建 D-Bus 服务 org.libteam.teamd.[teamdevname]。
支持以下方法:
- ConfigDump(),返回 teamd 所有 JSON 配置。
- ConfigDumpActual(),返回所有 teamd JSON 配置,但是只包含在线端口的配置,过滤所有非在线端口。
- StateDump(),返回 teamd 的状态以及其 JSON 中的子配置。
- PortAdd(String port_devname),添加端口到 teamdev。
- PortRemove
- PortConfigUpdate
- PortConfigDump
- StateItemValueGet(String state_item_path),在 state 树中查找 state 条目,并返回其值。
- StateItemValueSet(String state_item_path, String value)
有计划在将来用更多的方法扩展这个接口。
还有一个想法是扩展 API,以便能够将一个 runner 作为一个外部应用程序通过 control API 与 teamd 通信。
teamdctl
teamdctl 提供 control API 的封装,用来运行时监控和配置 teamd。1:1 封装 API。
teamdctl 命令更详细的描述和命令行参数,详见 manpage (man 8 teamdctl)。
Config
teamd 使用 JSON 配置文件配置,通过命令行或 .JSON 格式的的文件传递给 teamd。
选择 JSON 格式的原因是它可以轻松指定(和解析)分层配置。
teamd 配置样例(teamd1.conf):
1 | { |
配置几乎不言自明。只有 link_watch 部分可能看起来有点混乱。所以在这个例子中,默认 link-watcher 是 nsna_ping,设置为每 200 毫秒发送 NAs(邻居通告)。丢失答复的最大数量为 15 个。如果丢失更多答复,link 将被视为关闭。 端口 eth1 和 eth2 指定他们自己的 link-watcher。
另一个 teamd 配置样例(teamd2.conf):
1 | { |
在这个样例中,tx_hash 一节值得一提。它指明使用 skb 哪些部分计算 hash。
更多详细内容见 manpage(man 8 teamd.conf)。
样例
1 | # ip link |
Bonding v.s. team features
来源: https://github.com/jpirko/libteam/wiki/Bonding-vs.-Team-features
Feature Bonding Team
broadcast TX policy
Yes
Yes
round-robin TX policy
Yes
Yes
active-backup TX policy
Yes
Yes
LACP (802.3ad) support
Yes
Yes
Hash-based TX policy
Yes
Yes
Highly customizable hash function setup No
Yes
TX load-balancing support (TLB)
Yes
Yes
RX load-balancing support (ALB)
Yes
Planned
RX load-balancing support (ALB) in bridge or openvswitch No Planned
LACP hash port select
Yes
Yes
load-balancing for LACP support No
Yes
Ethtool link monitoring
Yes
Yes
ARP link monitoring
Yes
Yes
NS/NA (IPV6) link monitoring No
Yes
ports up/down delays
Yes
Yes
port priorities and stickiness (“primary” option enhancement) No
Yes
separate per-port link monitoring setup No
Yes
multiple link monitoring setup Limited
Yes
lockless TX/RX path No(rwlock)
Yes
(RCU)
VLAN support
Yes
Yes
user-space runtime control Limited Full
Logic in user-space No
Yes
Extensibility Hard Easy
Modular design No
Yes
Performance overhead Low Very Low
D-Bus interface No
Yes
ØMQ interface No
Yes
multiple device stacking
Yes
Yes
zero config using LLDP No Planned
man
man teamd
1 | js@js-vbox:~/sonic-buildimage/src/sonic-swss$ man teamd |
man teamd.conf
1 | js@js-vbox:~/sonic-buildimage/src/sonic-swss$ man teamd.conf |
man teamdctl
1 | js@js-vbox:~/sonic-buildimage/src/sonic-swss$ man teamdctl |