• 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. }
  • 相关阅读:
    yzoj P2344 斯卡布罗集市 题解
    yzoj P2350 逃离洞穴 题解
    yzoj P2349 取数 题解
    JXOI 2017 颜色 题解
    NOIP 2009 最优贸易 题解
    CH 4302 Interval GCD 题解
    CH4301 Can you answer on these queries III 题解
    Luogu2533[AHOI2012]信号塔
    Luogu3320[SDOI2015]寻宝游戏
    Luogu3187[HNOI2007]最小矩形覆盖
  • 原文地址:https://www.cnblogs.com/liuhongru/p/11402074.html
Copyright © 2020-2023  润新知