基于隧道(ERSPAN、GRE)的Tap-as-a-service 镜像¶
https://bugs.launchpad.net/neutron/+bug/2015471
镜像是一种广泛使用的工具,用于分析交换机端口的流量。Tap-as-a-service 项目旨在允许管理员将一个 Neutron 端口的流量镜像到另一个 Neutron 端口。
镜像也可以通过将流量封装到隧道中来实现,例如 GRE(通用 路由 封装)或 ERSPAN(封装 远程 交换机 端口 分析器)。ERSPAN 最初广泛应用于 Cisco 交换机,而 GRE 则被广泛用作隧道协议。
ERSPAN 协议有 3 个版本,其中最后两个版本被采用,分别是 版本 1 和 版本 2(另一种版本使用 TYPE I、II 和 III,TYPE II 是版本 1,TYPE III 是版本 2)。有关更多详细信息,请参阅 Cisco 的 ERSPAN 草案。
从 OVS 2.10 开始,可以使用 OVS 的 ERSPAN(参见 OVS 基本配置,以及 OVS 协议头字段),ERSPAN v1 和 v2,参见 erspan NEWS 更新提交。
从 OVN v22.12.0 开始,可以使用 OVN 创建镜像(参见 OVN 22.12 nbctl man 页面,以及 引入镜像的 OVN 提交)。
注意
OVN 仅支持 ERSPAN v1,并且使用 OVN 也可以创建干净的 GRE 类型镜像。
本规范提出扩展当前的 tap-as-a-service (TAAS) API,以允许用户从 Neutron 端口到远程 IP 创建 ERSPAN 或 GRE 镜像,并提出当前 TAAS 的 OVS 驱动程序所需的后端更改,并提出一个新的 OVN 驱动程序,以使用 OVN 的 ERSPAN 或 GRE 镜像。
问题描述¶
镜像流量在许多情况下对运营商都很有用,例如调试网络问题。
Tap-as-a-service 通过允许创建 tap-flow 并将流量镜像到相关的 tap-service,提供了一种流量镜像解决方案。每个 tap-flow 和 tap-service 都可以附加到一个 Neutron 端口,因此附加到 tap-flow 的端口是镜像流量的源,tap-service 的端口是镜像的目的地。tap-flow 和 tap-service 之间存在 N-1 的关系。这种镜像模型是将流量从一个 Neutron 端口镜像到另一个 Neutron 端口,通过 Neutron 网络。
当运营商需要将流量从云(来自 Neutron 端口)镜像到云外的分析器时,ERSPAN 或 GRE 镜像是一个很好的解决方案。
用例¶
作为运营商,我希望将来自
Neutron 端口的流量镜像到可以位于云 外的网络分析器。作为运营商,我希望将来自
Neutron 端口的流量镜像到浮动 IP。作为运营商,我希望将来自
Neutron 端口的流量镜像到专用 基础设施 网络,以避免使租户网络过载。作为运营商,我希望使用 ERSPAN 提供的额外头部(例如:原始 VLAN、版本 1 ERSPAN 时的原始 CoS)。
提议的变更¶
该提案是使用 OVS 和 OVN 内置的 ERSPAN 版本 1 和 GRE 镜像功能。为了使用 OVS 的 ERSPAN 或 GRE,将向 OVS 网桥添加一个端口,类型为 type=erspan 或 GRE 的 type=gre。
由于 ERSPAN 是 GRE 的一种修改,具有一些额外的 ERSPAN 特定头部(参见 Cisco 的 ERSPAN 草案),OVS 从先前创建的 OVS 端口到目标 IP 创建隧道。这意味着镜像流量被封装到 ERSPAN/GRE 隧道中。
ICMP 回显应答的 Wireshark 转储示例
Frame 1: 148 bytes on wire (1184 bits), 148 bytes captured (1184 bits)
Ethernet II, Src: RealtekU_79:ff:db (52:54:00:79:ff:db), Dst: RealtekU_91:2f:52 (52:54:00:91:2f:52)
Internet Protocol Version 4, Src: 100.109.0.84, Dst: 100.109.0.142
Generic Routing Encapsulation (ERSPAN)
Encapsulated Remote Switch Packet ANalysis Type II
Ethernet II, Src: fa:16:3e:d5:4b:c1 (fa:16:3e:d5:4b:c1), Dst: fa:16:3e:80:ed:09 (fa:16:3e:80:ed:09)
Internet Protocol Version 4, Src: 10.0.0.39, Dst: 10.0.0.47
Internet Control Message Protocol
这意味着隧道的源 IP 是创建 ERSPAN 端口的主机 IP(在我的虚拟环境中是 100.109.0.84)。在上面的示例中,两个内部 IP(10.0.0.47 和 10.0.0.39)是 2 个 Openstack 端口(VM)的固定 IP。
隧道外部协议源 IP(100.109.0.84)是创建镜像端口的主机 IP,因此位于云外,并由管理员控制。
注意
本规范未指定隧道的源地址(mac 或 IP)。因此,这取决于管理员,并且超出本规范的范围。
在本例中,隧道外部协议目标 IP(100.109.0.142)也位于云外,并且不在 Openstack Neutron 的控制范围内,而是另一个我可以在其上运行 tcpdump 的主机。
REST API 影响¶
TAAS 当前的 API 模型使用两个高级对象:tap-services 和 tap-flows。tap-flow 代表镜像流量的源,而 tap-service 代表镜像流量的目的地。可以为单个 tap-service 附加多个 tap-flow。有关详细信息,请参阅 tap-as-a-service API 参考。tap-flow 和 tap-service 都引用一个 Neutron 端口,该端口上的流量将是镜像的源(在 tap-flow 的情况下),或镜像的目的地(在 tap-service 的情况下)。
警告
镜像仅适用于安全组允许的流量。
上述 API 模型在实施使用 ERSPAN 或 GRE 隧道进行镜像的意图时没有用,因为流量源是网桥端口(在 OVS 的情况下)或逻辑交换机端口(在 OVN 的情况下),目的地仅由 IP 地址表示。
该提案是引入一个新的 ERSPAN 或 GRE 镜像高级 API:tap_mirror。
此解决方案保持了当前 API 的清洁和未超载,并使运营商更容易在 API 操作后期望正确的行为。
建议的 API 是 仅限管理员,以避免租户过载基础设施网络。
建议的 API 请求
POST /v2.0/taas/tap_mirrors创建一个将流量从 Neutron 端口镜像到外部 IP 的 tap 镜像
{ "tap_mirror": { "name": "mirror-traffic-of-server-a0", "description": "Mirror the traffic from server-a0", "directions": {"IN": 99, "OUT": 100}, "port_id": "1a1a5a96-e8cb-11ed-9678-9b663820b519", "remote_ip": "172.31.1.1", "mirror_type": "erspanv1"|"gre" } }
port_id是镜像的源,这是一个Neutron 端口。一个端口只能附加到一个 tap_mirror。
注意
只能使用 VM 端口作为镜像的源。
remote_ip:隧道远程端的 IP。
注意
此 API 提案保持了 TAAS API 当前的 N-1 源与目的地之间的关系。多个源端口的流量可以镜像到一个目的地 IP。
mirror_type字段用于在 ERSPAN 和 GRE 之间进行选择。directions是一个包含 direction:tunnel_id 对的字典。当前的 tap-as-a-service API 允许运营商在创建 tap-flow 时选择方向,可以是:IN、OUT、BOTH。本规范建议保留IN和OUT作为字典的键。方向的含义IN:进入端口的流量,以及进入连接到它的 VM 的流量(入站流量)。OUT:从端口流出的流量,从连接到端口的 VM 流出(出站流量)。
tunnel_id 将是
directions字典的值,这是源和目的地之间的 ERSPAN 或 GRE 会话的标识符。
注意
GRE 和 ERSPAN ID 的大小差异很大:GRE 具有 32 位密钥大小,但 ERSPAN 仅对 ERSPAN 会话 ID 有 10 位。这必须记录在案并在 API 上进行验证。
警告
使用 OVN,无法使用相同的 tunnel_id 将隧道(GRE 或 ERSPAN)创建到相同的 remote_ip,从相同的端口。这意味着对于两个方向,tunnel_id 不能相同。
注意
GRE 和 ERSPAN 都处理分片,因此如果带有额外头部的镜像流量的包大小大于接口上的 MTU,隧道中的包将被分片发送。
注意
对于 GRE 类型镜像,将在 IP 头部之上添加 8 字节的额外头部。
对于 ERSPAN,为 GRE 添加 8 字节,为 ERSPAN 添加 8 字节,并添加额外的 4 字节 CRC 尾部,总而言之,在这种情况下添加了 20 字节的额外头部。
建议的 API 定义
mirror_types_list = ['erspan', 'gre']
RESOURCE_ATTRIBUTE_MAP = {
'tap_mirror': {
'id': {
'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None}, 'is_visible': True,
'primary_key': True},
'name': {
'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'description': {
'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'port_id': {
'allow_post': True, 'allow_put': False,
'validate': {'type:uuid': None},
'enforce_policy': True, 'is_visible': True},
'directions': {
'allow_post': True, 'allow_put': False,
'validate': 'type:dict': {
'IN': {'type:integer': None, 'default': None, 'required': False},
'OUT': {'type:integer': None, 'default': None, 'required': False}},
'is_visible': True},
'remote_ip': {
'allow_post': True, 'allow_put': False,
'validate': {'type:ip_address': None},
'is_visible': True},
'mirror_type': {
'allow_post': True, 'allow_put': False,
'validate': {'type:values': mirror_types_list},
'is_visible': True,},
}
}
数据库影响¶
为了将新的 tap_mirror 存储在 DB 中,需要一个新的表 tapmirrors
op.create_table(
'tapmirrors',
sa.Column('id', sa.String(length=db_const.UUID_FIELD_SIZE),
primary_key=True, nullable=False),
sa.Column('project_id', sa.String(
length=db_const.PROJECT_ID_FIELD_SIZE), nullable=False),
sa.Column('name', sa.String(length=db_const.NAME_FIELD_SIZE),
nullable=True),
sa.Column('description', sa.String(
length=db_const.DESCRIPTION_FIELD_SIZE), nullable=True),
sa.Column('port_id', sa.String(db_const.UUID_FIELD_SIZE),
nullable=False, unique=True),
sa.Column('directions', sa.String(255), nullable=False),
sa.Column('remote_ip', sa.String(db_const.IP_ADDR_FIELD_SIZE)),
sa.Column('mirror_type', mirror_type_enum, nullable=False)
)
这也意味着将添加一个新的 TapMirror DB 模型。
用于镜像的 OVN 驱动程序¶
以下显示了基于版本 1 ERSPAN 创建 tap_mirror 的 API 调用以及相应的后端更改
$ # REST API operation
$ curl -g -i -X POST http://<host_ip:9696>/networking/v2.0/taas/tap_mirrors \
-d '{"tap_mirror": {"name": "mirror1", "port_id": "54c4b09f-8b3d-4685-b66d-ce22c67956a9",
"directions": {"OUT": 42}, "remote_ip": "100.109.0.142",
"mirror_type": "erspan"}}'
$ # backend changes
$ sudo ovn-nbctl mirror-list
mirror_out_297b12c0-e9a5-11ed-9f90-07946c615270:
Type : erspan
Sink : 100.109.0.142
Filter : from-lport
Index/Key: 42
$ sudo ovs-vsctl show
Bridge br-int
...
Port ovn-my_mirror2
Interface ovn-mirror_out_297b12c0-e9a5-11ed-9f90-07946c615270
type: erspan
options: {erspan_idx="42", erspan_ver="1", key="42", remote_ip="100.109.0.142"}
注意
使用 GRE 非常相似:OVN 镜像类型将是 GRE,OVS 端口类型将是 GRE。
上述端口选项的描述
erspan_idx是十六进制数,它是 ERSPAN 头部中的索引字段。erspan_ver是版本,对于版本 1 erspan 来说是 1。key是 ERSPAN 头部中的 SpanID 或会话 ID 字段。
注意
ERSPAN index 字段未指定,因为 OVN 不允许将其与 key-tunnel_id 字段分开设置。当 OVN 允许设置它时,未来的 TAAS 更改可以处理它。
Wireshark 中的示例包头部
Generic Routing Encapsulation (ERSPAN)
Flags and Version: 0x1000
Protocol Type: ERSPAN (0x88be)
Sequence Number: 18
Encapsulated Remote Switch Packet ANalysis Type II
0001 .... .... .... = Version: Type II (1)
.... 0000 0000 0000 = Vlan: 0
000. .... .... .... = COS: 0
...0 0... .... .... = Encap: Originally without VLAN tag (0)
.... .0.. .... .... = Truncated: Not truncated (0)
.... ..00 0000 0010 = SpanID: 42
0000 0000 0000 .... .... .... .... .... = Reserved: 0
.... .... .... 0000 0000 0000 0010 0000 = Index: 66
Index: 66: this is from ``erspan_idx=42``. Note that OVN's ``mirror-add``
command (and the OVN ovn-nb.ovsschema) accepts one `index` parameter and
uses that for OVS' ``key`` and ``erspan_idx`` parameters, but erspan_idx
is a hex value, so this is probably a bug or not fully considered thing
in OVN.
SpanId: 42: this is from ``key=42``
使用 OVN 镜像源端口的传入和传出流量,必须创建 2 个镜像(因为 OVN 镜像只能具有 from-lport 或 to-lport 作为方向),并附加到端口(logical-switch-port),一个使用 filter=from-lport,一个使用 filter=to-lport。
注意
OVN 镜像的实现允许用户通过选择方向作为 filter 使用 ovn-nbctl,或通过 ovsdb(参见 ovn-nb.ovsschema mirror 表)创建镜像。
因此,如果用户使用方向 IN 创建 tap_mirror,则 filter 将是 to-lport,如果 OUT,则 filter 将是 from-lport,在 directions 字典中同时存在 IN 和 OUT 的情况下,将创建 2 个镜像,一个使用 to-lport,一个使用 from-lport。
这意味着,在镜像传入和传出流量的情况下,tap-as-a-service 将为每个 tap_mirror 在 br-int 上创建 2 个 ERSPAN 或 GRE 端口。
OVS 驱动程序更改¶
为了在两个驱动程序之间保持一致性,本规范建议也为 OVS 驱动程序使用 GRE 和 ERSPAN 版本 1。
端到端调用如下所示
$ # REST API operation
$ curl -g -i -X POST http://<host_ip:9696>/networking/v2.0/taas/tap_mirrors \
-d '{"tap_mirror": {"name": "mirror1", "port_id": "54c4b09f-8b3d-4685-b66d-ce22c67956a9",
"direction": "IN", "remote_ip": "100.109.0.142", "tunnel_id": "42",
"mirror_type": "erspan"}}'
$ # Backend changes
$ sudo ovs-vsctl show
Bridge br-tap
...
Port mirror_in_ed6046d
Interface mirror_in_ed6046d
type: erspan
options: {erspan_idx="42", erspan_ver="1", key="2", remote_ip="100.109.0.84"}
$ sudo ovs-ofctl dump-flows br-tap
...
... priority=20,dl_dst=fa:16:3e:d3:3a:d1 actions=output:"mirror_in_ed6046d"
有关端口属性 key、erspan_idx 的含义,请参见 OVN 驱动程序用于镜像。
有关在用户选择 IN 和 OUT 方向时将如何处理的情况,请参见 OVN 驱动程序用于镜像。OVS 驱动程序将像 OVN 那样为两个方向创建 2 个 OVS 端口。
对于这两个方向,将在 br-tap 上安装 2 个不同的流,并在 action 字段中具有不同的输出端口
$ # Direction IN
$ sudo ovs-ofctl dump-flows br-tap
...
... priority=20,dl_dst=fa:16:3e:d3:3a:d1 actions=output:"mirror_in_ed6046d"
$ # Direction OUT
$ sudo ovs-ofctl dump-flows br-tap
...
... priority=20,dl_src=fa:16:3e:d3:3a:d1 actions=output:"mirror_out_ed6046d"
超出范围¶
本规范不建议使 OVN 驱动程序与当前的 OVS 或 SRIOV 驱动程序完全兼容。因此,建议的 OVN 驱动程序将仅实现 ERSPAN。
使 OVN 驱动程序与当前的 OVS 或 SRIOV 驱动程序完全兼容可以在未来的规范中进行。
实现¶
负责人¶
Lajos Katona (~lajoskatona) <lajos.katona@est.tech>, <katonalala@gmail.com>
工作项¶
添加新的 REST API 扩展,用于 tap-as-a-service、neutron-lib 和 tap-as-a-service 更改。
相应地更改 tap-as-a-service db 模式。
调整 ovsdbapp,使其能够操作 ovsdb 和 ovn-northd,并创建镜像。
更改 OVS 驱动程序。
创建一个新的仅 ERSPAN 的 OVN tap-as-a-service 驱动程序。
调整文档。
实施必要的测试。
可以使用浮动 IP 在 tempest 中进行端到端测试。
调整 OpenstackSDK 和必要的 CLI 代码。
调整 Heat 以便能够创建 ERSPAN 镜像。