BFD 作为 Neutron 的服务¶
https://bugs.launchpad.net/neutron/+bug/1907089
添加在 Neutron 中定义 BFD 监控器并将其与路由关联的可能性。
问题描述¶
BFD(双向转发检测)可用于检测 Neutron 路由器与任意目标之间的链路故障,例如外部非 Neutron 路由器。
BFD 可用于提供快速、低开销的链路故障检测,在相邻转发引擎之间 ([1]),例如检查额外的路由(下一跳-目标对)是否存活,并相应地更改路由。
注意
本规范不涵盖其他用例,如隧道监控和 HA 或 DVR 路由器监控,或 BGP 或其他路由协议监控。
提议的变更¶
本规范的目标是添加 API 以管理 bfd_monitor 实例,获取其状态,将监控器与额外的路由关联。目标是覆盖 Neutron 管理的 bfd_monitor 实例进入 ACTIVE 角色,从而发送 BFD 控制包的情况(参见 [2])。
这使得操作员能够监控路由器的路由(目标 - 下一跳对)。
监控场景¶
相同目标的两个不同的下一跳,例如 ecmp 路由分支
dst1 |
nexthop1 |
bfd_dst1 |
bfd1 |
dst1 |
nexthop2 |
bfd_dst1 |
bfd2 |
____nexthop1 ____
\ ____dst1
____nexthop2____ /
通过相同的下一跳监控两个不同的目标,当 bfd3 和 bfd4 的 dst_ip 相同的情况
dst3 |
nexthop3 |
bfd_dst3 |
bfd3 |
dst4 |
nexthop3 |
bfd_dst4 |
bfd4 |
____dst3
_____nexthop3_____/
\____dst4
REST API 影响¶
新的 bfd 监控器 API¶
创建 bfd_monitor
POST /v2.0/bfd_monitors { "bfd_monitor": { "name": "bfd1", "mode": "asynchronous", "min_rx": 1000, "min_tx": 100, "multiplier": 3, "dst_ip": "10.0.0.13", "auth_type": "", "auth_keys": {1: "secret_1", 2: "secret_2"}, } }
列出 bfd_monitors
GET /v2.0/bfd_monitors
显示 bfd_monitor
GET /v2.0/bfd_monitors/{monitor_id} { "bfd_monitor": { "name": "bfd1", "project_id": "{project_id}", "description": "", "mode": "asynchronous", "min_rx": 1000, "min_tx": 100, "multiplier": 3, "dst_ip": "10.0.0.13", "src_ip": "169.254.112.144", "auth_type": "", "auth_keys": {1: "secret_1"}, } }
更新 bfd_monitor
PUT /v2.0/bfd_monitors/{monitor_id} { "bfd_monitor": { "name": "bfd_monitor1" "description": "foo", "min_rx": 2000, "min_tx": 200, "multiplier": 4 } }
删除 bfd_monitor
DELETE /v2.0/bfd_monitors/{monitor_id}
注意
只有未关联的 bfd_monitor 才能被删除。
bfd_monitor 的 API 字段和描述
属性 |
类型 |
必需 |
CRUD |
描述 |
|---|---|---|---|---|
id |
uuid-str |
否 |
R |
bfd_monitor 的 ID |
name |
字符串 |
否 |
CRU |
bfd_monitor 的人类可读名称(限制为 255 个字符)。不必唯一。 |
description |
字符串 |
否 |
CRU |
bfd_monitor 的人类可读描述(限制为 255 个字符)。 |
project_id |
字符串 |
否 |
R |
bfd_monitor 的所有者。 |
mode |
字符串 |
否 |
CR |
可以是 |
dst_ip |
字符串 |
是 |
CR |
要监控的目标 IP 地址。对于单跳 BFD,这是路由的下一跳 IP,对于一般情况(如多跳 BFD),这是一个任意 IP(IPv4 或 Ipv6),可以用作 BFD 邻居。 |
src_ip |
字符串 |
否 |
CR |
用于发送 BFD 数据包的源 IP 地址。可选字段,如果未指定,则设置为接口上的 IP,例如 qg-xyz。可以在远程端设置以进行配置。 |
min_rx |
Integer |
否 |
CRU |
此 BFD 会话提供接收 BFD 控制消息的最短间隔,以毫秒为单位。至少为 1。默认值为 1000。 |
min_tx |
Integer |
否 |
CRU |
此 BFD 会话愿意传输 BFD 控制消息的最短间隔,以毫秒为单位。至少为 1。默认值为 100。 |
multiplier |
Integer |
否 |
CRU |
BFD 检测乘数。端点如果给定的连续 BFD 控制消息未能到达,则发出连接故障信号。默认值为 3。 |
status |
字符串 |
N/A |
R |
这表明 BFD 监控器是否已在后端成功创建,但没有关于会话状态的信息,可以使用 session_status API 端点获取会话状态。 |
auth_type |
字符串 |
否 |
CR |
身份验证类型,可以是 |
auth_key |
字典 |
否 |
CR |
一个身份验证密钥链的字典,其中键是 |
注意
要使用 BFD 的身份验证,请查看 [5]
bfd_monitors 的 API 扩展提案
ALIAS = 'bfd-monitor'
IS_SHIM_EXTENSION = False
IS_STANDARD_ATTR_EXTENSION = False
NAME = 'BFD monitors for Neutron'
DESCRIPTION = "Provides support for BFD monitors"
UPDATED_TIMESTAMP = "2021-02-12T11:00:00-00:00"
BFD_MONITOR = 'bfd_monitor'
BFD_MONITORS = 'bfd_monitors'
BFD_SESSION_STATUS = 'bfd_session_status'
BFD_MODE_ASYNC = 'asynchronous'
BFD_MODE_DEMAND = 'demand'
BFD_MODE_ONE_ARM = 'one_arm_echo'
RESOURCE_ATTRIBUTE_MAP = {
BFD_MONITORS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True,
'enforce_policy': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': db_const.NAME_FIELD_SIZE},
'default': '', 'is_filter': True, 'is_sort_key': True,
'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'is_visible': True, 'default': '',
'validate': {
'type:string': db_const.DESCRIPTION_FIELD_SIZE}},
'project_id': {'allow_post': True, 'allow_put': False,
'validate': {
'type:string': db_const.PROJECT_ID_FIELD_SIZE},
'required_by_policy': True,
'is_visible': True, 'enforce_policy': True},
'mode': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': db_const.STATUS_FIELD_SIZE},
'default': BFD_MODE_ASYNC, 'is_filter': True,
'is_sort_key': True, 'is_visible': True},
'dst_ip': {'allow_post': True, 'allow_put': False,
'validate': {'type:ip_address': None},
'is_sort_key': True, 'is_filter': True,
'is_visible': True, 'default': None,
'enforce_policy': True},
'src_ip': {'allow_post': True, 'allow_put': False,
'validate': {'type:ip_address_or_none': None},
'is_sort_key': True, 'is_filter': True,
'is_visible': True, 'default': None,
'enforce_policy': True},
'min_rx': {'allow_post': True, 'allow_put': True,
'validate': {'type:non_negative': None},
'convert_to': converters.convert_to_int,
'default': 1000,
'is_visible': True, 'enforce_policy': True},
'min_tx': {'allow_post': True, 'allow_put': True,
'validate': {'type:non_negative': None},
'convert_to': converters.convert_to_int,
'default': 100,
'is_visible': True, 'enforce_policy': True},
'multiplier': {'allow_post': True, 'allow_put': True,
'validate': {'type:non_negative': None},
'convert_to': converters.convert_to_int,
'default': 3,
'is_visible': True, 'enforce_policy': True},
'status': {'allow_post': False, 'allow_put': False,
'is_filter': True, 'is_sort_key': True,
'is_visible': True},
'auth_type': {'allow_post': True, 'allow_put': False,
'validate': {'type:string_or_none':
db_const.NAME_FIELD_SIZE},
'default': constants.ATTR_NOT_SPECIFIED,
'is_visible': True},
'auth_key': {'allow_post': True, 'allow_put': False,
'validate': {'type:dict_or_none': None},
'default': constants.ATTR_NOT_SPECIFIED,
'is_visible': True},
},
}
SUB_RESOURCE_ATTRIBUTE_MAP = {}
ACTION_MAP = {
BFD_MONITOR: {
'bfd_session_status': 'GET',
'bfd_monitor_associations': 'GET',
}
}
ACTION_STATUS = {}
REQUIRED_EXTENSIONS = [l3.ALIAS]
OPTIONAL_EXTENSIONS = []
显示 BFD 会话状态(有关详细信息,请参阅 rfc5880,状态变量部分)
GET /v2.0/bfd_monitors/{monitor_id}/session_status { "bfd_session_status": { "remotes": [{ "type": "extra_route", "router": {router_id}, "extra_route": { "destination": "10.0.3.0/24", "nexthop": "10.0.0.1" }, "src_ip": "169.254.112.144", "status": { "SessionState": "Up", "RemoteSessionState": "Up", "LocalDiagnostic": "", "RemoteDiagnostic": "", "LocalDiscriminator": "", "RemoteDiscriminator": "", "Forwarding": "", } }, { "type": "extra_route", ... }] } }
显示 bfd_monitor 关联
GET /v2.0/bfd_monitors/{monitor_id}/bfd_monitor_associations { "bfd_monitor_associations": { "remotes": [{ "type": "extra_route", "router": {router_id}, "extra_route": { "destination": "10.0.3.0/24", "nexthop": "10.0.0.1" }, "src_ip": "169.254.112.144", }, { "type": "extra_route", ... }] } }
注意
当前提案仅适用于“type”:“extra_route”,因此将显示 extra_route 的详细信息(目前为下一跳、目标)。
注意
一个 bfd_monitor 实例可以关联到多个 extra_route。
注意
src_ip 是用于发送 BFD 数据包的源 IP 地址。一个只读字段,实际上显示了接口上设置的 IP,例如 qg-xyz。可以在远程端设置。
状态字段的简短描述
属性 |
描述 |
|---|---|
SessionState |
BFD 会话的状态,可以是 |
RemoteSessionState |
远程端点 BFD 会话的状态,可以是 |
LocalDiagnostic |
一个诊断代码,指定本地系统上次更改会话状态的原因。错误消息在 RFC 5880 的第 4.1 节中定义(参见 [3])。 |
RemoteDiagnostic |
一个诊断代码,指定远程系统上次更改会话状态的原因。错误消息在 RFC 5880 的第 4.1 节中定义(参见 [3])。 |
LocalDiscriminator |
此 BFD 会话的本地区分器,用于唯一标识它。 |
RemoteDiscriminator |
此 BFD 会话的远程区分器。这是远程系统选择的区分器。 |
Forwarding |
报告 BFD 会话是否认为此接口可用于转发流量。可以是 |
注意
以上字段是可选的,因此根据后端,并非全部都会填充,在最坏的情况下,只会显示累积的 Forwarding 结果。
对路由器 extra routes API 的更改¶
更改以添加或删除路由器上的 extra routes,或更新路由器 API
PUT /v2.0/routers/{router_id} { "router": { "routes": [ { "destination": "179.24.1.0/24", "nexthop": "172.24.3.99" "bfd_monitor": {bfd_monitor_uuid} }, ] } } PUT /v2.0/routers/{router_id}/add_extraroutes { "router" : { "routes" : [ { "destination" : "10.0.3.0/24", "nexthop" : "10.0.0.13", "bfd_monitor": {bfd_monitor_uuid} }, { "destination" : "10.0.4.0/24", "nexthop" : "10.0.0.14" } ] } } PUT /v2.0/routers/{router_id}/remove_extraroutes { "router" : { "routes" : [ { "destination" : "10.0.3.0/24", "nexthop" : "10.0.0.13", "bfd_monitor": {bfd_monitor_uuid} } ] } }
注意
API 允许删除与路由关联的 bfd_monitor,而无需删除和重新创建整个下一跳-目标元组。
获取路由状态
GET /v2.0/routers/{router_id}/routes_status { "router": { "routes": [ { "destination" : "10.0.3.0/24", "nexthop" : "10.0.0.13", "status": "UP", "bfd_monitor": {bfd_monitor_uuid} }, ] } }
允许 bfd_monitor 关联到 extra_routes 的 API 扩展提案
ALIAS = 'bfd-for-extraroutes'
IS_SHIM_EXTENSION = False
IS_STANDARD_ATTR_EXTENSION = False
NAME = 'BFD monitors for extraroutes'
DESCRIPTION = ('Provides the possibility to associate a bfd_monitor to'
'extra_routes')
UPDATED_TIMESTAMP = '2021-01-29T00:00:00+00:00'
RESOURCE_NAME = l3.ROUTER
COLLECTION_NAME = l3.ROUTERS
ROUTES = 'routes'
RESOURCE_ATTRIBUTE_MAP = {
COLLECTION_NAME: {
ROUTES: {
'allow_post': False, 'allow_put': True,
'validate': {'type:hostroutes': ['destination', 'nexthop',
'bfd_monitor_id']},
'convert_to': converters.convert_none_to_empty_list,
'is_visible': True,
'default': constants.ATTR_NOT_SPECIFIED},
}
}
SUB_RESOURCE_ATTRIBUTE_MAP = None
ACTION_MAP = {}
REQUIRED_EXTENSIONS = [l3.ALIAS, extraroute.ALIAS]
OPTIONAL_EXTENSIONS = []
ACTION_STATUS = {}
注意
[6] 提出将 hostroutes 验证器更改为接受一个可能的字段列表以进行验证,例如:destination、nexthop 和 bfd_monitor_id。
BFD 本身以及获取 bfd_monitor 会话状态信息可能是一项非常消耗资源的操作(必须从后端获取信息),因此建议新的 API 端点来获取监控状态,以避免过度负载或影响普通的路由器 API 操作。
routes_status API 端点向用户提供有关被监控链路是否处于活动状态的反馈。
如果 bfd_monitor 的 session_status 为 DOWN(BFD 检测到给定的链路已断开),则应在后端删除给定的路由(在命令行上:ip r delete…),并在监控器变为 UP 时将其设置回去。
当 bfd_monitor 与任何路由关联时,后端会创建 bfd_monitor 实例,bfd_monitor 的 status 从 DOWN 设置为 ACTIVE。当更新 extra route 并且 bfd_monitor 值设置为空时,bfd_monitor 的 status 设置为 DOWN,因此 bfd_monitor 会在后端被删除。
由于 bfd_monitor 可以关联到许多路由,如果它关联到至少一个路由并且在后端创建成功,则 bfd_monitor 的 status 字段为 ACTIVE,当从最后一个 extra_route 删除关联时,它将更改为 DOWN(bfd_monitor 从最后一个 extra_route 中删除)。
后端¶
OVS 可以在接口上处理 BFD 并检查其状态。它的问题是 ovs 可以为每个端口拥有 1 个 BFD 会话,并且为了管理它,ovsdb 是最简单的方法,但正如 LIU Yulong 在 [7] 中所述,从 l3-agent 接触 ovsdb 可能会很奇怪。
使用 OVS 的一个可行方案是创建一个 BFD 桥接,例如 br-bfd,并向其中添加 veth 端口,并在这些接口上启用 BFD。
--------
| br-int |
--------
| veth-xy-br-int
|
| veth-xy
--------
| br-bfd |
--------
另一种可能性是使用 os-ken,但在我的实验中,os-ken 中的 BFD 实现不够成熟,并且无论如何都需要 ovsdb 连接,所以我建议不要将 os-ken 用于 BFD。
数据模型影响¶
为 bfd_monitors 创建新表,更改 routers 表以能够将 bfd_monitor 与 extra route(nexthop - destination 对)关联起来。
安全影响¶
无
性能影响¶
BFD 和获取 bfd_monitor 状态信息可能是一项非常消耗资源的操作,因为它可能从后端获取,必须仔细记录。
实现¶
负责人¶
- 主要负责人
Lajos Katona <katonalala@gmail.com> (IRC: lajoskatona)
工作项¶
REST API 更新。
DB 模式更新。
L3 代理更新以处理 BFD
RPC 更改以将 BFD 数据发送到 l3 代理。
RPC 更改以从 l3 代理获取 BFD 状态信息。
CLI 更新。
文档。
测试和 CI 相关更改。
测试¶
单元测试
功能测试
API 测试
也许这可以通过全栈测试端到端轻松测试。需要进一步调查。
文档影响¶
用户文档¶
新的 API 和对 legacy API(如 routers)的更改必须记录。