ovs 流表version

插入分类器的流表有一个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 - 简书 (jianshu.com)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值