基于隧道(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-servicestap-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。本规范建议保留 INOUT 作为字典的键。方向的含义

    • 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-lportto-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 字典中同时存在 INOUT 的情况下,将创建 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"

有关端口属性 keyerspan_idx 的含义,请参见 OVN 驱动程序用于镜像

有关在用户选择 INOUT 方向时将如何处理的情况,请参见 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 驱动程序完全兼容可以在未来的规范中进行。

实现

负责人

工作项

  • 添加新的 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 镜像。

参考资料