OVS分类器(二)---规则

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);
}

你可能感兴趣的:(c,c++,ubuntu)