zoukankan      html  css  js  c++  java
  • ovs-vswitchd的启动分析

    ovs-vswitchd的启动分析(无修改源码)

    (一)主要数据结构和概念了解

    1.概念

    在 OVS 中, 有几个非常重要的概念:

    Bridge: Bridge 代表一个以太网交换机(Switch),一个主机中可以创建一个或者多个 Bridge 设备。
    
    Port: 端口与物理交换机的端口概念类似,每个 Port 都隶属于一个 Bridge。
    
    Interface: 连接到 Port 的网络接口设备。在通常情况下,Port 和 Interface 是一对一的关系, 只有在配置 Port 为 bond 模式后,Port 和 Interface 是一对多的关系。

    当我们创建了一个交换机(网桥)以后,此时网络功能不受影响,但是会产生一个虚拟网卡,名字就是brname,之所以会产生一个虚拟网卡,是为了实现接下来的网桥(交换机)功能。

    有了这个交换机以后,我还需要为这个交换机增加端口(port),一个端口,就是一个物理网卡,当网卡加入到这个交换机之后,其工作方式就和普通交换机的一个端口的工作方式类似了

    这里要特别注意,网卡加入网桥以后,要按照网桥的工作标准工作,那么加入的一个端口就必须是以混杂模式工作,工作在链路层,处理2层的帧,所以这个port就不需要配置IP了。(你没见过哪个交换的端口有IP的吧)

    那么接下来你可能会问,通常的交换机不都是有一个管理接口,可以telnet到交换机上进行配置吧,那么在OVS中创建的虚拟交换机有木有这种呢,有的!上面既然创建交换机brname的时候产生了一个虚拟网口brname,那么,你给这个虚拟网卡配置了IP以后,就相当于给交换机的管理接口配置了IP,此时一个正常的虚拟交换机就搞定了。

    2.数据结构

    在ofproto-provider.h中注释里是这样说的。

    这里定义了四类数据结构:

    Struct ofproto表示一个交换机
    
    Struct ofport表示交换机上的一个端口
    
    Struct rule表示交换机上的一条flow规则
    
    Struct ofgroup表示一个flow规则组

    (二)main主函数分析

    在文件openvswitch-2.11.4/vswitchd/ovs-vswitchd.c中!

    Openvswitch主要管理两种类型的设备,一个是创建的拟网桥,一个是连接到虚拟网桥上的设备

    int
    main(int argc, char *argv[])
    {
        ...   
        线程开启、网桥初始化等等
        ...
    
        while (!exiting) {
            memory_run();
            if (memory_should_report()) {
                struct simap usage;
    
                simap_init(&usage);
                bridge_get_memory_usage(&usage);
                memory_report(&usage);
                simap_destroy(&usage);
            }
            bridge_run();  //其中bridge_run就是初始化数据库中已经创建的虚拟网桥。
            unixctl_server_run(unixctl);
            netdev_run();  //在内核中添加虚拟网卡
    
            memory_wait();
            bridge_wait();
            unixctl_server_wait(unixctl);
            netdev_wait();
            if (exiting) {
                poll_immediate_wake();
            }
            poll_block();
            if (should_service_stop()) {
                exiting = true;
            }
        }
        
        ...
        退出和销毁操作
        ...
    
        return 0;
    }

    (三)虚拟网桥的初始化bridge_run

    https://www.pianshen.com/article/53011366500/

    在文件openvswitch-2.11.4/vswitchd/bridge.c中!

    bridge_run --调用--> 

    void
    bridge_run(void)
    {
    
        bridge_run__();
      if (ovsdb_idl_get_seqno(idl) != idl_seqno ||
            if_notifier_changed(ifnotifier)) {
          ...
    bridge_reconfigure(cfg ? cfg : &null_cfg);       ...
    } }

    分支(一):主要会用到

    bridge_run__ --调用--> ofproto_run让每一个网桥去执行他对应的功能

    static void
    bridge_run__(void)
    {
        ......
        其他操作
        ......
    
        /* Let each bridge do the work that it needs to do. */
        HMAP_FOR_EACH (br, node, &all_bridges) {
            ofproto_run(br->ofproto);
        }
    }

    在文件openvswitch-2.11.4/ofproto/ofproto-dpif.c中!

    ofproto_run --回调--> run方法

    const struct ofproto_class ofproto_dpif_class = {
        ...
        run,
        ...
      port_add,    ...
    };
    int
    ofproto_run(struct ofproto *p)  //--------------重点
    {
        error = p->ofproto_class->run(p);
    ......
    connmgr_run(p->connmgr, handle_openflow); return error; }

    在文件openvswitch-2.11.4/ofproto/ofproto-dpif.c中!

    run方法会初始化netflow, sflow, ipfix,stp, rstp, mac address learning等一系列操作。

    同时也会去校验流表项规则是否过期!!!

    static int
    run(struct ofproto *ofproto_)
    {
        stp_run(ofproto);
        rstp_run(ofproto);
    
        new_dump_seq = seq_read(udpif_dump_seq(ofproto->backer->udpif));
    
        if (ofproto->dump_seq != new_dump_seq) {
            struct rule *rule, *next_rule;
    if (ofproto->dump_seq != new_dump_seq) {
            struct rule *rule, *next_rule;
            long long now = time_msec();
            /* We know stats are relatively fresh, so now is a good time to do some
             * periodic work. */
            ofproto->dump_seq = new_dump_seq;
    
            ovs_mutex_lock(&ofproto_mutex);
            LIST_FOR_EACH_SAFE (rule, next_rule, expirable,
                                &ofproto->up.expirable) {
                rule_expire(rule_dpif_cast(rule), now); //-------这里校验是否超时
            }
            ovs_mutex_unlock(&ofproto_mutex);
    
        }
        return 0;
    }

    分支(二):不会被用到,了解即可

    bridge_reconfigure(从ovsdb-server里面读取出来的配置) --调用--> bridge_add_ports对于每一个网桥,将网卡添加进去

    static void
    bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
    {
    
        HMAP_FOR_EACH (br, node, &all_bridges) {
            bridge_add_ports(br, &br->wanted_ports);
            shash_destroy(&br->wanted_ports);
        }
    
        bridge_run__();
    }

    bridge_add_ports --调用--> bridge_add_ports__ --调用--> iface_create --调用--> iface_do_create --调用--> netdev_open和ofproto_port_add

    int
    ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev,
                     ofp_port_t *ofp_portp)
    {
    
        error = ofproto->ofproto_class->port_add(ofproto, netdev);
    
    }

    ofproto_port_add --回调--> port_add --调用--> dpif_port_add

    int
    dpif_port_add(struct dpif *dpif, struct netdev *netdev, odp_port_t *port_nop)
    {
        error = dpif->dpif_class->port_add(dpif, netdev, &port_no);
    }

    dpif_port_add --回调--> port_add/dpif_netdev_port_add

    const struct dpif_class dpif_netdev_class = {
        "netdev",
        .... 
      dpif_netdev_port_add,    ....
    };

    dpif_netdev_port_add --调用-->  do_add_port

    static int
    dpif_netdev_port_add(struct dpif *dpif, struct netdev *netdev,
                         odp_port_t *port_nop)
    {
        struct dp_netdev *dp = get_dp_netdev(dpif);
    
        ovs_mutex_lock(&dp->port_mutex);
        dpif_port = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
    if (!error) {
            *port_nop = port_no;
            error = do_add_port(dp, dpif_port, netdev_get_type(netdev), port_no);
        }
        ovs_mutex_unlock(&dp->port_mutex);
    
        return error;
    }

    do_add_port这里会调用内核模块openvswitch.ko,在内核中添加虚拟网卡。

    static int
    do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
                odp_port_t port_no)
        OVS_REQUIRES(dp->port_mutex)
    {
        struct dp_netdev_port *port;
        int error;
    
        /* Reject devices already in 'dp'. */
        if (!get_port_by_name(dp, devname, &port)) {
            return EEXIST;
        }
    
        error = port_create(devname, type, port_no, &port);
        if (error) {
            return error;
        }
    
        hmap_insert(&dp->ports, &port->node, hash_port_no(port_no));
        seq_change(dp->port_seq);
    
        reconfigure_datapath(dp);
    
        return 0;
    }

    (四)虚拟网卡的初始化(与设备关联)netdev_run

    在文件openvswitch-2.11.4/lib/netdev.c:中!

    void netdev_run(void)
        OVS_EXCLUDED(netdev_mutex)
    {
        netdev_initialize();
    
        struct netdev_registered_class *rc;
        CMAP_FOR_EACH (rc, cmap_node, &netdev_classes) {
            if (rc->class->run) {
                rc->class->run(rc->class);
            }
        }
    }

    依次循环调用netdev_classes中的每一个run。

    对于不同类型的虚拟网卡,都有对应的netdev_class。

    #define NETDEV_LINUX_CLASS_COMMON                               
        .run = netdev_linux_run,                                    
       ...
    NETDEV_LINUX_CLASS_COMMON
    const struct netdev_class netdev_linux_class = {
        NETDEV_LINUX_CLASS_COMMON,
        LINUX_FLOW_OFFLOAD_API,
        .type = "system",
        .construct = netdev_linux_construct,
        .get_stats = netdev_linux_get_stats,
        .get_features = netdev_linux_get_features,
        .get_status = netdev_linux_get_status,
        .get_block_id = netdev_linux_get_block_id
    };
    
    const struct netdev_class netdev_tap_class = {
        NETDEV_LINUX_CLASS_COMMON,
        .type = "tap",
        .construct = netdev_linux_construct_tap,
        .get_stats = netdev_tap_get_stats,
        .get_features = netdev_linux_get_features,
        .get_status = netdev_linux_get_status,
    };
    
    const struct netdev_class netdev_internal_class = {
        NETDEV_LINUX_CLASS_COMMON,
        .type = "internal",
        .construct = netdev_linux_construct,
        .get_stats = netdev_internal_get_stats,
        .get_status = netdev_internal_get_status,
    };

    netdev_run --调用--> netdev_linux_run 会调用netlink的sock得到虚拟网卡的状态,并且更新状态。

    static void
    netdev_linux_run(const struct netdev_class *netdev_class OVS_UNUSED)
    {
        struct nl_sock *sock;
        sock = netdev_linux_notify_sock();do {
            ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub);
            error = nl_sock_recv(sock, &buf, &nsid, false);
            if (!error) {
                struct rtnetlink_change change;
    
                if (rtnetlink_parse(&buf, &change)) {
                    struct netdev *netdev_ = NULL;
                    char dev_name[IFNAMSIZ];
    
                    if (!change.ifname) {
                         change.ifname = if_indextoname(change.if_index, dev_name);
                    }
    
                    if (change.ifname) {
                        netdev_ = netdev_from_name(change.ifname);
                    }
                    if (netdev_ && is_netdev_linux_class(netdev_->netdev_class)) {
                        struct netdev_linux *netdev = netdev_linux_cast(netdev_);
    
                        ovs_mutex_lock(&netdev->mutex);
                        netdev_linux_update(netdev, nsid, &change);
                        ovs_mutex_unlock(&netdev->mutex);
                    }
    
                    if (change.ifname &&
                        rtnetlink_type_is_rtnlgrp_link(change.nlmsg_type)) {
    
                        /* Need to try updating the LAG information. */
                        ovs_mutex_lock(&lag_mutex);
                        netdev_linux_update_lag(&change);
                        ovs_mutex_unlock(&lag_mutex);
                    }
                    netdev_close(netdev_);
                }
            } 
        } while (!error);
    } 
  • 相关阅读:
    财务系统重复付款case分析及解决方案
    MySQL体系结构
    安装篇九:安装wordpress(5.4版本)
    安装篇八:配置 Nginx 使其支持 MySQL 应用
    安装篇七:配置 Nginx 使其支持 PHP 应用
    安装篇六:安装PHP(7.2.29版本)
    安装篇五:安装MySQL(5.6.38版本)
    安装篇四:安装NGINX(1.4.0版本)
    安装篇三:系统初始化设置
    安装篇二:CentOS 6.9系统安装
  • 原文地址:https://www.cnblogs.com/ssyfj/p/14767851.html
Copyright © 2011-2022 走看看