操作状态

1. 简介

Linux 区分接口的管理状态和操作状态。管理状态是“ip link set dev <dev> up 或 down”的结果,反映了管理员是否希望使用该设备来传输流量。

但是,一个接口不仅仅因为管理员启用了它就可以使用 - 以太网需要插入交换机,并且根据站点的网络策略和配置,在用户数据可以传输之前,需要执行 802.1X 身份验证。操作状态显示接口传输此用户数据的能力。

由于 802.1X,必须授予用户空间影响操作状态的可能性。为了适应这一点,操作状态被分成两个部分:两个只能由驱动程序设置的标志,以及一个与 RFC2863 兼容的状态,该状态从这些标志、策略派生而来,并且可以在某些规则下从用户空间更改。

2. 从用户空间查询

可以通过 netlink 操作 RTM_GETLINK 查询管理状态和操作状态。也可以订阅 RTNLGRP_LINK 以在接口处于管理启用状态时接收更新通知。这对于从用户空间进行设置非常重要。

这些值包含接口状态

ifinfomsg::if_flags & IFF_UP

接口处于管理启用状态

ifinfomsg::if_flags & IFF_RUNNING

接口处于 RFC2863 操作状态 UP 或 UNKNOWN。这是为了向后兼容,路由守护进程,dhcp 客户端可以使用此标志来确定是否应该使用该接口。

ifinfomsg::if_flags & IFF_LOWER_UP

驱动程序已发出信号 netif_carrier_on()

ifinfomsg::if_flags & IFF_DORMANT

驱动程序已发出信号 netif_dormant_on()

TLV IFLA_OPERSTATE

包含接口的 RFC2863 状态的数字表示

IF_OPER_UNKNOWN (0)

接口处于未知状态,驱动程序和用户空间都未设置操作状态。必须考虑将该接口用于用户数据,因为并非所有驱动程序都已实现设置操作状态。

IF_OPER_NOTPRESENT (1)

当前内核未使用(不存在的接口通常会消失),只是一个数值占位符。

IF_OPER_DOWN (2)

接口无法在 L1 上传输数据,例如,以太网未插入或接口已禁用 ADMIN。

IF_OPER_LOWERLAYERDOWN (3)

堆叠在 IF_OPER_DOWN 接口上的接口显示此状态(例如,VLAN)。

IF_OPER_TESTING (4)

接口处于测试模式,例如执行驱动程序自测或媒体(电缆)测试。在测试完成之前,它不能用于正常流量。

IF_OPER_DORMANT (5)

接口 L1 启动,但正在等待外部事件,例如,等待协议建立。(802.1X)

IF_OPER_UP (6)

接口已启动并可以用来操作。

此 TLV 也可以通过 sysfs 查询。

TLV IFLA_LINKMODE

包含链接策略。 这是下面描述的用户空间交互所需要的。

此 TLV 也可以通过 sysfs 查询。

3. 内核驱动程序 API

内核驱动程序可以访问映射到 IFF_LOWER_UP 和 IFF_DORMANT 的两个标志。 这些标志可以从任何地方设置,甚至可以从中断中设置。 可以保证只有驱动程序具有写入访问权限,但是,如果驱动程序的各个层操纵相同的标志,则驱动程序必须提供所需的同步。

__LINK_STATE_NOCARRIER, 映射到 !IFF_LOWER_UP

驱动程序使用 netif_carrier_on() 清除,使用 netif_carrier_off() 设置此标志。 在 netif_carrier_off() 上,调度程序停止发送数据包。 名称“carrier”和反转是历史遗留的,可以将其视为下层。

请注意,对于某些类型的软设备,它们不管理任何真正的硬件,可以从用户空间设置此位。 应该使用 TLV IFLA_CARRIER 来执行此操作。

netif_carrier_ok() 可用于查询该位。

__LINK_STATE_DORMANT, 映射到 IFF_DORMANT

由驱动程序设置,以表示该设备尚不能使用,因为必须完成某些驱动程序控制的协议建立。 相应的函数是 netif_dormant_on() 设置标志, netif_dormant_off() 清除标志, netif_dormant() 进行查询。

在设备分配时,标志 __LINK_STATE_NOCARRIER 和 __LINK_STATE_DORMANT 均被清除,因此有效状态等效于 netif_carrier_ok() 和 !netif_dormant()

每当驱动程序更改这些标志之一时,就会调度一个工作队列事件,以将标志组合转换为 IFLA_OPERSTATE,如下所示

!netif_carrier_ok()

如果接口已堆叠,则为 IF_OPER_LOWERLAYERDOWN,否则为 IF_OPER_DOWN。 内核可以识别堆叠的接口,因为它们的 ifindex != iflink。

netif_carrier_ok() && netif_dormant()

IF_OPER_DORMANT

netif_carrier_ok() && !netif_dormant()

如果禁用用户空间交互,则为 IF_OPER_UP。 否则,IF_OPER_DORMANT 允许用户空间在之后启动 IF_OPER_UP 转换。

4. 从用户空间设置

应用程序必须使用 netlink 接口来影响接口的 RFC2863 操作状态。 通过 RTM_SETLINK 将 IFLA_LINKMODE 设置为 1 会指示内核,当驱动程序设置了组合 netif_carrier_ok() && !netif_dormant() 时,接口应转到 IF_OPER_DORMANT 而不是 IF_OPER_UP。 此后,只要驱动程序未设置 netif_carrier_off()netif_dormant_on(),用户空间应用程序就可以将 IFLA_OPERSTATE 设置为 IF_OPER_DORMANT 或 IF_OPER_UP。 用户空间所做的更改将在 netlink 组 RTNLGRP_LINK 上进行多播。

因此,基本上 802.1X 请求者与内核的交互如下

  • 订阅 RTNLGRP_LINK

  • 通过 RTM_SETLINK 将 IFLA_LINKMODE 设置为 1

  • 查询 RTM_GETLINK 一次以获取初始状态

  • 如果初始标志不是 (IFF_LOWER_UP && !IFF_DORMANT),请等到 netlink 多播发出此状态信号

  • 执行 802.1X,如果标志再次下降,最终中止

  • 如果身份验证成功,则发送 RTM_SETLINK 以将 operstate 设置为 IF_OPER_UP,否则设置为 IF_OPER_DORMANT

  • 查看 operstate 和 IFF_RUNNING 如何通过 netlink 多播回显

  • 如果 802.1X 重新身份验证失败,则将接口设置回 IF_OPER_DORMANT

  • 如果内核更改 IFF_LOWER_UP 或 IFF_DORMANT 标志,则重新启动

如果请求者停止运行,则将 IFLA_LINKMODE 带回 0,并将 IFLA_OPERSTATE 设置为合理值。

路由守护进程或 dhcp 客户端只需要注意 IFF_RUNNING 或等待 operstate 转到 IF_OPER_UP/IF_OPER_UNKNOWN,然后才考虑接口/查询 DHCP 地址。

如有技术问题和/或意见,请发送电子邮件至 Stefan Rompf (stefan at loplof.de)。