美文网首页
ovs 流表version

ovs 流表version

作者: 分享放大价值 | 来源:发表于2021-07-10 16:51 被阅读0次

    插入分类器的流表有一个version字段,类型为struct versions,用来标记此流表在哪个version添加到分类器,如果流表被删除后标记在哪个version被删除。

    typedef uint64_t ovs_version_t;
    
    #define OVS_VERSION_MIN 0                  /* Default version number to use. */
    //可用版本的最大值,为ovs_version_t-1
    #define OVS_VERSION_MAX (TYPE_MAXIMUM(ovs_version_t) - 1)
    //添加流表时,remove_version被设置此值,表示此流表被添加,还没被删除
    #define OVS_VERSION_NOT_REMOVED TYPE_MAXIMUM(ovs_version_t)
    
    /*
     * OVS_VERSION_NOT_REMOVED has a special meaning for 'remove_version',
     * meaning that the rule has been added but not yet removed.
     */
    struct versions {
        ovs_version_t add_version;              /* Version object was added in. */
        ATOMIC(ovs_version_t) remove_version;   /* Version object is removed in. */
    };
    
    //插入分类器的结构,代表一个流表
    struct cls_match {
        ...
        /* Rule versioning. */
        struct versions versions;
        ...
    };
    

    结构体struct ofproto代表一个ovs网桥,其中字段tables_version用来控制哪些流表可被查询到,每次流表更新都会导致其值增加1。

    struct ofproto {
        ...
        ovs_version_t tables_version;  /* Controls which rules are visible to table lookups. */
        ...
    }
    

    struct cls_match的versions字段是在添加流表时被赋值,其中
    add_version为当前ofproto->tables_version的值,remove_version为OVS_VERSION_NOT_REMOVED。这样的话,查询流表时,只有ofproto->tables_version在add_version和remove_version范围内的流表才能被匹配。当删除此流表时,add_version值不变,但是remove_version被更新成当前ofproto->tables_version的值,由于ofproto->tables_version的值是不断增长的,所以流表被删除后,ofproto->tables_version的值肯定是大于remove_version的,所以查询流表时,相当于此流表是不可见的。

    version的更新

    handle_flow_mod__(struct ofproto *ofproto, const struct ofputil_flow_mod *fm, const struct openflow_mod_requester *req)
        //每次添加,更新或者删除流表 ofproto->tables_version都要加一
        ofm.version = ofproto->tables_version + 1;
        //添加流表时,设置 add_version 为 ofm.version,remove_version 为 OVS_VERSION_NOT_REMOVED
        ofproto_flow_mod_start(ofproto, &ofm);
            add_flow_start(ofproto, ofm);
                replace_rule_start(ofproto, ofm, old_rule, new_rule);
                    //将流表插入分类器
                    classifier_insert(&table->cls, &new_rule->cr, ofm->version, ofm->conjs, ofm->n_conjs);
                        classifier_replace(cls, rule, version, conj, n_conj);
                            //分配 cls_match,
                            struct cls_match *new;
                            new = cls_match_alloc(rule, version, conjs, n_conjs);
                                struct cls_match *cls_match = xmalloc(sizeof *cls_match + MINIFLOW_VALUES_SIZE(count));
                                //versions的 add_version 和 remove_version 都被初始化为 version
                                /* Make rule initially invisible. */
                                cls_match->versions = VERSIONS_INITIALIZER(version, version);
                            ...
                            //将流表插入分类器后,就可以将 remove_version 设置为 OVS_VERSION_NOT_REMOVED,
                            //可被 classifier_lookup 查询到
                            /* Make 'new' visible to lookups in the appropriate version. */
                            cls_match_set_remove_version(new, OVS_VERSION_NOT_REMOVED);
                                versions_set_remove_version(&rule->versions, version);
                                    atomic_store_relaxed(&versions->remove_version, version);
    
            //删除流表时,设置 remove_version 为 ofm.version,表示从ofm.version后的版本都不能查询到此流表
            delete_flow_start_strict(ofproto, ofm);
                delete_flows_start__(ofproto, ofm->version, rules);
                    struct rule *rule;
    
                    RULE_COLLECTION_FOR_EACH (rule, rules) {
                        struct oftable *table = &ofproto->tables[rule->table_id];
    
                        table->n_flows--;
                        //设置 remove_version 为 ofm.version,设置完后后面查找流表时就查不到它了
                        cls_rule_make_invisible_in_version(&rule->cr, version);
                            struct cls_match *cls_match = get_cls_match_protected(rule);
    
                            cls_match_set_remove_version(cls_match, remove_version);
                                versions_set_remove_version(&rule->versions, version);
                                    atomic_store_relaxed(&versions->remove_version, version);
                    }
    
        //ofproto->tables_version加1,并更新到 struct ofproto_dpif->tables_version
        ofproto_bump_tables_version(ofproto);
            ++ofproto->tables_version;
            ofproto->ofproto_class->set_tables_version(ofproto,
                                                       ofproto->tables_version);
                struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
    
                /* Use memory_order_release to signify that any prior memory accesses can
                 * not be reordered to happen after this atomic store.  This makes sure the
                 * new version is properly set up when the readers can read this 'version'
                 * value. */
                atomic_store_explicit(&ofproto->tables_version, version, memory_order_release);
                /* 'need_revalidate' can be reordered to happen before the atomic_store
                 * above, but it does not matter as this variable is not accessed by other
                 * threads. */
                ofproto->backer->need_revalidate = REV_FLOW_TABLE;
    

    使用version查询流表
    只有struct ofproto_dpif->tables_version

    //查询流表时,首先获取 struct ofproto_dpif->tables_version,只有此值在流表的 add_version 和 remove_version
    //之间的流表才能被匹配
    ovs_version_t
    ofproto_dpif_get_tables_version(struct ofproto_dpif *ofproto)
    {
        ovs_version_t version;
    
        /* Use memory_order_acquire to signify that any following memory accesses
         * can not be reordered to happen before this atomic read.  This makes sure
         * all following reads relate to this or a newer version, but never to an
         * older version. */
        atomic_read_explicit(&ofproto->tables_version, &version, memory_order_acquire);
        return version;
    }
    
    //查询openflow流表
    upcall_xlate
        //ofproto_dpif_get_tables_version获取 ofproto->tables_version
        xlate_in_init(&xin, upcall->ofproto,
                      ofproto_dpif_get_tables_version(upcall->ofproto),
                      upcall->flow, upcall->in_port, NULL,
                      stats.tcp_flags, upcall->packet, wc, odp_actions);
            xin->ofproto = ofproto;
            xin->tables_version = version;
            ...
    
        xlate_actions(&xin, &upcall->xout);
            struct xlate_ctx ctx = {
            .xin = xin,
            ...
            };
            //开始遍历ofproto的流表
            rule_dpif_lookup_from_table(ctx.xbridge->ofproto, ctx.xin->tables_version, flow, ctx.wc,
                                        ctx.xin->resubmit_stats, &ctx.table_id,
                                        flow->in_port.ofp_port, true, true, ctx.xin->xcache);
                struct rule_dpif *rule;
                for (next_id = *table_id;
                     next_id < ofproto->up.n_tables;
                     next_id++, next_id += (next_id == TBL_INTERNAL))
                {
                    *table_id = next_id;
                    rule = rule_dpif_lookup_in_table(ofproto, version, next_id, flow, wc);
                        classifier_lookup(cls, version, flow, wc)
                            classifier_lookup__(cls, version, flow, wc, true);
                                find_match_wc(subtable, version, flow, trie_ctx, cls->n_tries, wc);
                                    find_match(subtable, version, flow, hash);
                                        //只有可见的流表才能被匹配,
                                        //即大于add_version,并且小于remove_version的流表才能被匹配
                                        cls_match_visible_in_version(rule, version)
                                            versions_visible_in_version(&rule->versions, version);
                                                ovs_version_t remove_version;
    
                                                /* C11 does not want to access an atomic via a const object pointer. */
                                                atomic_read_relaxed(&CONST_CAST(struct versions *,versions)->remove_version,
                                                                    &remove_version);
    
                                                return versions->add_version <= version && version < remove_version;
                }
    

    这里还有个疑问,前面说到不管增加还是删除流表ofproto->tables_version是一直增长的,虽然它的类型是uint64_t,是个很大的值,但是如果进程不重启,随着时间拉长,总会溢出反转成0的,这样的话,前面添加的流表就查不到了。

    可以做个试验,创建一个网桥,两个namespace,两对veth口,分别连接到网桥和namespace,每个namespace中的veth口配置同网段ip,再添加如下两条流表,此时是可以互相ping通的。

    ovs-ofctl add-flow br10 "priority=80, in_port=2, action=1"
    ovs-ofctl add-flow br10 "priority=80, in_port=1, action=2"
    

    在添加上面流表时,通过gdb ovs-vswitchd进程,在函数handle_flow_mod__观察到ofproto->tables_version为18和19,
    此时两条流表的version分别为

    flow1: add_version = 18, remove_version = OVS_VERSION_NOT_REMOVED
    flow2: add_version = 19, remove_version = OVS_VERSION_NOT_REMOVED
    

    ofproto->tables_version的值为19,所以在查询流表时,这两条流表是可见的。

    但是如果在gdb中强制将ofproto->tables_version改为0,模拟ofproto->tables_version溢出反转的情况,发现两个namespace互相ping不同的。因为ofproto->tables_version不在流表的add_version 和remove_version 范围。查询流表时,查不到这两条流表就会drop报文。

    这种情况不太清楚ovs是怎么解决的,莫非就是期望ofproto->tables_version不会溢出反转?知道的同学留个言告知下,多谢

    相关文章

      网友评论

          本文标题:ovs 流表version

          本文链接:https://www.haomeiwen.com/subject/fvecpltx.html