OVS 派OFPT_PORT_STATUS 流程

依据openflow合约[OFP1.0-38],当从物理端口ovs datapath 添加,改动或者删除的时候。都会先运行详细动作。然后通过ofp_port_status异步消息告知Controller,比方当我们运行 ovs-vsctl add-port br0 eth0 之类的命令后,就会更新ovsdb数据库。某一个轮询时全局变量 reconfiguring
变为true,从而会又一次配置这个ovs。

if (reconfiguring) {
		// cfg 条目能够追踪到ovsdb中某个配置发生改变
        if (cfg) {
            if (!reconf_txn) {
                reconf_txn = ovsdb_idl_txn_create(idl);
            }
	// 又一次配置每一个cfg,核心入口
            if (bridge_reconfigure_continue(cfg)) {
                ovsrec_open_vswitch_set_cur_cfg(cfg, cfg->next_cfg);
            }
        } else {
            bridge_reconfigure_continue(&null_cfg);
        }
    }

接下来详细运行配置:

static bool
bridge_reconfigure_continue(const struct ovsrec_open_vswitch *ovs_cfg)
{
    struct sockaddr_in *managers;
    int sflow_bridge_number;
    size_t n_managers;
    struct bridge *br;
    bool done;

    assert(reconfiguring);
	// reconfigure首先要做的就是先删除旧端口,而后依据配置构建新端口
    done = bridge_reconfigure_ofp();

    /* Complete the configuration. */
    sflow_bridge_number = 0;
    collect_in_band_managers(ovs_cfg, &managers, &n_managers);
    HMAP_FOR_EACH (br, node, &all_bridges) {
        struct port *port;

        /* We need the datapath ID early to allow LACP ports to use it as the
         * default system ID. */
        bridge_configure_datapath_id(br);

        HMAP_FOR_EACH (port, hmap_node, &br->ports) {
            struct iface *iface;

            port_configure(port);

            LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
                iface_configure_cfm(iface);
                iface_configure_qos(iface, port->cfg->qos);
                iface_set_mac(iface);
            }
        }
        bridge_configure_mirrors(br);
        bridge_configure_flow_eviction_threshold(br);
        bridge_configure_forward_bpdu(br);
        bridge_configure_mac_idle_time(br);
        bridge_configure_remotes(br, managers, n_managers);
        bridge_configure_netflow(br);
        bridge_configure_sflow(br, &sflow_bridge_number);
        bridge_configure_stp(br);
        bridge_configure_tables(br);
    }
    free(managers);

    if (done) {
        /* ovs-vswitchd has completed initialization, so allow the process that
         * forked us to exit successfully. */
        daemonize_complete();
        reconfiguring = false;

        VLOG_INFO("%s (Open vSwitch) %s", program_name, VERSION);
    }

    return done;
}

这里先删除全部的port,再加入:

static bool
bridge_reconfigure_ofp(void)
{
    long long int deadline;
    struct bridge *br;

    time_refresh();
    deadline = time_msec() + OFP_PORT_ACTION_WINDOW;

    /* The kernel will reject any attempt to add a given port to a datapath if
     * that port already belongs to a different datapath, so we must do all
     * port deletions before any port additions. */
    HMAP_FOR_EACH (br, node, &all_bridges) {
        struct ofpp_garbage *garbage, *next;

        LIST_FOR_EACH_SAFE (garbage, next, list_node, &br->ofpp_garbage) {
            /* It's a bit dangerous to call bridge_run_fast() here as ofproto's
             * internal datastructures may not be consistent.  Eventually, when
             * port additions and deletions are cheaper, these calls should be
             * removed. */
            bridge_run_fast();
            ofproto_port_del(br->ofproto, garbage->ofp_port);
            list_remove(&garbage->list_node);
            free(garbage);

            time_refresh();
            if (time_msec() >= deadline) {
                return false;
            }
            bridge_run_fast();
        }
    }

    HMAP_FOR_EACH (br, node, &all_bridges) {
        struct if_cfg *if_cfg, *next;

        HMAP_FOR_EACH_SAFE (if_cfg, next, hmap_node, &br->if_cfg_todo) {
			//这里是核心,在我们的ovs bridge上添加一个接口
            iface_create(br, if_cfg, -1);
            time_refresh();
            if (time_msec() >= deadline) {
                return false;
            }
        }
    }

    return true;
}

依据配置 if_cfg 给br添加一个interface,假设指定的openflowport号是负数。 则表示自己主动分配:

static bool
iface_create(struct bridge *br, struct if_cfg *if_cfg, int ofp_port)
{
    const struct ovsrec_interface *iface_cfg = if_cfg->cfg;
    const struct ovsrec_port *port_cfg = if_cfg->parent;

    struct netdev *netdev;
    struct iface *iface;
    struct port *port;
    int error;

    /* Get rid of 'if_cfg' itself.  We already copied out the interesting
     * bits. */
    hmap_remove(&br->if_cfg_todo, &if_cfg->hmap_node);
    free(if_cfg);

    /* Do the bits that can fail up front.
     *
     * It's a bit dangerous to call bridge_run_fast() here as ofproto's
     * internal datastructures may not be consistent.  Eventually, when port
     * additions and deletions are cheaper, these calls should be removed. */
    bridge_run_fast();
    assert(!iface_lookup(br, iface_cfg->name));
    error = iface_do_create(br, iface_cfg, port_cfg, &ofp_port, &netdev);
    bridge_run_fast();
    if (error) {
        iface_clear_db_record(iface_cfg);
        return false;
    }

    /* Get or create the port structure. */
    port = port_lookup(br, port_cfg->name);
    if (!port) {
        port = port_create(br, port_cfg);
    }

    /* Create the iface structure. */
    iface = xzalloc(sizeof *iface);
    list_push_back(&port->ifaces, &iface->port_elem);
    hmap_insert(&br->iface_by_name, &iface->name_node,
                hash_string(iface_cfg->name, 0));
    iface->port = port;
    iface->name = xstrdup(iface_cfg->name);
    iface->ofp_port = -1;
    iface->netdev = netdev;
    iface->type = iface_get_type(iface_cfg, br->cfg);
    iface->cfg = iface_cfg;

    iface_set_ofp_port(iface, ofp_port);

    /* Populate initial status in database. */
    iface_refresh_stats(iface);
    iface_refresh_status(iface);

    /* Add bond fake iface if necessary. */
    if (port_is_bond_fake_iface(port)) {
        struct ofproto_port ofproto_port;

        if (ofproto_port_query_by_name(br->ofproto, port->name,
                                       &ofproto_port)) {
            struct netdev *netdev;
            int error;

            error = netdev_open(port->name, "internal", &netdev);
            if (!error) {
				// 将这个网络设备增加到我们的openflow switch中
                ofproto_port_add(br->ofproto, netdev, NULL);
                netdev_close(netdev);
            } else {
                VLOG_WARN("could not open network device %s (%s)",
                          port->name, strerror(error));
            }
        } else {
            /* Already exists, nothing to do. */
            ofproto_port_destroy(&ofproto_port);
        }
    }

    return true;
}

调用ofproto(openflow sw接口)详细实现的port_add方法:

int
ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev,
                 uint16_t *ofp_portp)
{
    uint16_t ofp_port;
    int error;

    error = ofproto->ofproto_class->port_add(ofproto, netdev, &ofp_port);
	// 看 dpif_linux_class 的详细实现
    if (!error) {
		// 更新我们的openflow交换机(即ofproto)
        update_port(ofproto, netdev_get_name(netdev));
    }
    if (ofp_portp) {
        *ofp_portp = error ? OFPP_NONE : ofp_port;
    }
    return error;
}
static void
update_port(struct ofproto *ofproto, const char *name)
{
    struct ofproto_port ofproto_port;
    struct ofputil_phy_port pp;
    struct netdev *netdev;
    struct ofport *port;

    COVERAGE_INC(ofproto_update_port);

    /* Fetch 'name''s location and properties from the datapath. */
    netdev = (!ofproto_port_query_by_name(ofproto, name, &ofproto_port)
              ? ofport_open(ofproto, &ofproto_port, &pp)
              : NULL);
    if (netdev) {
        port = ofproto_get_port(ofproto, ofproto_port.ofp_port);
        if (port && !strcmp(netdev_get_name(port->netdev), name)) {
            struct netdev *old_netdev = port->netdev;

            /* 'name' hasn't changed location.  Any properties changed? */
            if (!ofport_equal(&port->pp, &pp)) {
                ofport_modified(port, &pp);
            }

            update_mtu(ofproto, port);

            /* Install the newly opened netdev in case it has changed.
             * Don't close the old netdev yet in case port_modified has to
             * remove a retained reference to it.*/
            port->netdev = netdev;
            port->change_seq = netdev_change_seq(netdev);

            if (port->ofproto->ofproto_class->port_modified) {
                port->ofproto->ofproto_class->port_modified(port);
            }

            netdev_close(old_netdev);
        } else {
            /* If 'port' is nonnull then its name differs from 'name' and thus
             * we should delete it.  If we think there's a port named 'name'
             * then its port number must be wrong now so delete it too. */
            if (port) {
                ofport_remove(port);
            }
            ofport_remove_with_name(ofproto, name);
			// 看这里
            ofport_install(ofproto, netdev, &pp);
        }
    } else {
        /* Any port named 'name' is gone now. */
        ofport_remove_with_name(ofproto, name);
    }
    ofproto_port_destroy(&ofproto_port);
}
static void
ofport_install(struct ofproto *p,
               struct netdev *netdev, const struct ofputil_phy_port *pp)
{
    const char *netdev_name = netdev_get_name(netdev);
    struct ofport *ofport;
    int error;

    /* Create ofport. */
    ofport = p->ofproto_class->port_alloc();
    if (!ofport) {
        error = ENOMEM;
        goto error;
    }
    ofport->ofproto = p;
    ofport->netdev = netdev;
    ofport->change_seq = netdev_change_seq(netdev);
    ofport->pp = *pp;
    ofport->ofp_port = pp->port_no;

    /* Add port to 'p'. */
    hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->ofp_port, 0));
    shash_add(&p->port_by_name, netdev_name, ofport);

    update_mtu(p, ofport);

    /* Let the ofproto_class initialize its private data. */
    error = p->ofproto_class->port_construct(ofport);
    if (error) {
        goto error;
    }
	// 更新操作完毕后。发送通知消息到Controller
    connmgr_send_port_status(p->connmgr, pp, OFPPR_ADD);
    return;

error:
    VLOG_WARN_RL(&rl, "%s: could not add port %s (%s)",
                 p->name, netdev_name, strerror(error));
    if (ofport) {
        ofport_destroy__(ofport);
    } else {
        netdev_close(netdev);
    }
}

发送port_status 和端口改变原因到SDN Controller:

void
connmgr_send_port_status(struct connmgr *mgr,
                         const struct ofputil_phy_port *pp, uint8_t reason)
{
    /* XXX Should limit the number of queued port status change messages. */
    struct ofputil_port_status ps;
    struct ofconn *ofconn;

    ps.reason = reason;
    ps.desc = *pp;
    LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
        if (ofconn_receives_async_msg(ofconn, OAM_PORT_STATUS, reason)) {
            struct ofpbuf *msg;

            msg = ofputil_encode_port_status(&ps, ofconn->protocol);
            ofconn_send(ofconn, msg, NULL);
        }
    }
}

版权声明:本文博主原创文章。博客,未经同意不得转载。

时间: 2024-07-29 16:23:16

OVS 派OFPT_PORT_STATUS 流程的相关文章

OVS 发送OFPT_PORT_STATUS 过程

根据openflow协议,当一个物理端口从ovs datapath 增加,修改或者删除的时候,都会通过ofp_port_status异步消息告知Controller,比如当我们执行 ovs-vsctl add-port br0 eth0 之类的命令后,就会更新ovsdb数据库,而后全局变量 reconfiguring 变为true,从而会重新配置这个ovs. if (reconfiguring) { // cfg 条目可以追踪到ovsdb中某个配置发生改变 if (cfg) { if (!rec

OVS处理upcall流程分析

处理upcall总体框架: 1.由函数handle_upcalls()批量处理(in batches)的是由内核传上来的dpif_upcalls,会解析出upcall的类型.这里主要看在内核中匹配流表失败的MISS_UPCALL. 处理完毕后会得到多个flow_miss. 结构体dpif_upcall代表的是由内核传到用户空间的一个包,包含上传原因,packet data.以及以netlink attr形式存在的键值. struct dpif_upcall { /* All types. */

openVswitch(OVS)源代码分析之工作流程(收发数据包)

前面已经把分析openVswitch源代码的基础(openVswitch(OVS)源代码分析之数据结构)写得非常清楚了,虽然访问的人比较少,也因此让我看到了一个现象:第一篇,openVswitch(OVS)源代码分析之简介其实就是介绍了下有关于云计算现状和openVswitch的各个组成模块,还有笼统的介绍了下其工作流程,个人感觉对于学习openVswitch源代码来说没有多大含金量.云计算现状是根据公司发展得到的个人体会,对学习openVswitch源代码其实没什么帮助:openVswitch

Python资料& 个人日常总结 _20151220

后期有时间再排版. 后面越来越乱了.没时间整理分类 不嫌麻烦 就打开链接试试吧. 这是我之前为同学准备的,也是我从入门以来搜集的资源 适合以Python为第一个语言来学习编程的同学. 下面这个重要,可以先看看.再往下看.或者通览一遍 有没有你需要的. --> 编程入门指南 v1.4 http://zhuanlan.zhihu.com/xiao-jing-mo/19959253 ???????????? 写给 编程入门者 : __更新关于 0编程者接触Python的知乎讨论 https://www

机器码农:深度学习自动编程

转自原文机器码农:深度学习自动编程 作者简介:张俊林,中科院软件所博士,曾担任阿里巴巴.百度.新浪微博资深技术专家,目前是用友畅捷通工智能相关业务负责人,关注深度学习在自然语言处理方面的应用. 责编:何永灿,欢迎人工智能领域技术投稿.约稿.给文章纠错,请发送邮件至[email protected] 本文为<程序员>原创文章,未经允许不得转载,更多精彩文章请订阅2017年<程序员> 机器自动编程是人工智能一直以来期望攻克的重要应用领域,随着深度学习的逐步流行,最近在自动编程方向获得了

企业内部报修现状分析与解决方案

目前,通过走访调查,企业内部报修平台尚不完善,还是采取传统行业死板的呼叫网管等报修,存在很多问题. 存在如下弊端:为进一步改善报修方式,青鸟报修系统诞生,已为3000多家企业解决报修存在的问题,提高了企业报修方式,通过扫描微信二维码即可完成报修,后台有完善的统计功能与派单流程,全程高效,进度实时知晓,用过青鸟报修系统的用户比较依赖于它.今天简单跟大家介绍一下系统的主要功能. 由于时间关系就展示这么多,如有兴趣可通过青鸟报修官网进行了解,http://www.qnbaoxiu.com 原文地址:h

选型宝访谈:海底捞 如何打造全渠道+全场景的智能客服系统?

01直播简介 说起客服系统,大家可能会首先想到呼叫中心(Call Center),想到那些熟悉的客服号码:10086.95588.12306.400.800...... 曾经,电话和邮件,是客户与企业连接的主要纽带. 然而,"移动+社交"改变了这一切. APP.微信.小程序.电商网站.QQ.邮件.电话-..客户可能会通过任何一种方式,与你的企业发生连接, 客服的渠道,正在变的极度碎片化. 移动+社交时代,我们应该怎样构建新的客服系统? 在新一代智能客服系统的赛道里,UDESK 是颇受关

openVswitch(OVS)源码分析之工作流程(哈希桶结构体的解释)

这篇blog是专门解决前篇openVswitch(OVS)源码分析之工作流程(哈希桶结构体的疑惑)中提到的哈希桶结构flex_array结构体成员变量含义的问题. 引用下前篇blog中分析讨论得到的flex_array结构体成员变量的含义结论: struct { int element_size; // 这是flex_array_part结构体存放的哈希头指针的大小 int total_nr_elements; // 这是全部flex_array_part结构体中的哈希头指针的总个数 int e

openVswitch(OVS)源代码分析之工作流程(数据包处理)

上篇分析到数据包的收发,这篇开始着手分析数据包的处理问题.在openVswitch中数据包的处理是其核心技术,该技术分为三部分来实现:第一.根据skb数据包提取相关信息封装成key值:第二.根据提取到key值和skb数据包进行流表的匹配:第三.根据匹配到的流表做相应的action操作(若没匹配到则调用函数往用户空间传递数据包):其具体的代码实现在 datapath/datapath.c 中的,函数为: void ovs_dp_process_received_packet(struct vpor