ovs patch port 对比 veth pair

ovs patch port 和 veth pair 功能上很像(类似 跳线)

OVS 跳线端口就像一根从一个 (OVS) 交换机端口插入到另一个 (OVS) 交换机端口的物理电缆。它与 Linux veth pair 非常相似。

事实上,在某些情况下,这两者可以替换使用。

例如,在 OpenStack 计算节点中,通常有两个 ovs 桥:br-intbr-eth1 的在早期的 OpenStack 版本(在 Kilo 之前)中,两者通过 linux veth 对连接。但是,在较新的发行版(例如 Liberty 之后的发行版)中,默认连接方式已更改为 OVS 补丁端口。

根据一些资料,从 linux veth 对切换到 OVS 补丁端口的原因是为了性能考虑。除此之外,至少对于 OpenStack,补丁端口带来了另一个巨大的好处:在 OVS 中子代理重启期间,实例 (VM) 的流量不会中断 - 这就是 **OVS 代理正常重启] ** 在较新的 OpenStack 版本中实现的。

1. OVS netdev

网络设备(例如物理网卡)有两端(部分):

  • 一端在内核中,负责发送/接收,
  • 一端在用户空间中,用于管理内核部分,例如更改设备 MTU 大小、禁用/启用队列等。

内核和用户空间空间之间的通信通常通过 netlinkioctl(已弃用)进行。

对于 TUN/TAP 等虚拟网络设备,其工作过程类似,需要确保 TAP 设备接收的数据包不是来自外部,而是来自用户空间;

TAP 设备发送的数据包不会发送到外部,而是进入用户空间。

在 OVS 中, 一个 struct netdev 实例代表 OVS 用户空间中的一个网络设备,它用于控制这个设备的内核端,它可以是物理网卡、TAP 设备或其他类型。

c 复制代码
/* A network device (e.g. an Ethernet device) */
struct netdev {
    char *name;                         /* Name of network device. */
    const struct netdev_class *netdev_class; /* Functions to control
                                                this device. */
    ...

    int n_txq;
    int n_rxq;
    int ref_cnt;                        /* Times this devices was opened. */
};

netdev_class 是所有网络设备的一般抽象,定义在 lib/netdev-provider.h 中。

c 复制代码
struct netdev_class {
    const char *type; /* Type of netdevs in this class, e.g. "system", "tap", "gre", etc. */
    bool is_pmd;      /* If 'true' then this netdev should be polled by PMD threads. */

    /* ## Top-Level Functions ## */
    int (*init)(void);
    void (*run)(const struct netdev_class *netdev_class);
    void (*wait)(const struct netdev_class *netdev_class);

    /* ## netdev Functions ## */
    int (*construct)(struct netdev *);
    void (*destruct)(struct netdev *);

    ...

    int (*rxq_recv)(struct netdev_rxq *rx, struct dp_packet_batch *batch);
    void (*rxq_wait)(struct netdev_rxq *rx);
};

任何设备类型都必须在使用 (成为 netdev provider ) 之前实现 netdev_class 中的方法,因此在不同平台上有不同类型的实现:用于 Linux 平台、用于 BSD 平台、用于 Windows 等。

2. Linux netdev

  • system - netdev_linux_class

system设备的 send() 方法会通过 AF_PACKET 套接字向内核发送数据包

  • internal - netdev_internal_class

internal设备的 send() 方法将通过 AF_PACKET 套接字向内核发送数据包

  • tap - netdev_tap_class

TAP 设备的 send() 方法将通过 TAP 设备的用户空间部分的 write 系统调用将数据包发送到内核: write(netdev->tap_fd, data, size)

下面是 3 种 linux netdev 的声明,其中 NETDEV_LINUX_CLASS 是一个用于初始化所有回调的宏:

kotlin 复制代码
const struct netdev_class netdev_linux_class =
    NETDEV_LINUX_CLASS(
        "system",
        netdev_linux_construct,
        netdev_linux_get_stats,
        netdev_linux_get_features,
        netdev_linux_get_status);

const struct netdev_class netdev_tap_class =
    NETDEV_LINUX_CLASS(
        "tap",
        netdev_linux_construct_tap,
        netdev_tap_get_stats,
        netdev_linux_get_features,
        netdev_linux_get_status);

const struct netdev_class netdev_internal_class =
    NETDEV_LINUX_CLASS(
        "internal",
        netdev_linux_construct,
        netdev_internal_get_stats,
        NULL,                  /* get_features */
        netdev_internal_get_status);

3. vport netdev

vport 是 OVS 数据路径中的 OVS 抽象虚拟端口。vport netdevs 是 OVS vports 的用户空间部分。它分为隧道型补丁型两大类。

  1. tunnel class 隧道类 用于隧道网络

    • geneve 日内瓦
    • gre
    • vxlan
    • lisp
    • stt 短期试验
  2. patch - patch_class

    用于在不同 OVS 网桥之间转发数据包

隧道 vport 的注册位于 netdev_vport_tunnel_register() 中,

patch 端口在 netdev_vport_patch_register() 中。

然后,它们都将调用 netdev_register_provider()

arduino 复制代码
void
netdev_vport_tunnel_register(void)
{
    static const struct vport_class vport_classes[] = {
        TUNNEL_CLASS("geneve", "genev_sys", netdev_geneve_build_header,
                netdev_tnl_push_udp_header, netdev_geneve_pop_header),
        TUNNEL_CLASS("gre", "gre_sys", netdev_gre_build_header,
                netdev_gre_push_header, netdev_gre_pop_header),
        TUNNEL_CLASS("vxlan", "vxlan_sys", netdev_vxlan_build_header,
                netdev_tnl_push_udp_header, netdev_vxlan_pop_header),
        TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL),
        TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL),
    };

    for (i = 0; i < ARRAY_SIZE(vport_classes); i++) {
        netdev_register_provider(&vport_classes[i].netdev_class);
    }
}

void
netdev_vport_patch_register(void)
{
    static const struct vport_class patch_class =
        { NULL,
            { "patch", false,
              VPORT_FUNCTIONS(get_patch_config, set_patch_config,
                              NULL, NULL, NULL, NULL, NULL) }};
    netdev_register_provider(&patch_class.netdev_class);
}

以下是简化的 init 宏:

php 复制代码
#define VPORT_FUNCTIONS(GET_CONFIG, SET_CONFIG,             \
                        GET_TUNNEL_CONFIG, GET_STATUS,      \
                        BUILD_HEADER,                       \
                        PUSH_HEADER, POP_HEADER)            \
    netdev_vport_alloc,                                     \
    netdev_vport_construct,                                 \
    BUILD_HEADER,                                           \
    PUSH_HEADER,                                            \
    POP_HEADER,                                             \
                                                            \
    NULL,                       /* send */                  \
    NULL,                       /* send_wait */             \
    ...
    NULL,                   /* rx_recv */                  \
    NULL,                   /* rx_drain */


#define TUNNEL_CLASS(NAME, DPIF_PORT, BUILD_HEADER, PUSH_HEADER, POP_HEADER)   \
    { DPIF_PORT,                                                               \
        { NAME, false,                                                         \
          VPORT_FUNCTIONS(get_tunnel_config,                                   \
                          set_tunnel_config,                                   \
                          get_netdev_tunnel_config,                            \
                          tunnel_get_status,                                   \
                          BUILD_HEADER, PUSH_HEADER, POP_HEADER) }}

注意 ,所有 vport 类型的 netdev 的 sendrx_recv 回调都是 NULL

这意味着:数据包无法通过 vport netdevs 从用户空间发送到内核,并且 vport 不会从物理网卡接收数据包

实际上,vport 用于在 DataPath 内部转发数据包 ,或者通过调用内核dev_queue_xmit()方法将数据包发送出去 。

patch port 没有实现 netdev_class 的 send 和 receive 方法,因此无法通过 patch port 从用户空间向内核 vport 发送数据包,并且 patch port 不会接收来自物理设备的数据包。

实际上,一个 patch 端口只接收来自 ovs bridge (ofproto) 另一端(端口)的数据包。通过这种方式,它连接了两侧(通常是两个 OVS 桥)。

参考:

  1. arthurchiao.art/blog/ovs-de...
相关推荐
Asthenia04128 小时前
Spring AOP 和 Aware:在Bean实例化后-调用BeanPostProcessor开始工作!在初始化方法执行之前!
后端
Asthenia04129 小时前
什么是消除直接左递归 - 编译原理解析
后端
Asthenia04129 小时前
什么是自上而下分析 - 编译原理剖析
后端
Asthenia041210 小时前
什么是语法分析 - 编译原理基础
后端
Asthenia041210 小时前
理解词法分析与LEX:编译器的守门人
后端
uhakadotcom10 小时前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
Asthenia041211 小时前
Spring扩展点与工具类获取容器Bean-基于ApplicationContextAware实现非IOC容器中调用IOC的Bean
后端
Asthenia041211 小时前
Java受检异常与非受检异常分析
后端