ovs分类器中,规则从复杂度上可以分为简单规则和关联匹配规则。简单规则独立存在,关联规则,同时存在。关联规则需要同时命中才能最终命中。关联规则对性能影响很大。
简单规则
在分类器中,规则使用struct cls_rule结构进行组织。一个规则有一个掩码域和一个值域。
struct cls_rule
/* A rule to be inserted to the classifier. */
/* 一个规则被插入分类器 */
struct cls_rule {
struct rculist node; /* In struct cls_subtable 'rules_list'. 双链表,连接到对应的子表中 */
const int priority; /* Larger numbers are higher priorities.值越大其优先级越高 */
OVSRCU_TYPE(struct cls_match *) cls_match; /* NULL if not in a
* classifier. */
const struct minimatch match; /* Matching rule. 匹配器,由两个miniflow构成,一个位掩码一个值 */
};
/* Internal representation of a rule in a "struct cls_subtable".
*
* The 'next' member is an element in a singly linked, null-terminated list.
* This list links together identical "cls_match"es in order of decreasing
* priority. The classifier code maintains the invariant that at most one rule
* of a given priority is visible for any given lookup version.
* 子表中的规则表示。next成员构成单链表,该链表表示相同的匹配域的不同规则。按照优先级
* 高低顺序进行排列。
*/
struct cls_match {
/* Accessed by everybody. */
OVSRCU_TYPE(struct cls_match *) next; /* Equal, lower-priority matches. 相同的匹配条件,根据优先级进行排序 */
OVSRCU_TYPE(struct cls_conjunction_set *) conj_set;
/* Accessed by readers interested in wildcarding. */
/* 有先级,值越大优先级越高 */
const int priority; /* Larger numbers are higher priorities. */
/* Accessed by all readers. */
struct cmap_node cmap_node; /* Within struct cls_subtable 'rules'. h挂到子表的rules链表中 */
/* Rule versioning. */
/* 规则版本号 */
struct versions versions;
const struct cls_rule *cls_rule;/* 指向其所属的规则 */
const struct miniflow flow; /* Matching rule. Mask is in the subtable. 匹配域,其掩码在subtable中 */
/* 'flow' must be the last field. */
};
关联匹配规则
关联匹配即多个规则同时命中后才算命中规则,即叫关联匹配。命中关联匹配中的一个规则叫soft-match,关联匹配所有规则都soft-match后,那么整体规则才算命中,由soft-match转换成hard-match。
例如:
关联匹配即多个规则同时命中后才算命中规则,即叫关联匹配。命中关联匹配中的一个规则叫soft-match,关联匹配所有规则都soft-match后,那么整体规则才算命中,由soft-match转换成hard-match。
例如:
conj_id=1234 actions=controller
ip,ip_src=10.0.0.1 actions=conjunction(1234, 1/2)
ip,ip_src=10.0.0.4 actions=conjunction(1234, 1/2)
ip,ip_src=10.0.0.6 actions=conjunction(1234, 1/2)
ip,ip_src=10.0.0.7 actions=conjunction(1234, 1/2)
ip,ip_dst=10.0.0.2 actions=conjunction(1234, 2/2)
ip,ip_dst=10.0.0.5 actions=conjunction(1234, 2/2)
ip,ip_dst=10.0.0.7 actions=conjunction(1234, 2/2)
ip,ip_dst=10.0.0.8 actions=conjunction(1234, 2/2)
上面规则即定义了一个关联规则,命中关联规则后,将报文发送给控制器:
conj_id=1234 actions=controller
关联规则有两个维度,分别是1和2。维度1中规则有:
ip,ip_src=10.0.0.1 actions=conjunction(1234, 1/2)
ip,ip_src=10.0.0.4 actions=conjunction(1234, 1/2)
ip,ip_src=10.0.0.6 actions=conjunction(1234, 1/2)
ip,ip_src=10.0.0.7 actions=conjunction(1234, 1/2)
维度2中的规则有:
ip,ip_dst=10.0.0.2 actions=conjunction(1234, 2/2)
ip,ip_dst=10.0.0.5 actions=conjunction(1234, 2/2)
ip,ip_dst=10.0.0.7 actions=conjunction(1234, 2/2)
ip,ip_dst=10.0.0.8 actions=conjunction(1234, 2/2)
维度中规则的关系是或的关系,只要命中其中一个规则,则认为该维度命中;维度与维度之间为与的关系,所有维度都命中后,关联规则才算命中,即hard-match。
数据结构
/* A collection of "struct cls_conjunction"s currently embedded into a
* cls_match.一个cls_conjunction集合用于嵌入到cls_match中的
* 一个关联规则,可以关联多个关联id
*/
struct cls_conjunction_set {
/* Link back to the cls_match.
*
* cls_conjunction_set is mostly used during classifier lookup, and, in
* turn, during classifier lookup the most used member of
* cls_conjunction_set is the rule's priority, so we cache it here for fast
* access. 反向指向其所属的匹配器 */
struct cls_match *match;
int priority; /* Cached copy of match->priority.缓存其所属的匹配器的优先级 */
/* Conjunction information.
* 连接点信息
* 'min_n_clauses' allows some optimization during classifier lookup. */
unsigned int n; /* Number of elements in 'conj'. 关联规则个数*/
unsigned int min_n_clauses; /* Smallest 'n' among elements of 'conj'.最小的n在关联集合数组中 */
struct cls_conjunction conj[];/* 关联的规则数组 */
};
struct cls_conjunction {
uint32_t id;/* 关联动作id */
uint8_t clause;/* 流的bit位置,即关联的维度,从0开始 */
uint8_t n_clauses;/* 关联的流的个数,多少个维度 */
};
//关联id结构,一个关联id,clauses个维度
//该结构是一个内部结构,用于关联匹配时进行暂存,等待所有维度都命中后,转换为hard-match。
struct conjunctive_match {
struct hmap_node hmap_node;/* 用于连接到hash表中,一般用于暂存表matches中 */
uint32_t id;/* 关联id */
uint64_t clauses;/* 对应的流表位掩码,64个bit,也就是说ovs支持64个维度 */
};
相关函数
/* 遍历hash桶,在暂存hash表中找到关联id相同的项,没找到返回NULL */
static struct conjunctive_match *
find_conjunctive_match__(struct hmap *matches, uint64_t id, uint32_t hash)
{
struct conjunctive_match *m;
HMAP_FOR_EACH_IN_BUCKET (m, hmap_node, hash, matches) {/* 在一个hash表中进行匹配 */
if (m->id == id) {/* 遍历hash桶的每一个元素,比较id是否相等 */
return m;
}
}
return NULL;
}
/* 查找关联匹配 */
static bool
find_conjunctive_match(const struct cls_conjunction_set *set, /* 本规则对应的关联id集合 */
unsigned int max_n_clauses, /* 本table所命中该set对应的规则优先的规则个数 */
struct hmap *matches, /* 关联id暂存hash表*/
/* conjunctive_match局部数组,避免动态分配内存 */
struct conjunctive_match *cm_stubs, size_t n_cm_stubs,
uint32_t *idp)//返回命中的关联id
{
const struct cls_conjunction *c;
if (max_n_clauses < set->min_n_clauses) {/* 命中对应优先级的规则个数小于该规则所有的关联id的维度 */
return false;/* 直接返回,不可能命中 */
}
/* 遍历该规则对应的每一个关联id */
for (c = set->conj; c < &set->conj[set->n]; c++) {/* 遍历每一个关联匹配 */
struct conjunctive_match *cm;/* 关联匹配 */
uint32_t hash;
/* 关联规则的维度大于对应优先级命中的规则,那么不可能命中,直接跳过 */
if (c->n_clauses > max_n_clauses) {
continue;
}
hash = hash_int(c->id, 0);/* 对id进行hash,然后用来根据关联id查找关联匹配 */
/* matches为暂存表,即同一个关联id的第一个处理的c会添加到该hash表中 */
cm = find_conjunctive_match__(matches, c->id, hash);
if (!cm) {/* 没有找到,说明需要新增 */
/* 计算已经暂存的关联id的个数,如果大于我们的局部数组,那么需要动态分配内存用于暂存hash表 */
size_t n = hmap_count(matches);
cm = n < n_cm_stubs ? &cm_stubs[n] : xmalloc(sizeof *cm);
hmap_insert(matches, &cm->hmap_node, hash);/* 将新建的cm插入到hash表中 */
cm->id = c->id;/* 记录id */
/* 有几个维度就将0xffffffffffffffff向左移动多少位,即将最右边几个bit清零,然后在命中的时候,
** 命中一个维度就将该bit置1.一旦cm->clauses == 0xffffffffffffffff 时表示该关联匹配命中了。
*/
cm->clauses = UINT64_MAX << (c->n_clauses & 63);
}
/* 设置本关联匹配维度对应的bit位 */
cm->clauses |= UINT64_C(1) << c->clause;
if (cm->clauses == UINT64_MAX) {/* 一旦等于全ff,则说明关联匹配全命中了 */
*idp = cm->id;/* 返回关联匹配id,说明同一个优先级不能设置两个关联id */
return true;
}
}
return false;
}
/* 释放关联匹配的内存,即释放掉暂存表matches,分为两部分,一部分是cm_stubs中的内容不用在这里释放
** 需要释放的是动态申请的内存
*/
static void
free_conjunctive_matches(struct hmap *matches,
struct conjunctive_match *cm_stubs, size_t n_cm_stubs)
{
if (hmap_count(matches) > n_cm_stubs) {/* 只有大于我们的局部数组的时候,才需要进行内存的释放 */
struct conjunctive_match *cm, *next;
HMAP_FOR_EACH_SAFE (cm, next, hmap_node, matches) {
/* 不在数组内的内存是动态申请的,进行释放 */
if (!(cm >= cm_stubs && cm < &cm_stubs[n_cm_stubs])) {
free(cm);
}
}
}
hmap_destroy(matches);
}