之前写的文章,翻了出来跟大家分享下。。。

1.  架构说明

1.1.  整体架构

OVS(openvswitch)是开源的虚拟交换机。也是当前市场上云环境中部署份额最大的交换机。支持 openflow协议,ovsdb协议管理。

一个OVS实例包括,ovsdb-server、ovs-vswitchd、datapath快转模块(linux内核中实现,可选的。dpdk模式是在用户态实现快转,并不需要内核态的datapath模块)。

  • ovsdb-server:作用是对ovsdb操作。
  • ovs-vswitchd:核心模块,作用是实现OpenFlow交换机、和controller通信、和db通信、实现用户态转发、和内核态快转路径通信。
  • datapath:在内核空间实现报文快速转发。

上图从整体架构说明了ovs的工作方式。ovs包括ovsdb配置管理方式和openflow流表转发控制方式。

  1. ovsdb配置管理方式:管理者通过OVSDB管理协议管理OVS交换机。OVSDB管理协议的规范文件是RFC 7047(The Open vSwitch Database Management Protocol)。RFC7047主要包括,定义OVSDB的结构、交互协议(JSON-RPC)、DB的操作类型。
  2. openflow流表控制方式:controller控制器通过OpenFlow协议给OVS交换机下发流表,控制交换机的转发行为。controller不通过OVSDB,而是直接向OVS交换机下发流表。

1.2.  核心组件及其关联关系

ovsdb-server和ovs-vswitchd之间是通过socket交互信息。

ovs-vswitchd通过netlink和内核态快转模块通信。

比如通过ovs-vsctl命令增加ovs交换机接口,ovs-vsctl会通过ovsdb-server向ovsdb更新数据,ovs-vswitchd监测到ovsdb变化时,会更新交换机配置,比如添加接口。

dpdk方式的ovs除了不使用内核模块外,架构和图中相同。

1.3.    内部模块及其关联关系

为了有个整体的了解,下面列几张整体的架构图,不做详细解释,后文会有流程解释。

2.  vswitchd和ovsdb通信

IDL:接口描述语言。IDL是用来描述软件组件接口的一种计算机语言。IDL通过一种中立的方式来描述接口,使得在不同平台上运行的对象和用不同语言编写的程序可以相互通信交流。也就是说,用一种通用的格式,比如下面数据就是描述一个Open_vSwitch表,最后生成C语言描述的表操作.c文件
使用ovsschema格式定义数据库表,IDL解析器会把该文件内容解析成对数据库初始化、操作等函数,生成ovsdb-idl.c文件。

2.1.  建立连接

ovsdb_idl_create建立一个ovsdb的连接session,形如:idl =ovsdb_idl_create(remote, &ovsrec_idl_class, true, true)。

remote类似:unix:/var/run/openvswitch/db.sock。

后续使用该idl(struct ovsdb_idl)和ovsdb通信,更新数据。

2.2.  接收数据

使用jsonrpc_session_recv函数接收数据,形如:msg = jsonrpc_session_recv(idl->session)。

接收消息的结构是:

struct jsonrpc_msg {

    enum jsonrpc_msg_type type;

    char *method;              /* Request or notification only. */

    struct json *params;       /* Request or notification only. */

    struct json *result;       /* Successful reply only. */

    struct json *error;         /* Error reply only. */

    struct json *id;           /* Request or reply only. */

};

其中type是JSON-RPC 1.0定义的消息类型,包括request、notification、reply、error:

/* Messages. */

enum jsonrpc_msg_type {

    JSONRPC_REQUEST,          /* Request. */

    JSONRPC_NOTIFY,           /* Notification.*/

    JSONRPC_REPLY,            /* Successfulreply. */

   JSONRPC_ERROR              /* Error reply. */

};

 

params、result、error、id是不同类型的消息对应的消息数据。

当解析消息后,监测到需要更新本地数据时,调用ovsdb_idl_parse_update函数,解析消息并更新本地数据。

2.3.  json数据解析

/* A JSON value. */

struct json {

    enum json_type type;

    union {

        struct shash *object;  /* Contains "struct json *"s. */

        struct json_array array;

        long long int integer;

        double real;

        char *string;

    } u;

};

 

type字段:说明了该json数据是联合体中的哪一种类型。

/* Type of a JSON value. */

enum json_type {

    JSON_NULL,                 /* null */

    JSON_FALSE,                /* false */

    JSON_TRUE,                 /* true */

    JSON_OBJECT,               /* {"a": b, "c": d, ...}*/

    JSON_ARRAY,                /* [1, 2, 3, ...] */

    JSON_INTEGER,              /* 123. */

    JSON_REAL,                 /* 123.456. */

    JSON_STRING,               /* "..." */

    JSON_N_TYPES

};

 

联合体u:表示数据可以解析的类型。

例如,当type是JSON_OBJECT类型时,且是update2更新操作,那么object字段保存着多个数据表,每张表数据中又携带着待更新的多个row数据,和每row对应的操作(modify、insert、delete、initial)。然后遍历每个表的每行数据,并执行对应的数据操作(modify、insert、delete、initial)对本地数据更新。

注:本地数据和远端的OVSDB并不相同,本地数据是vswitchd从数据库取出数据后临时放到本地结构中的。

2.4.  更新数据改变标记

当监听到ovsdb-server发来的rpc消息时,如果数据库发送改变就改变对应的change_seqno。后续vswitchd根据change_seqno的值是否发生了变化,决定是否重新配置ovs交换机。

3.  dpdk加速处理

3.1.  ovs dpdk加速配置

在使用dpdk 类型的datapath加速之前,需要设置dpdk-init=true启动参数。

设置方法:

ovs-vsctl --no-wait set Open_vSwitch .other_config:dpdk-init=true

设置dpdk的相关参数,都通过other_config选项完成。

主要的参数有:

  • dpdk-init

指定ovs是否初始化和支持dpdk端口。

  • dpdk-lcore-mask

指明dpdk使用的逻辑核。同dpdk的-c参数。

  • dpdk-socket-mem

指明不同numa节点提前申请的大页内存。同dpdk的--socket-mem参数。

  • dpdk-hugepage-dir

大页文件系统mount的目录。同dpdk的--huge-dir参数。

  • vhost-sock-dir

设置vhost-user 套接字的路径。

  • dpdk-extra

其他的dpdk配置参数。

3.2.  dpdk初始化

主要包括:

1)    dpdk eal初始化。

2)    启动dpdk接口状态监控线程(使用dpdk的库函数),如果状态发送变化,则更新netdev设备的变化标记。

3)     注册dpdk类型的netdev class。其中包括dpdk设备类型,ring类型、vhost、vhost client类型。

4.  数据结构抽象关系

4.1.  分层抽象

  • ofproto  class

openflow交换机实现类,用来实现一个openflow交换机。主要包括创建、构造、操作openflowflow等相关方法。

  • dpif  class

datapath接口类,用来和datapath交互。主要包括datapath的open、run、端口操作、端口数据监听等相关方法。datapath是数据面的一种抽象。

  • netdev  class

网络设备抽象类,用来和网络设备交互。主要包括网卡的设置、网卡结构的抽象、数据包发送接收等相关方法。

  • ofproto  class

openflow交换机实现类,用来实现一个openflow交换机。主要包括创建、构造、操作openflowflow等相关方法。

  • dpif  class

datapath接口类,用来和datapath交互。主要包括datapath的open、run、端口操作、端口数据监听等相关方法。datapath是数据面的一种抽象。

  • netdev  class

网络设备抽象类,用来和网络设备交互。主要包括网卡的设置、网卡结构的抽象、数据包发送接收等相关方法。

bridge网桥通过ofproto  class构造一个openflow交换机。再通过dpif  class打开(如果没有对应类型的datapath,就创建一个)对应类型的datapath通路。使用netdev  class构造一个抽象网络设备,上层抽象为端口,然后对网络设备收发包等操作。

         需要说明的是,datapath是按类型分类的,现在ovs有两种类型的datapath:system(对应linux内核数据通路)、netdev(dpdk用户态数据通路)。所以使用同一种datapath类型的ovs网桥共享同一个datapath对象。如下图所示。

4.2.  主要数据结构关系

该部分说明的内容是关于dpdk类型的datapath。

注:A B,表示A是一个变量而非指针,它的结构和内容是B。

流程解释如下:

1)        bridge层

1.1    struct  bridge对应每个ovs网桥最上层的结构。它通过成员指针变量ofproto,联系到struct  ofproto交换机结构内容。

2)        ofproto层

2.1   由于联系到的struct ofproto内容是struct ofproto_dpif结构的一个up成员变量。所以通过struct ofproto地址可以cast到struct ofproto_dpif结构内容,通过该内容和dpif层交互。

2.2   通过struct ofproto_dpif的成员指针变量dpif_backer,联系到struct  dpif_backer结构内容。该结构是dpif操作的入口点。

3)        dpif层

3.1   通过struct  dpif_backer的成员指针变量dpif,联系到structdpif结构内容。

3.2  由于struct dpif结构内容是struct  dpif_netdev结构的dpif成员变量,所以通过structdpif地址可以cast到struct dpif_netdev结构内容。

3.3   struct dpif_netdev结构内容中有指针成员变量dp,通过dp可以联系到struct  dp_netdev结构内容。该内容是datapath的抽象结构内容。

4)        datapath层

4.1  struct  dp_netdev的成员指针变量ports是一个表结构,它联系着多个port端口内容。端口的结构是struct  dp_netdev_port。struct  dp_netdev另外一个成员指针变量netdev,联系着dp层的struct  netdev结构内容,该结构内容是网卡的逻辑抽象。

4.2  struct  dp_netdev_port的指针成员变量rxqs也是一个表结构,它联系着多个队列内容。队列的结构是struct  dp_netdev_rxq.它的成员变量core_id表示该队列所在的core。

4.3   通过struct dp_netdev_rxq的成员指针变量rxq,联系到dp层的struct  netdev_rxq结构内容。structdp_netdev_rxq的成员变量queue_id表示该队列id。

5)        netdev层

5.1   由于struct  netdev结构内容是struct  netdev_dpdk结构的一个up成员变量。所以通过struct  netdev地址可以cast到struct  netdev_dpdk结构内容。

5.2    通过struct  detdev_dpdk结构内容的成员指针变量netdev_class和dpdk类型网卡交互。

5.3   由于struct netdev_rxq结构内容是struct netdev_rxq_dpdk结构内容的一个up成员变量。所以通过struct netdev_rxq地址可以cast到struct netdev_rxq_dpdk结构内容。该内容是dpdk类型的netdev_rxq,它的成员变量port_id是dpdk网络设备的port。

5.  vswitchd启动

简要的概括下vswitchd启动的内容。

1)   和ovsdb-server通信初始化,建立

2)   dpdk初始化

3)   ofproto(OpenFlow交换机)初始化

4)   netdev初始化

5)   网桥配置

更新网桥(添加、删除)

更新端口

创建ofproto(OpenFlow交换机),创建或打开dpif_backer

添加端口,添加到pmd中

配置网桥、端口属性

datapath run

dp_netdev的pmd更新

dpif_run(运行dpif-netdev的datapath,配置netdev等,接收发送报文)

ofproto run,配置ofproto需要的参数和功能(netflow、sflow、ipfix、mac学习)

OpenFlow控制器连接相关run

6.  附录-数据结构查询

6.1.  ofproto_class

是一个OpenFlow交换机的实现类结构,提供的类功能很多,主要分为:

1.        工厂函数功能。如初始化、枚举支持的datapath类型。

2.        datapath类型处理。

3.        OpenFlow交换机功能。如构造、运行等。

4.        OpenFlow流表功能。

5.        OpenFlow配置。

6.        OpenFlow计量。

7.        OpenFlow 1.1 group功能。

8.        获取datapath信息功能。

9.        连接表功能。

6.2.  dpif_class

datapath接口实现类,每种datapath都有对应的接口实现实例。dpif有两种实现类:dpif_netlink_class、dpif_netdev_class。即system、netdev。比如,dpdk的netdevdatapath类型的实现实例dpif_netdev_class。

6.3.  netdev_class

网络设备实现类。包括抽象的设备结构和收发包功能。

6.4.  bridge

6.5.  ofproto

6.6.  port

6.7.  iface

6.8.  ofport

6.9.  ofport_dpif

6.10.  ofproto_port

6.11.  dpif_port

dp向dpif返回的端口信息。

6.12.  ofproto_dpif

6.13.  dpif_backer

datapath类型共享实现实例。

6.14.  udpif

6.15.  dpif

datapath接口。

6.16.  dpif_netdev

netdev类型的datapath的接口。

6.17.  dp_netdev

基于网络设备接口的datapath。

6.18.  dp_netdev_port

基于netdev类型的datapath的端口。

挂载在dp_netdev的ports成员中。

6.19.  netdev

抽象的网络设备。

6.20.  netdev_dpdk

dpdk类型的netdev结构,继承自netdev结构。

6.21.  dp_netdev_rxq

用来表示core和队列的对应关系。

6.22.  netdev_rxq

网络设备的收包队列结构,以队列区分。

6.23.  netdev_rxq_dpdk

dpdk的收包队列结构,继承自netdev_rxq.

端口号和netdev_dpdk中的端口号相同。

使用dpdk的接口函数rte_zmalloc申请,受dpdk的内存管理控制。

6.24.  dp_netdev_pmd_thread

6.25.  rxq_poll

6.26.  tx_port

说明:本来附录是有具体的源代码的,并加了点注释,但弄上去的话,就太多了,最后就只列了下结构的名字和简要的说明。。。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐