• Openvswitch原理与代码分析(1):总体架构


     

    一、Opevswitch总体架构

    Openvswitch的架构网上有如下的图表示:

    每个模块都有不同的功能

    ovs-vswitchd 为主要模块,实现交换机的守护进程daemon

    在Openvswitch所在的服务器进行ps aux可以看到以下的进程

    root 1008 0.1 0.8 242948 31712 ? S<Ll Aug06 32:17 ovs-vswitchd unix:/var/run/openvswitch/db.sock -vconsole:emer -vsyslog:err -vfile:info --mlockall --no-chdir --log-file=/var/log/openvswitch/ovs-vswitchd.log --pidfile=/var/run/openvswitch/ovs-vswitchd.pid --detach --monitor

    注意这里ovs-vswitchd监听了一个本机的db.sock文件

    openvswitch.ko为Linux内核模块,支持数据流在内核的交换

    我们使用lsmod列举加载到内核的模块:

    ~# lsmod | grep openvswitch

    openvswitch 66901 0

    gre 13808 1 openvswitch

    vxlan 37619 1 openvswitch

    libcrc32c 12644 2 btrfs,openvswitch

    既有Openvswitch.ko,也有

    ovsdb-server 轻量级数据库服务器,保存配置信息,ovs-vswitchd通过这个数据库获取配置信息

    通过ps aux可以看到如下进程

    root 985 0.0 0.0 21172 2120 ? S< Aug06 1:20 ovsdb-server /etc/openvswitch/conf.db -vconsole:emer -vsyslog:err -vfile:info --remote=punix:/var/run/openvswitch/db.sock --private-key=db:Open_vSwitch,SSL,private_key --certificate=db:Open_vSwitch,SSL,certificate --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --no-chdir --log-file=/var/log/openvswitch/ovsdb-server.log --pidfile=/var/run/openvswitch/ovsdb-server.pid --detach –monitor

    可以看出,ovsdb-server将配置信息保存在conf.db中,并通过db.sock提供服务,ovs-vswitchd通过这个db.sock从这个进程读取配置信息。

    /etc/openvswitch/conf.db是json格式的,可以通过命令ovsdb-client dump将数据库结构打印出来。

    数据库结构包含如下的表格。

    数据库结构如下:

    通过ovs-vsctl创建的所有的网桥,网卡,都保存在数据库里面,ovs-vswitchd会根据数据库里面的配置创建真正的网桥,网卡。

    ovs-dpctl 用来配置switch内核模块。

    ovs-vsctl 查询和更新ovs-vswitchd的配置。

    ovs-appctl 发送命令消息,运行相关daemon。

    ovs-ofctl 查询和控制OpenFlow交换机和控制器。

    二、Openvswitch的代码结构

    Openvwitch进行数据流交换的主要逻辑都是在ovs-vswitchd和openvswitch.ko里面实现的。

    ovs-vswitchd会从ovsdb-server读取配置,然后调用ofproto层进行虚拟网卡的创建或者流表的操作。

    Ofproto是一个库,实现了软件的交换机和对流表的操作。

    Netdev层抽象了连接到虚拟交换机上的网络设备。

    Dpif层实现了对于流表的操作。

    对于OVS来讲,有以下几种网卡类型

    1). netdev: 通用网卡设备 eth0 veth

    接收: 一个nedev在L2收到报文后回直接通过ovs接收函数处理,不会再走传统内核协议栈.

    发送: ovs中的一条流指定从该netdev发出的时候就通过该网卡设备发送

    2). internal: 一种虚拟网卡设备

    接收: 当从系统发出的报文路由查找通过该设备发送的时候,就进入ovs接收处理函数

    发送: ovs中的一条流制定从该internal设备发出的时候,该报文被重新注入内核协议栈

    3). gre device: gre设备. 不管用户态创建多少个gre tunnel, 在内核态有且只有一个gre设备

    接收: 当系统收到gre报文后,传递给L4层解析gre header, 然后传递给ovs接收处理函数

    发送: ovs中的一条流制定从该gre设备发送, 报文会根据流表规则加上gre头以及外层包裹ip,查找路由发送

    在如上的代码结构中,vswitchd中就是ovs-vswitchd的入口代码,ovsdb就是ovsdb-server的代码,ofproto即上述的中间抽象层,lib下面有netdev,dpif的实现,datapath里面就是内核模块openvswitch.ko的代码。

    三、ovs-vswitchd和openvswitch.ko的交互方式netlink

    datapath 运行在内核态,ovs-vswitchd 运行在用户态,两者通过netlink 通信。

    netlink 是一种灵活和强大的进程间通信机制(socket),甚至可以沟通用户态和内核态。

    netlink 是全双工的。作为socket,netlink 的地址族是AF_NETLINK(TCP/IP socket 的地址族是AF_INET)

    目前有大量的通信场景应用了netlink,这些特定扩展和设计的netlink 通信bus,被定义为family。比如NETLINK_ROUTE、NETLINK_FIREWALL、NETLINK_ARPD 等。

    因为大量的专用family 会占用了family id,而family id 数量自身有限(kernel 允许32个);同时为了方便用户扩展使用,一个通用的netlink family 被定义出来,这就是generic netlink family。

    要使用generic netlink,需要熟悉的数据结构包括genl_family、genl_ops 等。

    下面写一个generic netlink的简单实例

    定义family如下

    1. /* attributes */
    2. enum {
    3. DOC_EXMPL_A_UNSPEC,
    4. DOC_EXMPL_A_MSG,
    5. __DOC_EXMPL_A_MAX,
    6. };
    7. #define DOC_EXMPL_A_MAX (__DOC_EXMPL_A_MAX - 1)
    8. /* attribute policy */
    9. static struct nla_policy doc_exmpl_genl_policy[DOC_EXMPL_A_MAX + 1] = {
    10. [DOC_EXMPL_A_MSG] = { .type = NLA_NUL_STRING },
    11. };
    12. /* family definition */
    13. static struct genl_family doc_exmpl_gnl_family = {
    14. .id = GENL_ID_GENERATE,
    15. .hdrsize = 0,
    16. .name = "DOC_EXMPL",
    17. .version = 1,
    18. .maxattr = DOC_EXMPL_A_MAX,
    19. };

    定义op如下

    1. /* handler */
    2. int doc_exmpl_echo(struct sk_buff *skb, struct genl_info *info)
    3. {
    4. /* message handling code goes here; return 0 on success, negative values on failure */
    5. }
    6. /* commands */
    7. enum {
    8. DOC_EXMPL_C_UNSPEC,
    9. DOC_EXMPL_C_ECHO,
    10. __DOC_EXMPL_C_MAX,
    11. };
    12. #define DOC_EXMPL_C_MAX (__DOC_EXMPL_C_MAX - 1)
    13. /* operation definition */
    14. struct genl_ops doc_exmpl_gnl_ops_echo = {
    15. .cmd = DOC_EXMPL_C_ECHO,
    16. .flags = 0,
    17. .policy = doc_exmpl_genl_policy,
    18. .doit = doc_exmpl_echo,
    19. .dumpit = NULL,
    20. };

    注册family 到generic netlink 机制

    1. int rc;
    2. rc = genl_register_family(&doc_exmpl_gnl_family);
    3. if (rc != 0)
    4. goto failure;

    将操作注册到family

    1. int rc;
    2. rc = genl_register_ops(&doc_exmpl_gnl_family, &doc_exmpl_gnl_ops_echo);
    3. if (rc != 0)
    4.     goto failure;

    Datapath是如何使用netlink的呢?

    在dp_init()函数(datapath.c)中,调用dp_register_genl()完成对四种类型的family 以及相应操作的注册,包括datapath、vport、flow 和packet。

    前三种family,都对应四种操作都包括NEW、DEL、GET、SET,而packet 的操作仅为EXECUTE。

    对于flow这个family的定义如下:

    1. static const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = {
    2.     [OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED },
    3.     [OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED },
    4.     [OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG },
    5. };
    6. static struct genl_family dp_flow_genl_family = {
    7.     .id = GENL_ID_GENERATE,
    8.     .hdrsize = sizeof(struct ovs_header),
    9.     .name = OVS_FLOW_FAMILY,
    10.     .version = OVS_FLOW_VERSION,
    11.     .maxattr = OVS_FLOW_ATTR_MAX,
    12.     SET_NETNSOK
    13. };

    Flow相关的ops的定义如下:

    1. static struct genl_ops dp_flow_genl_ops[] = {
    2.     {
    3.         .cmd = OVS_FLOW_CMD_NEW,
    4.         .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
    5.         .policy = flow_policy,
    6.         .doit = ovs_flow_cmd_new_or_set
    7.     },
    8.     {
    9.         .cmd = OVS_FLOW_CMD_DEL,
    10.         .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
    11.         .policy = flow_policy,
    12.         .doit = ovs_flow_cmd_del
    13.     },
    14.     {
    15.         .cmd = OVS_FLOW_CMD_GET,
    16.         .flags = 0, /* OK for unprivileged users. */
    17.         .policy = flow_policy,
    18.         .doit = ovs_flow_cmd_get,
    19.         .dumpit = ovs_flow_cmd_dump
    20.     },
    21.     {
    22.         .cmd = OVS_FLOW_CMD_SET,
    23.         .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
    24.         .policy = flow_policy,
    25.         .doit = ovs_flow_cmd_new_or_set,
    26.     },
    27. };

    Ovs-vswitchd作为客户端如何使用netlink

    Ovs-vswitchd 对于netlink 的实现,主要在lib/netlink-socket.c 文件中。

    libdpif-provider.h定义了struct dpif_class {,包含一系列函数指针,例如open,close等。

    真正的dpif_class有两种

    一个是dpif-netdev.c中定义的const struct dpif_class dpif_netdev_class = {

    1. const struct dpif_class dpif_netdev_class = {
    2.     "netdev",
    3.     dpif_netdev_init,
    4.     dpif_netdev_enumerate,
    5.     dpif_netdev_port_open_type,
    6.     dpif_netdev_open,
    7.     dpif_netdev_close,
    8.     dpif_netdev_destroy,
    9.     dpif_netdev_run,
    10.     dpif_netdev_wait,
    11.     dpif_netdev_get_stats,
    12.     dpif_netdev_port_add,
    13.     dpif_netdev_port_del,
    14.     dpif_netdev_port_query_by_number,
    15.     dpif_netdev_port_query_by_name,
    16.     NULL, /* port_get_pid */
    17.     dpif_netdev_port_dump_start,
    18.     dpif_netdev_port_dump_next,
    19.     dpif_netdev_port_dump_done,
    20.     dpif_netdev_port_poll,
    21.     dpif_netdev_port_poll_wait,
    22.     dpif_netdev_flow_flush,
    23.     dpif_netdev_flow_dump_create,
    24.     dpif_netdev_flow_dump_destroy,
    25.     dpif_netdev_flow_dump_thread_create,
    26.     dpif_netdev_flow_dump_thread_destroy,
    27.     dpif_netdev_flow_dump_next,
    28.     dpif_netdev_operate,
    29.     NULL, /* recv_set */
    30.     NULL, /* handlers_set */
    31.     dpif_netdev_pmd_set,
    32.     dpif_netdev_queue_to_priority,
    33.     NULL, /* recv */
    34.     NULL, /* recv_wait */
    35.     NULL, /* recv_purge */
    36.     dpif_netdev_register_dp_purge_cb,
    37.     dpif_netdev_register_upcall_cb,
    38.     dpif_netdev_enable_upcall,
    39.     dpif_netdev_disable_upcall,
    40.     dpif_netdev_get_datapath_version,
    41.     NULL, /* ct_dump_start */
    42.     NULL, /* ct_dump_next */
    43.     NULL, /* ct_dump_done */
    44.     NULL, /* ct_flush */
    45. };

    一种是在dpif-netlink.c中,定义了const struct dpif_class dpif_netlink_class = {

    1. const struct dpif_class dpif_netlink_class = {
    2.     "system",
    3.     NULL, /* init */
    4.     dpif_netlink_enumerate,
    5.     NULL,
    6.     dpif_netlink_open,
    7.     dpif_netlink_close,
    8.     dpif_netlink_destroy,
    9.     dpif_netlink_run,
    10.     NULL, /* wait */
    11.     dpif_netlink_get_stats,
    12.     dpif_netlink_port_add,
    13.     dpif_netlink_port_del,
    14.     dpif_netlink_port_query_by_number,
    15.     dpif_netlink_port_query_by_name,
    16.     dpif_netlink_port_get_pid,
    17.     dpif_netlink_port_dump_start,
    18.     dpif_netlink_port_dump_next,
    19.     dpif_netlink_port_dump_done,
    20.     dpif_netlink_port_poll,
    21.     dpif_netlink_port_poll_wait,
    22.     dpif_netlink_flow_flush,
    23.     dpif_netlink_flow_dump_create,
    24.     dpif_netlink_flow_dump_destroy,
    25.     dpif_netlink_flow_dump_thread_create,
    26.     dpif_netlink_flow_dump_thread_destroy,
    27.     dpif_netlink_flow_dump_next,
    28.     dpif_netlink_operate,
    29.     dpif_netlink_recv_set,
    30.     dpif_netlink_handlers_set,
    31.     NULL, /* poll_thread_set */
    32.     dpif_netlink_queue_to_priority,
    33.     dpif_netlink_recv,
    34.     dpif_netlink_recv_wait,
    35.     dpif_netlink_recv_purge,
    36.     NULL, /* register_dp_purge_cb */
    37.     NULL, /* register_upcall_cb */
    38.     NULL, /* enable_upcall */
    39.     NULL, /* disable_upcall */
    40.     dpif_netlink_get_datapath_version, /* get_datapath_version */
    41. #ifdef __linux__
    42.     dpif_netlink_ct_dump_start,
    43.     dpif_netlink_ct_dump_next,
    44.     dpif_netlink_ct_dump_done,
    45.     dpif_netlink_ct_flush,
    46. #else
    47.     NULL, /* ct_dump_start */
    48.     NULL, /* ct_dump_next */
    49.     NULL, /* ct_dump_done */
    50.     NULL, /* ct_flush */
    51. #endif
    52. };

    datapath 中对netlink family 类型进行了注册,ovs-vswitchd 在使用这些netlink family 之前需要获取它们的信息,这一过程主要在lib/dpif-netlink.c 文件(以dpif_netlink_class 为例),dpif_netlink_init ()函数。

    1. static int
    2. dpif_netlink_init(void)
    3. {
    4.     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
    5.     static int error;
    6.  
    7.     if (ovsthread_once_start(&once)) {
    8.         error = nl_lookup_genl_family(OVS_DATAPATH_FAMILY,
    9.                                       &ovs_datapath_family);
    10.         if (error) {
    11.             VLOG_ERR("Generic Netlink family '%s' does not exist. "
    12.                      "The Open vSwitch kernel module is probably not loaded.",
    13.                      OVS_DATAPATH_FAMILY);
    14.         }
    15.         if (!error) {
    16.             error = nl_lookup_genl_family(OVS_VPORT_FAMILY, &ovs_vport_family);
    17.         }
    18.         if (!error) {
    19.             error = nl_lookup_genl_family(OVS_FLOW_FAMILY, &ovs_flow_family);
    20.         }
    21.         if (!error) {
    22.             error = nl_lookup_genl_family(OVS_PACKET_FAMILY,
    23.                                           &ovs_packet_family);
    24.         }
    25.         if (!error) {
    26.             error = nl_lookup_genl_mcgroup(OVS_VPORT_FAMILY, OVS_VPORT_MCGROUP,
    27.                                            &ovs_vport_mcgroup);
    28.         }
    29.  
    30.         ovsthread_once_done(&once);
    31.     }
    32.  
    33.     return error;
    34. }

    完成这些查找后,ovs-vswitchd 即可利用dpif 中的api,通过发出这些netlink 消息给datapath,实现对datapath 的操作。

  • 相关阅读:
    你所不知道的mfc…mfc项目索引 &mfc调优指南 &mfc vc添加添加子功能指南
    Cu 大彻大悟内存管理 mm (update 0410)
    [转]Linux iostat监测IO状态
    linux virtual memory layout by moniskiller upload [读书笔记]
    河畔找到的 面经笔经
    【转】Linux本地磁盘(硬盘)介绍
    读写UTF8、Unicode文件
    codesmith执行时提示“调用的目标发生了异常”的处理过程经验。
    DB2表信息以及字段信息的表
    iBatis.NET获取resultMap相关数据
  • 原文地址:https://www.cnblogs.com/liuhongru/p/11398252.html
Copyright © 2020-2023  润新知