插入分类器的流表有一个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不会溢出反转?知道的同学留个言告知下,多谢
网友评论