简介
分类器会将规则根据掩码的情况分成子表,每一种掩码情况一个子表。相同掩码的规则放在同一个子表中。规则的匹配是在子表的基础上进行。
数据结构
/* Classifier internal definitions, subject to change at any time. */
/* 分类器内部定义的数据结构 */
/* A set of rules that all have the same fields wildcarded. */
struct cls_subtable {
struct cmap_node cmap_node; /* Within classifier's 'subtables_map'. 连接到分类器的子表hash表中,根据掩码进行hash */
/* These fields are only used by writers. 这些域只能被写者改写*/
int max_priority; /* Max priority of any rule in subtable.所有规则中可能存在的最大的优先级 */
unsigned int max_count; /* Count of max_priority rules. 最大优先级规则的个数 */
/* Accessed by iterators. 使用迭代器遍历访问同一掩码的规则链表 */
struct rculist rules_list; /* Unordered. 无序的规则链表 */
/* Identical, but lower priority rules are not inserted to any of the
* following data structures. */
/* These fields are accessed by readers who care about wildcarding. 这个值为3,不包括最后一个段 */
const uint8_t n_indices; /* How many indices to use. 存在掩码有效段个数 */
//这里CLS_MAX_INDICES + 1是因为有四个段,最后一个段单独处理。
const struct flowmap index_maps[CLS_MAX_INDICES + 1]; /* Stage maps. 阶段索引映射 */
//前缀树的前缀长度,所有前缀有相同的前缀长度。
unsigned int trie_plen[CLS_MAX_TRIES]; /* Trie prefix length in 'mask'
* (runtime configurable). 子表前缀树的长度 */
const int ports_mask_len;
//阶段查询hash map,保存的是前三个段的hash值,可以通过hash进行快速过滤
struct ccmap indices[CLS_MAX_INDICES]; /* Staged lookup indices. 截断查询索引 */
rcu_trie_ptr ports_trie; /* NULL if none. 前缀树,基于传输层的源端口 */
/* These fields are accessed by all readers. */
/* 根据最后一个段进行hash,确定hash桶,然后进行匹配 */
struct cmap rules; /* Contains 'cls_match'es. 同一掩码的规则的匹配器hash到该表中 */
const struct minimask mask; /* Wildcards for fields. 掩码,用于比较 */
/* 'mask' must be the last field. */
};
函数
insert_subtable
该函数新建一个子表,插入到分类器中。当一个规则的掩码与现有的所有子表的掩码都不相同时,会调用新建一个子表。
/* The new subtable will be visible to the readers only after this. */
/* 在这个函数后,新的子表可以被读者看到 */
static struct cls_subtable *
insert_subtable(struct classifier *cls, const struct minimask *mask)
{
uint32_t hash = minimask_hash(mask, 0);/* 根据掩码计算hash值 */
struct cls_subtable *subtable;
int i, index = 0;
struct flowmap stage_map;
uint8_t prev;
size_t count = miniflow_n_values(&mask->masks);/* 计算掩码中1的个数 */
subtable = xzalloc(sizeof *subtable + MINIFLOW_VALUES_SIZE(count));/* 分配子表 */
cmap_init(&subtable->rules);/* 初始化规则hash表 */
miniflow_clone(CONST_CAST(struct miniflow *, &subtable->mask.masks),
&mask->masks, count);/* 将掩码拷贝到子表中 */
/* Init indices for segmented lookup, if any. */
/* 初始化段查询索引 */
prev = 0;
for (i = 0; i < cls->n_flow_segments; i++) {/* 遍历每一个段,所有的子表都跟分类器拥有一样的段匹配器 */
/* 构建段映射map表,prev为段起始地址,cls->flow_segments[i]为段的结束地址 */
stage_map = miniflow_get_map_in_range(&mask->masks, prev,
cls->flow_segments[i]);
/* Add an index if it adds mask bits. */
/* 如果非空,即存在有效值域 */
if (!flowmap_is_empty(stage_map)) {/* 如果该子表的掩码在这个段不为空 */
ccmap_init(&subtable->indices[index]);/* 初始化该段匹配hash表 */
*CONST_CAST(struct flowmap *, &subtable->index_maps[index])/* 将该段的掩码赋值给段掩码数组 */
= stage_map;
index++;/* 统计存在掩码段的个数 */
}
prev = cls->flow_segments[i];/* 获取下一个段的边界 */
}
/* Map for the final stage. */
*CONST_CAST(struct flowmap *, &subtable->index_maps[index])
= miniflow_get_map_in_range(&mask->masks, prev, FLOW_U64S);/* 进行最后一个段的处理 */
/* Check if the final stage adds any bits. */
/* 检查最后一个段是否为空 */
if (index > 0) {
if (flowmap_is_empty(subtable->index_maps[index])) {/* 最后一个段没有掩码,则统计减掉1 */
/* Remove the last index, as it has the same fields as the rules
* map. */
--index;
ccmap_destroy(&subtable->indices[index]);
}
}
*CONST_CAST(uint8_t *, &subtable->n_indices) = index;/* 存在有效掩码段的个数 */
//计算前缀树的前缀长度
for (i = 0; i < cls->n_tries; i++) {/* 遍历该分类器支持的前缀树,初始化该子表相应的前缀树掩码的长度 */
subtable->trie_plen[i] = minimask_get_prefix_len(mask,
cls->tries[i].field);/* 获取前缀长度 */
}
/* Ports trie. 端口前缀树 */
ovsrcu_set_hidden(&subtable->ports_trie, NULL);
*CONST_CAST(int *, &subtable->ports_mask_len)/* 得到该位置1的个数 */
= 32 - ctz32(ntohl(MINIFLOW_GET_BE32(&mask->masks, tp_src)));
/* List of rules. 初始化子表规则链表 */
rculist_init(&subtable->rules_list);
/* 将子表插入到分类器的子表hash表中 */
cmap_insert(&cls->subtables_map, &subtable->cmap_node, hash);
return subtable;
}
find_match
/* 查找匹配域 */
static inline const struct cls_match *
find_match(const struct cls_subtable *subtable, ovs_version_t version,
const struct flow *flow, uint32_t hash)
{
const struct cls_match *head, *rule;
CMAP_FOR_EACH_WITH_HASH (head, cmap_node, hash, &subtable->rules) {/* 遍历规则hash表 */
if (OVS_LIKELY(miniflow_and_mask_matches_flow(&head->flow,/* 匹配域 */
&subtable->mask,/* 子表掩码 */
flow))) {/* 进行掩码匹配 */
/* Return highest priority rule that is visible. */
/* 返回本设备支持的最高优先级的规则 */
CLS_MATCH_FOR_EACH (rule, head) {/* 遍历hash桶链表的每一个规则 */
if (OVS_LIKELY(cls_match_visible_in_version(rule, version))) {
return rule;
}
}
}
}
return NULL;
}
find_match_wc
/* 进行通配符的查找 */
static const struct cls_match *
find_match_wc(const struct cls_subtable *subtable, ovs_version_t version,
const struct flow *flow, struct trie_ctx trie_ctx[CLS_MAX_TRIES],
unsigned int n_tries, struct flow_wildcards *wc)
{
if (OVS_UNLIKELY(!wc)) {/* 对于不需要生成缓存的匹配器,wc为null,比如underlay路由表 */
return find_match(subtable, version, flow,
flow_hash_in_minimask(flow, &subtable->mask, 0));
}
uint32_t basis = 0, hash;
const struct cls_match *rule = NULL;
struct flowmap stages_map = FLOWMAP_EMPTY_INITIALIZER;
unsigned int mask_offset = 0;
int i;
/* Try to finish early by checking fields in segments. */
/* 在段里面尝试完成早期的校验 */
for (i = 0; i < subtable->n_indices; i++) {/* 进行segments查找 */
if (check_tries(trie_ctx, n_tries, subtable->trie_plen/* 前缀长度,匹配,查看是否可以跳过这个子表 */,
subtable->index_maps[i], flow, wc)) {
/* 'wc' bits for the trie field set, now unwildcard the preceding
* bits used so far. */
goto no_match;
}
/* Accumulate the map used so far. */
/* 累积到目前为止的掩码 */
stages_map = flowmap_or(stages_map, subtable->index_maps[i]);
/* 计算当前的hash值 */
hash = flow_hash_in_minimask_range(flow, &subtable->mask,
subtable->index_maps[i],
&mask_offset, &basis);
/* 段匹配,没有命中,跳出 */
if (!ccmap_find(&subtable->indices[i], hash)) {
goto no_match;
}
}
/* Trie check for the final range. */
/* 为最后的范围做树校验 */
if (check_tries(trie_ctx, n_tries, subtable->trie_plen,
subtable->index_maps[i], flow, wc)) {
goto no_match;
}
/* 对指定range进行hash */
hash = flow_hash_in_minimask_range(flow, &subtable->mask,
subtable->index_maps[i],
&mask_offset, &basis);
/* 进行子表匹配,找到最高优先级的 */
rule = find_match(subtable, version, flow, hash);
if (!rule && subtable->ports_mask_len) {/* 没有找到,并且有传输层src ports引擎树 */
/* The final stage had ports, but there was no match. Instead of
* unwildcarding all the ports bits, use the ports trie to figure out a
* smaller set of bits to unwildcard.
* 最后阶段存在端口,但是这儿没有匹配,代替非通配所有的端口,使用ports tree去计算最小的通配端口集合 */
unsigned int mbits;
ovs_be32 value, plens, mask;
/* 获取四层端口的掩码 */
mask = MINIFLOW_GET_BE32(&subtable->mask.masks, tp_src);/* 获取四层源端口掩码 */
/* 两者相与 */
value = ((OVS_FORCE ovs_be32 *)flow)[TP_PORTS_OFS32] & mask;/* 与流进行掩码与操作,得到我们需要查找的值 */
/* 对端口树进行查找 */
mbits = trie_lookup_value(&subtable->ports_trie, &value, &plens, 32);
((OVS_FORCE ovs_be32 *)&wc->masks)[TP_PORTS_OFS32] |=
mask & be32_prefix_mask(mbits);
goto no_match;
}
/* Must unwildcard all the fields, as they were looked at. */
/* 必须非通配所有的域,因为他们需要被查找匹配,这里采用的是或操作
** 查询多个表格后会命中多个规则,mask进行或
*/
flow_wildcards_fold_minimask(wc, &subtable->mask);
//返回掩码
return rule;
no_match:
/* Unwildcard the bits in stages so far, as they were used in determining
* there is no match. */
flow_wildcards_fold_minimask_in_map(wc, &subtable->mask, stages_map);
return NULL;
}