早期的VPP本身的node框架比较固定,各个node之间逻辑连接已经固化。为此新版本增加了feature机制,每个feature是一个node,
用户可以启用/停止某个或某些feature。用户也可以自己写插件,把自定义node(自己的业务逻辑)加入到指定位置。
feature机制在node机制的基础上进行构建,通过改变下一个node的选择实现动态变更业务拓扑,提高了灵活性。VPP的一个arc中的所有feature可以看成一个新的大的抽象的node。该arc中的node按照排好的序号运行,不按照node的指向运行。
feature机制注册
feature arc注册对象
/** feature registration object */
typedef struct _vnet_feature_arc_registration
{
/** next registration in list of all registrations 该arc下面的所有的feature链表 */
struct _vnet_feature_arc_registration *next;
/** Feature Arc name */
char *arc_name;/* arc的名字,arc可以仅仅是一个名字,不需要对应的node */
/** Start nodes 该arc的起始节点以及个数,从该节点会进入arc处理 */
char **start_nodes;
int n_start_nodes;
/** End of the arc (optional, for consistency-checking)该arc中的最后一个feature的名字,可选的,
** 用于校验warshall算法排序后最后一个feature是否为指定的feature */
char *last_in_arc;
/* Feature arc index, assigned by init function 初始化函数分配的arc索引 */
u8 feature_arc_index;
u8 *arc_index_ptr; /* 初始化函数分配的arc索引*arc_index_ptr == feature_arc_index。两者用的地方不一样,用于解耦合 */
} vnet_feature_arc_registration_t;
feature注册对象
/* Enable feature callback. 使能特性的回调函数,不同feature不一样 */
typedef clib_error_t *(vnet_feature_enable_disable_function_t)
(u32 sw_if_index, int enable_disable);
/** feature registration object */
typedef struct _vnet_feature_registration
{
/** next registration in list of all registrations 两个指针,next用于把所有feature链接到一个链表。
next_in_arc动态使用,将feature按照所属的arc进行分类 */
struct _vnet_feature_registration *next, *next_in_arc;
/** Feature arc name 所属arc的名字 */
char *arc_name;
/** Graph node name 该feature对应的node的名字,node必须存在 */
char *node_name;
/** Pointer to this feature index, filled in by vnet_feature_arc_init */
u32 *feature_index_ptr;/* 初始化函数分配的feature索引*feature_index_ptr == feature_index */
u32 feature_index;/* 两者用的地方不一样,主要用于解耦合 */
/** Constraints of the form "this feature runs before X" */
char **runs_before;/* 限制条件,表示在哪些feature之前运行,用于指导最后生成有序feature列表 */
/** Constraints of the form "this feature runs after Y" */
char **runs_after;/* 限制条件,表示在哪些feature之后运行,用于指导最后生成有序feature列表 */
/** Function to enable/disable feature 使能特性的回调函数,
不同feature不一样,主要用于特性的准备和清理工作。在特性使能和禁止时被调用
**/
vnet_feature_enable_disable_function_t *enable_disable_cb;
} vnet_feature_registration_t;
arc中feature限制条件注册对象
/** constraint registration object,灵活的限制条件用于限制feature的执行顺序,与前面的runs_before和runs_after联合起来共同生效 */
typedef struct _vnet_feature_constraint_registration
{
/** next constraint set in list of all registrations*/
/** 形成链表next在注册时初始化,所有同类链接到一个链表。
** next_in_arc动态使用,同一个arc的对象链接到一个链表
*/
struct _vnet_feature_constraint_registration *next, *next_in_arc;
/** Feature arc name 所属arc的名字 */
char *arc_name;
/** Feature arc index, assigned by init function
** arc索引,从arc中获取的,用于指向其所属的arc
*/
u8 feature_arc_index;
/** Node names, to run in the specified order
** 节点名字数组,是一个二维数组,数组下标越小优先级越高。
** 例如有数组node_names[2][2],那么顺序为node_names[0][0]->node_names[0][1]->node_names[1][0]->node_names[1][1]
** 可以实现"a then b then c then d"更长的顺序限制功能
*/
char **node_names;
} vnet_feature_constraint_registration_t;
feature机制注册管理全局对象
feature机制都是使用带有__constructor__属性的宏进行注册的,所以需要一个全局变量来维护这些信息。这个全局变量就是:
extern vnet_feature_main_t feature_main;
typedef struct
{
/** feature arc configuration list 所有注册的arc连接成链表 */
vnet_feature_arc_registration_t *next_arc;
/* 所有注册的arc根据名字构建成hash表,用于根据名字查找对应的vnet_feature_arc_registration_t */
uword **arc_index_by_name;
/** feature path configuration lists */
vnet_feature_registration_t *next_feature;
/** 链表数组,每一个arc一个链表,该链表包含了该arc所有的feature,在函数vnet_feature_init动态形成 */
vnet_feature_registration_t **next_feature_by_arc;
vnet_feature_constraint_registration_t *next_constraint;
vnet_feature_constraint_registration_t **next_constraint_by_arc;
/* hash表数组,以arc所有作为下标,每一个成员是一个hash表,构建该arc中的特性名字与特性之间的映射 */
uword **next_feature_by_name;
/** feature config main objects */
/* 根据arc_index获取对应的arc的配置项,一个arc一个元素 */
vnet_feature_config_main_t *feature_config_mains;
/** Save partial order results for show command */
/** 以arc索引为下标,每一个成员是一个特性数组,数组包含了该arc的有序特性 */
char ***feature_nodes;
/** bitmap of interfaces which have driver rx features configured */
/** 位域数组,二维数组,每一个arc作为一维索引,接口作为二维索引 */
uword **sw_if_index_has_features;
/** feature reference counts by interface */
/** i16数组,表示接口上对应的arc上配置的特性的数目,是一个二维数组:第一维度是接口索引,第二维度是arc */
i16 **feature_count_by_sw_if_index;
/** Feature arc index for device-input */
/** device input arc分配的索引 */
u8 device_input_feature_arc_index;
/** convenience */
vlib_main_t *vlib_main;
vnet_main_t *vnet_main;
} vnet_feature_main_t;
feature机制配置管理结构
typedef struct
{
/* Pool of configs. Index 0 is always null config and is never deleted. */
vnet_config_t *config_pool;
/* Hash table mapping vector config string to config pool index. */
uword *config_string_hash;
/* Global heap of configuration data. */
u32 *config_string_heap;
/* Node index which starts/ends feature processing. */
/* 该arc的起始节点数组和结束节点的node索引 */
u32 *start_node_indices, end_node_index;
/* Interior feature processing nodes (not including start and end nodes). */
/* 根据feature索引找到node索引的映射表 */
u32 *node_index_by_feature_index;
/* vnet_config pool index by user index */
u32 *config_pool_index_by_user_index;
/* Temporary vector for holding config strings. Used to avoid continually
allocating vectors. */
u32 *config_string_temp;
} vnet_config_main_t;
typedef struct vnet_feature_config_main_t_
{
vnet_config_main_t config_main; /* arc的配置结构,主要与配置相关,后面分析配置章节会深入分析 */
u32 *config_index_by_sw_if_index;/* 特性的接口配置索引,根据接口索引获取该接口上配置的feature配置索引 */
} vnet_feature_config_main_t;
feature机制注册宏定义
#ifndef CLIB_MARCH_VARIANT
#define VNET_FEATURE_ARC_INIT(x,...) \ /* 注册arc对象宏 */
__VA_ARGS__ vnet_feature_arc_registration_t vnet_feat_arc_##x;\ /* 声明一个vnet_feature_arc_registration_t对象 */
static void __vnet_add_feature_arc_registration_##x (void) \ /* 声明一个__vnet_add_feature_arc_registration_函数 */
__attribute__((__constructor__)) ; \
static void __vnet_add_feature_arc_registration_##x (void) \ /* 定义__vnet_add_feature_arc_registration_函数 */
{ \
vnet_feature_main_t * fm = &feature_main; \
vnet_feat_arc_##x.next = fm->next_arc; \ /* 将静态变量vnet_feat_arc_##x连接fm->next_arc到链表中 */
fm->next_arc = & vnet_feat_arc_##x; \
} \
static void __vnet_rm_feature_arc_registration_##x (void) \
__attribute__((__destructor__)) ; \
static void __vnet_rm_feature_arc_registration_##x (void) \
{ \
vnet_feature_main_t * fm = &feature_main; \
vnet_feature_arc_registration_t *r = &vnet_feat_arc_##x; \
VLIB_REMOVE_FROM_LINKED_LIST (fm->next_arc, r, next); \
} \
__VA_ARGS__ vnet_feature_arc_registration_t vnet_feat_arc_##x /* 定义初始化vnet_feat_arc_##x变量 */
#define VNET_FEATURE_INIT(x,...) \ /* 注册feature对象宏 */
__VA_ARGS__ vnet_feature_registration_t vnet_feat_##x; \ /* 声明一个vnet_feature_registration_t对象 */
static void __vnet_add_feature_registration_##x (void) \ /* 声明一个__vnet_add_feature_registration_##x_函数 */
__attribute__((__constructor__)) ; \
static void __vnet_add_feature_registration_##x (void) \ /* 定义__vnet_add_feature_registration_##x函数 */
{ \
vnet_feature_main_t * fm = &feature_main; \
vnet_feat_##x.next = fm->next_feature; \ /* 将静态变量vnet_feat_##x连接fm->next_feature到链表中 */
fm->next_feature = & vnet_feat_##x; \
} \
static void __vnet_rm_feature_registration_##x (void) \
__attribute__((__destructor__)) ; \
static void __vnet_rm_feature_registration_##x (void) \
{ \
vnet_feature_main_t * fm = &feature_main; \
vnet_feature_registration_t *r = &vnet_feat_##x; \
VLIB_REMOVE_FROM_LINKED_LIST (fm->next_feature, r, next); \
} \
__VA_ARGS__ vnet_feature_registration_t vnet_feat_##x /* 定义初始化vnet_feat_##x变量 */
#define VNET_FEATURE_ARC_ORDER(x,...) \ /* 该宏用的非常少,目前vpp还没有使用过该宏 */
__VA_ARGS__ vnet_feature_constraint_registration_t \
vnet_feature_constraint_##x; \
static void __vnet_add_constraint_registration_##x (void) \
__attribute__((__constructor__)) ; \
static void __vnet_add_constraint_registration_##x (void) \
{ \
vnet_feature_main_t * fm = &feature_main; \
vnet_feature_constraint_##x.next = fm->next_constraint; \ /* 将静态变量vnet_feature_constraint_##x连接fm->next_constraint到链表中 */
fm->next_constraint = & vnet_feature_constraint_##x; \
} \
static void __vnet_rm_constraint_registration_##x (void) \
__attribute__((__destructor__)) ; \
static void __vnet_rm_constraint_registration_##x (void) \
{ \
vnet_feature_main_t * fm = &feature_main; \
vnet_feature_constraint_registration_t *r = &vnet_feature_constraint_##x; \
VLIB_REMOVE_FROM_LINKED_LIST (fm->next_constraint, r, next); \
} \
__VA_ARGS__ vnet_feature_constraint_registration_t vnet_feature_constraint_##x /* 定义初始化vnet_feature_constraint_##x变量 */
#else
#define VNET_FEATURE_ARC_INIT(x,...) \
extern vnet_feature_arc_registration_t __clib_unused vnet_feat_arc_##x; \
static vnet_feature_arc_registration_t __clib_unused __clib_unused_vnet_feat_arc_##x
#define VNET_FEATURE_INIT(x,...) \
extern vnet_feature_registration_t __clib_unused vnet_feat_##x; \
static vnet_feature_registration_t __clib_unused __clib_unused_vnet_feat_##x
#define VNET_FEATURE_ARC_ORDER(x,...) \
extern vnet_feature_constraint_registration_t \
__clib_unused vnet_feature_constraint_##x; \
static vnet_feature_constraint_registration_t __clib_unused \
__clib_unused_vnet_feature_constraint_##x
#endif
feature机制注册实例
我们以snat为例看一下宏定义的使用
/* Hook up input features */
VNET_FEATURE_INIT (ip4_snat_in2out, static) = {
.arc_name = "ip4-unicast",
.node_name = "nat44-in2out", /* 必须存在nat44-in2out节点 */
.runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
};
VNET_FEATURE_ARC_INIT (ip4_unicast, static) =
{
.arc_name = "ip4-unicast",
.start_nodes = VNET_FEATURES ("ip4-input", "ip4-input-no-checksum"),/* 起始节点,必须存在node */
.last_in_arc = "ip4-lookup", /* 整个arc的最后一个feature必须是ip4-lookup,否则不能初始化成功 */
.arc_index_ptr = &ip4_main.lookup_main.ucast_feature_arc_index,/* 需要将arc索引写入到ucast_feature_arc_index中,供对应模块使用 */
};
feature机制初始化
依靠__constructor__属性注册了相应的feature对象后,在main函数中需要对这些对象进行加工处理,对应的初始化函数为vnet_feature_init:
static clib_error_t *
vnet_feature_init (vlib_main_t * vm)
{
vnet_feature_main_t *fm = &feature_main;/* 获取feature的全局注册信息 */
vnet_feature_registration_t *freg;
vnet_feature_arc_registration_t *areg;
vnet_feature_constraint_registration_t *creg;
u32 arc_index = 0;/* arc索引从0开始 */
/* 创建一个字符串hash表,用于构建arc名字与vnet_feature_arc_registration_t之间的映射关系 */
fm->arc_index_by_name = hash_create_string (0, sizeof (uword));
areg = fm->next_arc;
/* process feature arc registrations */
/* 处理每一个注册了的arc */
while (areg)
{
char *s;
int i = 0;
areg->feature_arc_index = arc_index;/* 动态分配arc索引 */
if (areg->arc_index_ptr)
*areg->arc_index_ptr = arc_index;/* 设置该arc的索引 */
/* 构建arc名字与arc的描述结构体指针的映射关系 */
hash_set_mem (fm->arc_index_by_name, areg->arc_name,
pointer_to_uword (areg));
/* process start nodes,计算起始节点的个数 */
while ((s = areg->start_nodes[i]))
{
i++;
}
areg->n_start_nodes = i;/* 起始节点的个数 */
/* next */
areg = areg->next;
arc_index++;
}
/* 确保每一个向量都能够容纳arc_index - 1个元素 */
vec_validate (fm->next_feature_by_arc, arc_index - 1);
vec_validate (fm->feature_nodes, arc_index - 1);
vec_validate (fm->feature_config_mains, arc_index - 1);
vec_validate (fm->next_feature_by_name, arc_index - 1);
vec_validate (fm->sw_if_index_has_features, arc_index - 1);
vec_validate (fm->feature_count_by_sw_if_index, arc_index - 1);
vec_validate (fm->next_constraint_by_arc, arc_index - 1);
/* 遍历每一个特性 */
freg = fm->next_feature;/* 遍历每一个特性,构建next_feature_by_arc链表数组 */
while (freg)
{
vnet_feature_registration_t *next;
/* 获取特性对应的arc */
uword *p = hash_get_mem (fm->arc_index_by_name, freg->arc_name);
if (p == 0)
{
/* Don't start vpp with broken features arcs */
clib_warning ("Unknown feature arc '%s'", freg->arc_name);
os_exit (1);
}
areg = uword_to_pointer (p[0], vnet_feature_arc_registration_t *);
arc_index = areg->feature_arc_index;
next = freg->next;
/* 根据feature所属的arc构建链表,即将同一个arc的feature连接到一个链表中
* 链表头为fm->next_feature_by_arc[arc_index]
*/
freg->next_in_arc = fm->next_feature_by_arc[arc_index];
fm->next_feature_by_arc[arc_index] = freg;
/* next */
freg = next;
}
/* Move bulk constraints to the constraint by arc lists */
/* 构建constraint链表,链表头为fm->next_constraint_by_arc[arc_index] */
creg = fm->next_constraint;
while (creg)
{
vnet_feature_constraint_registration_t *next;
uword *p = hash_get_mem (fm->arc_index_by_name, creg->arc_name);
if (p == 0)
{
/* Don't start vpp with broken features arcs */
clib_warning ("Unknown feature arc '%s'", creg->arc_name);
os_exit (1);
}
areg = uword_to_pointer (p[0], vnet_feature_arc_registration_t *);
arc_index = areg->feature_arc_index;
next = creg->next;
creg->next_in_arc = fm->next_constraint_by_arc[arc_index];
fm->next_constraint_by_arc[arc_index] = creg;
/* next */
creg = next;
}
/* 再次遍历每一个arc,调用vnet_feature_arc_init对该arc中的feature进行初始化 */
areg = fm->next_arc;
while (areg)
{
clib_error_t *error;
vnet_feature_config_main_t *cm;
vnet_config_main_t *vcm;
char **features_in_order, *last_feature;
arc_index = areg->feature_arc_index;
cm = &fm->feature_config_mains[arc_index];
vcm = &cm->config_main;/* 主要配置 */
/* 初始化主要配置,主要是根据该arc中的feature的限制条件进行feature排序,校验,构建顺序 */
if ((error = vnet_feature_arc_init
(vm, vcm, areg->start_nodes, areg->n_start_nodes,
fm->next_feature_by_arc[arc_index],/* 该节点的所有feature */
fm->next_constraint_by_arc[arc_index],/* 该节点的所有constraint */
&fm->feature_nodes[arc_index])))/* arc的节点数组 */
{
clib_error_report (error);
os_exit (1);
}
/* 该arc已经排好序的feature数组 */
features_in_order = fm->feature_nodes[arc_index];
/* If specified, verify that the last node in the arc is actually last */
/* 如果指定了需要校验的排序,那么进行校验 */
if (areg->last_in_arc && vec_len (features_in_order) > 0)
{
last_feature = features_in_order[vec_len (features_in_order) - 1];
/* 比较最后一个特性的名字是否是我们指定的 */
if (strncmp (areg->last_in_arc, last_feature,
strlen (areg->last_in_arc)))
clib_warning
("WARNING: %s arc: last node is %s, but expected %s!",
areg->arc_name, last_feature, areg->last_in_arc);
}
/* 为该arc创建一个特性名字hash表,构建特性名字与特性之间的映射关系 */
fm->next_feature_by_name[arc_index] =
hash_create_string (0, sizeof (uword));
freg = fm->next_feature_by_arc[arc_index];
while (freg)
{
hash_set_mem (fm->next_feature_by_name[arc_index],
freg->node_name, pointer_to_uword (freg));
freg = freg->next_in_arc;
}
/* next */
areg = areg->next;
arc_index++;
}
return 0;
}
单个arc初始化
使用函数vnet_feature_arc_init初始化一个arc中的feature
/**
* @brief Initialize a feature graph arc
* @param vm vlib main structure pointer
* @param vcm vnet config main structure pointer
* @param feature_start_nodes names of start-nodes which use this
* feature graph arc
* @param num_feature_start_nodes number of start-nodes
* @param first_reg first element in
* [an __attribute__((constructor)) function built, or
* otherwise created] singly-linked list of feature registrations
* @param first_const first element in
* [an __attribute__((constructor)) function built, or
* otherwise created] singly-linked list of bulk order constraints
* @param [out] in_feature_nodes returned vector of
* topologically-sorted feature node names, for use in
* show commands
* @returns 0 on success, otherwise an error message. Errors
* are fatal since they invariably involve mistyped node-names, or
* genuinely missing node-names
*/
clib_error_t *
vnet_feature_arc_init (vlib_main_t * vm,
vnet_config_main_t * vcm,
char **feature_start_nodes, /* 该arc的起始节点数组 */
int num_feature_start_nodes, /* 起始节点数目,所谓起始节点即表示报文进入该节点后需要进行feature机制运行 */
vnet_feature_registration_t * first_reg, /* 该arc的特性链表头 */
vnet_feature_constraint_registration_t * /* 该arc的限制链表头 */
first_const_set, char ***in_feature_nodes) /* 有序特性名字存储数组 */
{
uword *index_by_name;
uword *reg_by_index;
u8 **node_names = 0;
u8 *node_name;
char *prev_name;
char **these_constraints;
char *this_constraint_c;
u8 **constraints = 0;
u8 *constraint_tuple;
u8 *this_constraint;
u8 **orig, **closure;
uword *p;
int i, j, k;
u8 *a_name, *b_name;
int a_index, b_index;
int n_features;
u32 *result = 0;
vnet_feature_registration_t *this_reg = 0;
vnet_feature_constraint_registration_t *this_const_set = 0;
char **feature_nodes = 0;
hash_pair_t *hp;
u8 **keys_to_delete = 0;
index_by_name = hash_create_string (0, sizeof (uword));
reg_by_index = hash_create (0, sizeof (uword));
this_reg = first_reg;
/* pass 1, collect feature node names, construct a before b pairs */
/* 第一步:收集特性节点名字,构建一个a,b对数组 */
while (this_reg)
{
node_name = format (0, "%s%c", this_reg->node_name, 0);/* 特性节点名字 */
hash_set (reg_by_index, vec_len (node_names), (uword) this_reg);/* 特性索引与节点hash */
/* 特性索引与名字hash */
hash_set_mem (index_by_name, node_name, vec_len (node_names));
/* 加入数组 */
vec_add1 (node_names, node_name);
/* 在节点之前进行执行,遍历每一个这样的节点 */
these_constraints = this_reg->runs_before;
while (these_constraints && these_constraints[0])
{
this_constraint_c = these_constraints[0];
constraint_tuple = format (0, "%s,%s%c", node_name,
this_constraint_c, 0);
vec_add1 (constraints, constraint_tuple);
these_constraints++;
}
/* 在节点之后执行 */
these_constraints = this_reg->runs_after;
while (these_constraints && these_constraints[0])
{
this_constraint_c = these_constraints[0];
constraint_tuple = format (0, "%s,%s%c",
this_constraint_c, node_name, 0);
vec_add1 (constraints, constraint_tuple);
these_constraints++;
}
this_reg = this_reg->next_in_arc;
}
/* pass 2, collect bulk "a then b then c then d" constraints */
/* 处理额外的限制,限制是一个链表,链表的每一个节点时是一个节点数组,表示一种依赖关系,限制性前面的后执行后面的 */
this_const_set = first_const_set;
while (this_const_set)
{
these_constraints = this_const_set->node_names;
prev_name = 0;
/* Across the list of constraints */
while (these_constraints && these_constraints[0])
{
this_constraint_c = these_constraints[0];
/* 获取限制特性 */
p = hash_get_mem (index_by_name, this_constraint_c);
if (p == 0)
{
clib_warning
("bulk constraint feature node '%s' not found for arc '%s'",
this_constraint_c);
these_constraints++;
continue;
}
if (prev_name == 0)
{
prev_name = this_constraint_c;
these_constraints++;
continue;
}
constraint_tuple = format (0, "%s,%s%c", prev_name,
this_constraint_c, 0);
vec_add1 (constraints, constraint_tuple);
prev_name = this_constraint_c;
these_constraints++;
}
this_const_set = this_const_set->next_in_arc;
}
/* 下面的代码整个是使用warshall算法对有向图传递闭包进行处理,目的是依据限制构建一个合理feature执行顺序。有点烧脑,有空再研究 */
n_features = vec_len (node_names);
/* 构建一个n_features*n_features的矩阵 */
orig = clib_ptclosure_alloc (n_features);
/* 遍历每一个限制条件 */
/* 构建前后依赖矩阵 */
for (i = 0; i < vec_len (constraints); i++)
{
this_constraint = constraints[i];
if (comma_split (this_constraint, &a_name, &b_name))
return clib_error_return (0, "comma_split failed!");
p = hash_get_mem (index_by_name, a_name);
/*
* Note: the next two errors mean that something is
* b0rked. As in: if you code "A depends on B," and you forget
* to define a FEATURE_INIT macro for B, you lose.
* Nonexistent graph nodes are tolerated.
*/
if (p == 0)
{
clib_warning ("feature node '%s' not found (before '%s', arc '%s')",
a_name, b_name, first_reg->arc_name);
continue;
}
a_index = p[0];
p = hash_get_mem (index_by_name, b_name);
if (p == 0)
{
clib_warning ("feature node '%s' not found (after '%s', arc '%s')",
b_name, a_name, first_reg->arc_name);
continue;
}
b_index = p[0];
/* add a before b to the original set of constraints */
orig[a_index][b_index] = 1;
vec_free (this_constraint);
}
/* Compute the positive transitive closure of the original constraints */
closure = clib_ptclosure (orig);
/* Compute a partial order across feature nodes, if one exists. */
again:
for (i = 0; i < n_features; i++)
{
for (j = 0; j < n_features; j++)
{
if (closure[i][j])
goto item_constrained;
}
/* Item i can be output */
vec_add1 (result, i);
{
for (k = 0; k < n_features; k++)
closure[k][i] = 0;
/*
* Add a "Magic" a before a constraint.
* This means we'll never output it again
*/
closure[i][i] = 1;
goto again;
}
item_constrained:
;
}
/* see if we got a partial order... */
if (vec_len (result) != n_features)
return clib_error_return
(0, "Arc '%s': failed to find a suitable feature order!",
first_reg->arc_name);
/*
* We win.
* Bind the index variables, and output the feature node name vector
* using the partial order we just computed. Result is in stack
* order, because the entry with the fewest constraints (e.g. none)
* is output first, etc.
*/
for (i = n_features - 1; i >= 0; i--)
{
p = hash_get (reg_by_index, result[i]);
ASSERT (p != 0);
this_reg = (vnet_feature_registration_t *) p[0];
if (this_reg->feature_index_ptr)
*this_reg->feature_index_ptr = n_features - (i + 1);
this_reg->feature_index = n_features - (i + 1);
vec_add1 (feature_nodes, this_reg->node_name);
}
/* Set up the config infrastructure */
/* 构建feature与node索引之间的映射关系,同时设置起始节点和终止节点 */
vnet_config_init (vm, vcm,
feature_start_nodes,
num_feature_start_nodes,
feature_nodes, vec_len (feature_nodes));
/* Save a copy for show command */
*in_feature_nodes = feature_nodes;
/* Finally, clean up all the shit we allocated */
/* *INDENT-OFF* */
hash_foreach_pair (hp, index_by_name,
({
vec_add1 (keys_to_delete, (u8 *)hp->key);
}));
/* *INDENT-ON* */
hash_free (index_by_name);
for (i = 0; i < vec_len (keys_to_delete); i++)
vec_free (keys_to_delete[i]);
vec_free (keys_to_delete);
hash_free (reg_by_index);
vec_free (result);
clib_ptclosure_free (orig);
clib_ptclosure_free (closure);
return 0;
}
构建feature与对应node的关联关系
使用函数vnet_config_init进行构建
void
vnet_config_init (vlib_main_t * vm,
vnet_config_main_t * cm,
char *start_node_names[],/* 该arc的起始节点和个数 */
int n_start_node_names,
char *feature_node_names[], int n_feature_node_names)/* 该arc的特性节点和个数 */
{
vlib_node_t *n;
u32 i;
clib_memset (cm, 0, sizeof (cm[0]));
/* 创建一个hash表 */
cm->config_string_hash =
hash_create_vec (0,
STRUCT_SIZE_OF (vnet_config_t, config_string_vector[0]),
sizeof (uword));
ASSERT (n_feature_node_names >= 1);
/* 遍历每一个起始节点,根据起始节点名字获取对应节点的索引,构建starts索引与节点索引之间的映射 */
vec_resize (cm->start_node_indices, n_start_node_names);
for (i = 0; i < n_start_node_names; i++)
{
n = vlib_get_node_by_name (vm, (u8 *) start_node_names[i]);
/* Given node name must exist. */
ASSERT (n != 0);
cm->start_node_indices[i] = n->index;
}
/* 遍历所有featrue节点,构建feature索引与节点索引之间的映射 */
vec_resize (cm->node_index_by_feature_index, n_feature_node_names);
for (i = 0; i < n_feature_node_names; i++)
{
if (!feature_node_names[i])
cm->node_index_by_feature_index[i] = ~0;
else
{
n = vlib_get_node_by_name (vm, (u8 *) feature_node_names[i]);
/* Given node may exist in plug-in library which is not present */
if (n)
{
if (i + 1 == n_feature_node_names)/* 最后一个节点为我们的结束节点 */
cm->end_node_index = n->index;
cm->node_index_by_feature_index[i] = n->index;
}
else
cm->node_index_by_feature_index[i] = ~0;
}
}
}
vnet_feature_init函数调用入口
该函数使用宏VLIB_INIT_FUNCTION (vnet_feature_init);进行初始化函数注册,在三个地方会调用该函数:
1. ethernet_init
2. ip4_lookup_init
3. ip6_lookup_init
由vpp的初始化函数调用机制保证该函数只调用一次。
feature机制配置
前面介绍的只是feature机制的注册过程,并没有生效。只有配置后才能生效。
配置实例分析
在分析配置之前,我们先看一下实际的配置过程中feature相关表项的现象。
我们以nat为例:
1. 给接口配置IP之前
vpp# show interface features GigabitEthernet3/0/0
ip4-unicast:
ip4-not-enabled
2. 配置nat之前,配置接口地址,查看接口的feature
vpp# set interface ip address GigabitEthernet3/0/0 192.168.134.128/24
vpp# show interface features GigabitEthernet3/0/0
Feature paths configured on GigabitEthernet3/0/0...
......
ip4-multicast:
ip4-unicast:
......
3. 配置nat之后,查看接口的feature
vpp# set interface nat44 in GigabitEthernet13/0/0 out GigabitEthernet3/0/0
vpp# show interface features GigabitEthernet3/0/0
Feature paths configured on GigabitEthernet3/0/0...
......
ip4-unicast:
nat44-out2in-worker-handoff
......
从上面的配置变化可知,feature与接口相关,通过命令配置可以让指定的feature生效。
4. gdb跟踪feature调用过程
(gdb) b vnet_feature_enable_disable
Breakpoint 1 at 0x7fd5d5470d90: file /home/jd/vpp/src/vnet/feature/feature.c, line 277.
(gdb) c
Continuing.
Thread 1 "vpp_main" hit Breakpoint 1, vnet_feature_enable_disable (arc_name=arc_name@entry=0x7fd58e836c22 "ip4-unicast",
node_name=node_name@entry=0x7fd58e836bb8 "nat44-in2out-worker-handoff", sw_if_index=sw_if_index@entry=3, enable_disable=enable_disable@entry=1,
feature_config=feature_config@entry=0x0, n_feature_config_bytes=n_feature_config_bytes@entry=0) at /home/jd/vpp/src/vnet/feature/feature.c:277
277 {
(gdb) bt
#0 vnet_feature_enable_disable (arc_name=arc_name@entry=0x7fd58e836c22 "ip4-unicast",
node_name=node_name@entry=0x7fd58e836bb8"nat44-in2out-worker-handoff",sw_if_index=sw_if_index@entry=3,enable_disable=enable_disable@entry=1,
feature_config=feature_config@entry=0x0,n_feature_config_bytes=n_feature_config_bytes@entry=0)at/home/jd/vpp/src/vnet/feature/feature.c:277
#1 0x00007fd58e7915d0 in snat_interface_add_del (sw_if_index=3, is_inside=is_inside@entry=1 '\001', is_del=is_del@entry=0)
at /home/jd/vpp/src/plugins/nat/nat.c:1935
#2 0x00007fd58e7fd8ef in snat_feature_command_fn (vm=<optimized out>, input=<optimized out>, cmd=<optimized out>)
at /home/jd/vpp/src/plugins/nat/nat44_cli.c:698
#3 0x00007fd5d4725a46 in vlib_cli_dispatch_sub_commands (vm=vm@entry=0x7fd5d499f700 <vlib_global_main>,
cm=cm@entry=0x7fd5d499f900 <vlib_global_main+512>, input=input@entry=0x7fd5944a4f60, parent_command_index=<optimized out>)
at /home/jd/vpp/src/vlib/cli.c:607
#4 0x00007fd5d4726087 in vlib_cli_dispatch_sub_commands (vm=vm@entry=0x7fd5d499f700 <vlib_global_main>,
cm=cm@entry=0x7fd5d499f900 <vlib_global_main+512>, input=input@entry=0x7fd5944a4f60, parent_command_index=<optimized out>)
at /home/jd/vpp/src/vlib/cli.c:568
#5 0x00007fd5d4726087 in vlib_cli_dispatch_sub_commands (vm=vm@entry=0x7fd5d499f700 <vlib_global_main>,
cm=cm@entry=0x7fd5d499f900 <vlib_global_main+512>, input=input@entry=0x7fd5944a4f60, parent_command_index=parent_command_index@entry=0)
at /home/jd/vpp/src/vlib/cli.c:568
#6 0x00007fd5d4726354 in vlib_cli_input (vm=0x7fd5d499f700 <vlib_global_main>, input=input@entry=0x7fd5944a4f60,
function=function@entry=0x7fd5d477f8a0 <unix_vlib_cli_output>, function_arg=function_arg@entry=0) at /home/jd/vpp/src/vlib/cli.c:707
#7 0x00007fd5d4781466 in unix_cli_process_input (cm=0x7fd5d49a0040 <unix_cli_main>, cli_file_index=0) at /home/jd/vpp/src/vlib/unix/cli.c:2420
#8 unix_cli_process (vm=0x7fd5d499f700 <vlib_global_main>, rt=0x7fd594494000, f=<optimized out>) at /home/jd/vpp/src/vlib/unix/cli.c:2536
#9 0x00007fd5d473dda6 in vlib_process_bootstrap (_a=<optimized out>) at /home/jd/vpp/src/vlib/main.c:1469
#10 0x00007fd5d423da44 in clib_calljmp () from /usr/lib/x86_64-linux-gnu/libvppinfra.so.19.08
#11 0x00007fd5937ffb80 in ?? ()
#12 0x00007fd5d4743911 in vlib_process_startup(f=0x0,p=0x7fd594494000,vm=0x7fd5d499f700<vlib_global_main>) at /home/jd/vpp/src/vlib/main.c:1491
#13 dispatch_process (vm=0x7fd5d499f700 <vlib_global_main>, p=0x7fd594494000, last_time_stamp=0, f=0x0) at /home/jd/vpp/src/vlib/main.c:1536
#14 0x0000000000000000 in ?? ()
(gdb)
feature机制配置全局管理对象
typedef struct
{
/* Pool of configs. Index 0 is always null config and is never deleted. */
/* 配置池,与config_string_heap存在相互指向过程,为什么分为两块内存保存配置,比较奇怪 */
vnet_config_t *config_pool;
/* Hash table mapping vector config string to config pool index. */
/* */
uword *config_string_hash;
/* Global heap of configuration data. */
/* 全局配置数据堆,从堆中分配用于保存配置的内存 */
u32 *config_string_heap;
/* Node index which starts/ends feature processing. */
/* 该arc的起始节点数组和结束节点的node索引 */
u32 *start_node_indices, end_node_index;
/* Interior feature processing nodes (not including start and end nodes). */
/* 根据feature索引找到node索引的映射表 */
u32 *node_index_by_feature_index;
/* vnet_config pool index by user index */
/* 根据用户索引获取配置池索引 */
u32 *config_pool_index_by_user_index;
/* Temporary vector for holding config strings. Used to avoid continually
allocating vectors. */
u32 *config_string_temp;
} vnet_config_main_t;
typedef struct
{
/* Sorted vector of features for this configuration. */
/* 该配置中的有序特性数组 */
vnet_config_feature_t *features;
/* Config string as vector for hashing. */
u32 *config_string_vector;
/* Config string including all next indices and feature data as a vector. */
/* 从堆中分配的一块内存的索引config_string_heap_index,用于获取对应的内存
config_string_heap_handle用于销毁该内存*/
u32 config_string_heap_index, config_string_heap_handle;
/* Index in main pool. */
/* 主配置池中的索引 */
u32 index;
/* Number of interfaces/traffic classes that reference this config.
* 引用计数
*/
u32 reference_count;
} vnet_config_t;
typedef struct
{
/* Features are prioritized by index. Smaller indices get
performed first.
特性索引,有优先级的意义,越小优先级越高
*/
u32 feature_index;
/* VLIB node which performs feature. 特性对应的节点*/
u32 node_index;
/* Next index relative to previous node or main node. 相对于前一个节点的序号 */
u32 next_index;
/* Opaque per feature configuration data. 特性的透明数据 */
u32 *feature_config;
} vnet_config_feature_t;
配置函数vnet_feature_enable_disable
int
vnet_feature_enable_disable (const char *arc_name, const char *node_name,
u32 sw_if_index, int enable_disable,
void *feature_config, u32 n_feature_config_bytes)
{
u32 feature_index;
u8 arc_index;
/* 根据名字获取其对应的arc的索引 */
arc_index = vnet_get_feature_arc_index (arc_name);
if (arc_index == (u8) ~ 0)
return VNET_API_ERROR_INVALID_VALUE;
/* 根据节点名字和arc_index获取对应的特性索引 */
feature_index = vnet_get_feature_index (arc_index, node_name);
return vnet_feature_enable_disable_with_index (arc_index, feature_index,
sw_if_index, enable_disable,
feature_config,
n_feature_config_bytes);
}
vnet_feature_enable_disable_with_index
int
vnet_feature_enable_disable_with_index (u8 arc_index, u32 feature_index,/* arc索引,feature索引 */
u32 sw_if_index, int enable_disable,/* 接口索引,使能/去使能 */
void *feature_config, /* 配置透明参数 */
u32 n_feature_config_bytes)/* 配置透明参数大小 */
{
vnet_feature_main_t *fm = &feature_main;
vnet_feature_config_main_t *cm;
i16 feature_count;
u32 ci;
if (arc_index == (u8) ~ 0)
return VNET_API_ERROR_INVALID_VALUE;
if (feature_index == ~0)
return VNET_API_ERROR_INVALID_VALUE_2;
/* 获取arc对应的配置控制块 */
cm = &fm->feature_config_mains[arc_index];
vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0);
ci = cm->config_index_by_sw_if_index[sw_if_index];/* 获取接口对应的配置索引 */
vec_validate (fm->feature_count_by_sw_if_index[arc_index], sw_if_index);
/* 获取该接口上引用该arc的特性个数 */
feature_count = fm->feature_count_by_sw_if_index[arc_index][sw_if_index];
/* 如果是禁止,并且没有使能过特性,那么直接返回 */
if (!enable_disable && feature_count < 1)
return 0;
/* 如果是使能,那么调用vnet_config_add_feature,否则调用vnet_config_del_feature */
ci = (enable_disable
? vnet_config_add_feature
: vnet_config_del_feature)
(vlib_get_main (), &cm->config_main, ci, feature_index, feature_config,
n_feature_config_bytes);
if (ci == ~0)
{
return 0;
}
/* 重新设置配置索引 */
cm->config_index_by_sw_if_index[sw_if_index] = ci;
/* update feature count 更新统计信息*/
enable_disable = (enable_disable > 0);
feature_count += enable_disable ? 1 : -1;
ASSERT (feature_count >= 0);
/* 更新接口的特性bit位 */
fm->sw_if_index_has_features[arc_index] =
clib_bitmap_set (fm->sw_if_index_has_features[arc_index], sw_if_index,
(feature_count > 0));
/* 跟新该接口的邻居信息,暂不分析 */
adj_feature_update (sw_if_index, arc_index, (feature_count > 0));
/* 更新该接口上使能的该接口的特性个数 */
fm->feature_count_by_sw_if_index[arc_index][sw_if_index] = feature_count;
return 0;
}
vnet_config_add_feature
u32
vnet_config_add_feature (vlib_main_t * vm,
vnet_config_main_t * cm,
u32 config_string_heap_index,
u32 feature_index,
void *feature_config, u32 n_feature_config_bytes)
{
vnet_config_t *old, *new;
vnet_config_feature_t *new_features, *f;
u32 n_feature_config_u32s;
/* 根据特性索引获取特性对应的node */
u32 node_index = vec_elt (cm->node_index_by_feature_index, feature_index);
if (node_index == ~0) // feature node does not exist
return ~0;
/* 该特性是否已经配置过 */
if (config_string_heap_index == ~0)/* 没有配置过 */
{
old = 0;
new_features = 0;
}
else/* 已经配置过,则获取其配置控制块 */
{
/* 根据堆索引获取配置信息 */
u32 *p = vnet_get_config_heap (cm, config_string_heap_index);
/* -1个偏移保存的是vnet_config_t信息地址 */
old = pool_elt_at_index (cm->config_pool, p[-1]);
new_features = old->features;
if (new_features)
/* 拷贝一份特性数组 */
new_features = duplicate_feature_vector (new_features);
}
/* 增加一个特性 */
vec_add2 (new_features, f, 1);
f->feature_index = feature_index;
f->node_index = node_index;
/* 计算新的配置信息有多个个四字节 */
n_feature_config_u32s =
round_pow2 (n_feature_config_bytes,
sizeof (f->feature_config[0])) /
sizeof (f->feature_config[0]);
/* 将特性参数拷贝到f->feature_config中 */
vec_add (f->feature_config, feature_config, n_feature_config_u32s);
/* Sort (prioritize) features. */
/* 对特性根据索引进行排序 */
if (vec_len (new_features) > 1)
vec_sort_with_function (new_features, feature_cmp);
/* 释放原来的配置 */
if (old)
remove_reference (cm, old);
/* 构建一个新的vnet_config_t,增加引用计数 */
new = find_config_with_features (vm, cm, new_features);
new->reference_count += 1;
/*
* User gets pointer to config string first element
* (which defines the pool index
* this config string comes from).
*/
vec_validate (cm->config_pool_index_by_user_index,
new->config_string_heap_index + 1);
/* 通过用户索引获取对应的配置索引 */
cm->config_pool_index_by_user_index[new->config_string_heap_index + 1]
= new - cm->config_pool;
/* 返回用户索引 */
return new->config_string_heap_index + 1;
}
find_config_with_features
static vnet_config_t *
find_config_with_features (vlib_main_t * vm,
vnet_config_main_t * cm,
vnet_config_feature_t * feature_vector)
{
u32 last_node_index = ~0;
vnet_config_feature_t *f;
u32 *config_string;
uword *p;
vnet_config_t *c;
config_string = cm->config_string_temp;
cm->config_string_temp = 0;
if (config_string)
_vec_len (config_string) = 0;
/* 遍历每一个特性数组 */
vec_foreach (f, feature_vector)
{
/* Connect node graph. 将节点添加到全局的动态节点图中,返回的值为当前节点在其前一个节点的下一个节点数组中的索引 */
f->next_index = add_next (vm, cm, last_node_index, f->node_index);
last_node_index = f->node_index;
/* Store next index in config string. 存储索引在配置字符中 */
vec_add1 (config_string, f->next_index);
/* Store feature config. 存储配置参数在后面 */
vec_add (config_string, f->feature_config, vec_len (f->feature_config));
}
/* Terminate config string with next for end node. */
if (last_node_index == ~0 || last_node_index != cm->end_node_index)
{
u32 next_index = add_next (vm, cm, last_node_index, cm->end_node_index);
vec_add1 (config_string, next_index);
}
/* See if config string is unique.config_string中是特性的索引 */
p = hash_get_mem (cm->config_string_hash, config_string);
if (p)
{
/* Not unique. Share existing config. */
cm->config_string_temp = config_string; /* we'll use it again later. */
free_feature_vector (feature_vector);
/* 获取配置索引 */
c = pool_elt_at_index (cm->config_pool, p[0]);
}
else
{
u32 *d;
/* config_pool中分配一个vnet_config_t结构 */
pool_get (cm->config_pool, c);
/* 计算vnet_config_t的索引 */
c->index = c - cm->config_pool;
/* 指向配置的特性数组 */
c->features = feature_vector;
c->config_string_vector = config_string;
/* Allocate copy of config string in heap.
VLIB buffers will maintain pointers to heap as they read out
configuration data. */
c->config_string_heap_index
= heap_alloc (cm->config_string_heap, vec_len (config_string) + 1,
c->config_string_heap_handle);
/* First element in heap points back to pool index. */
d =
vec_elt_at_index (cm->config_string_heap,
c->config_string_heap_index);
d[0] = c->index;
clib_memcpy (d + 1, config_string, vec_bytes (config_string));
/* 构建配置字符向量地址与配置索引的映射关系 */
hash_set_mem (cm->config_string_hash, config_string, c->index);
c->reference_count = 0; /* will be incremented by caller. */
}
return c;
}
vnet_config_del_feature
u32
vnet_config_del_feature (vlib_main_t * vm,
vnet_config_main_t * cm,
u32 config_string_heap_index,
u32 feature_index,
void *feature_config, u32 n_feature_config_bytes)
{
vnet_config_t *old, *new;
vnet_config_feature_t *new_features, *f;
u32 n_feature_config_u32s;
{
/* 根据堆索引获取配置堆 */
u32 *p = vnet_get_config_heap (cm, config_string_heap_index);
/* 获取vnet_config_t配置 */
old = pool_elt_at_index (cm->config_pool, p[-1]);
}
/* 计算该特性的私有透明数据长度 */
n_feature_config_u32s =
round_pow2 (n_feature_config_bytes,
sizeof (f->feature_config[0])) /
sizeof (f->feature_config[0]);
/* Find feature with same index and opaque data. */
/* 根据索引找到对应的featue,并且要求其对应的透明数据需要完全一样 */
vec_foreach (f, old->features)
{
if (f->feature_index == feature_index /* 遍历每一个特性,比较特性索引以及私有透明数据 */
&& vec_len (f->feature_config) == n_feature_config_u32s
&& (n_feature_config_u32s == 0
|| !memcmp (f->feature_config, feature_config,
n_feature_config_bytes)))
break;
}
/* Feature not found. 没有找到*/
if (f >= vec_end (old->features))
return ~0;
/* 复制特性结构 */
new_features = duplicate_feature_vector (old->features);
f = new_features + (f - old->features);/* 得到要删除的特性的地址 */
vnet_config_feature_free (f);
/* 从数组中删除该特性 */
vec_delete (new_features, 1, f - new_features);
/* must remove old from config_pool now as it may be expanded and change
memory location if the following function find_config_with_features()
adds a new config because none of existing config's has matching features
and so can be reused */
remove_reference (cm, old);
/* 将新特性重新插入 */
new = find_config_with_features (vm, cm, new_features);
new->reference_count += 1;
vec_validate (cm->config_pool_index_by_user_index,
new->config_string_heap_index + 1);
cm->config_pool_index_by_user_index[new->config_string_heap_index + 1]
= new - cm->config_pool;
return new->config_string_heap_index + 1;
}
到此,整个配置的添加删除介绍完毕,似乎在删除feature函数中没有add_next的反操作,是否为一个bug?
feature机制运行流程
前面的章节中,我们分析了feature机制的配置流程。配置号feature机制后,报文在运行过程中将会经过这些动态添加
的节点。
feature arc的起始节点
我们以前面提到的snat注册的节点为例进行分析
VNET_FEATURE_ARC_INIT (ip4_unicast, static) =
{
.arc_name = "ip4-unicast",
.start_nodes = VNET_FEATURES ("ip4-input", "ip4-input-no-checksum"),/* 起始节点,必须存在node */
.last_in_arc = "ip4-lookup", /* 整个arc的最后一个feature必须是ip4-lookup,否则不能初始化成功 */
.arc_index_ptr = &ip4_main.lookup_main.ucast_feature_arc_index,/* 需要将arc索引写入到ucast_feature_arc_index中,供对应模块使用 */
};
起始节点为"ip4-input", "ip4-input-no-checksum"两个节点。两个节点的功能函数都会调用ip4_input_inline函数。
ip4_input_set_next
p4_input_inline对报文处理完节点的功能后,会调用函数ip4_input_set_next进行下一个节点的选择,我们重点分析一下函数ip4_input_set_next:
static_always_inline u32
ip4_input_set_next (u32 sw_if_index, vlib_buffer_t * b, int arc_enabled)/* 参数分别为接口索引,报文buf,是否使能arc机制 */
{
ip4_main_t *im = &ip4_main;
ip_lookup_main_t *lm = &im->lookup_main;
u32 next;
u8 arc;
ip4_header_t *ip = vlib_buffer_get_current (b);
if (PREDICT_FALSE (ip4_address_is_multicast (&ip->dst_address)))/* 组播报文,默认的下一个节点为IP4_INPUT_NEXT_LOOKUP_MULTICAST */
{
next = IP4_INPUT_NEXT_LOOKUP_MULTICAST;
arc = lm->mcast_feature_arc_index;/* 这个是在feature在初始化设置的,即arc_index_ptr参数 */
}
else
{
next = IP4_INPUT_NEXT_LOOKUP;/* 单播报文默认的下一个节点为IP4_INPUT_NEXT_LOOKUP */
arc = lm->ucast_feature_arc_index;/* 获取ucast_feature索引,这个是在feature在初始化设置的,即arc_index_ptr参数 */
}
if (arc_enabled)/* 使能了arc机制,进行下一个节点选择 */
vnet_feature_arc_start (arc, sw_if_index, &next, b);
return next;
}
vnet_feature_arc_start
static_always_inline void
vnet_feature_arc_start (u8 arc, u32 sw_if_index, u32 * next0,
vlib_buffer_t * b0)
{
vnet_feature_arc_start_with_data (arc, sw_if_index, next0, b0, 0);
}
static_always_inline void *
vnet_feature_arc_start_with_data (u8 arc, u32 sw_if_index, u32 * next,/* 参数为arc索引,接口索引,得到的下一个node索引 */
vlib_buffer_t * b, u32 n_data_bytes)
{
vnet_feature_main_t *fm = &feature_main;
vnet_feature_config_main_t *cm;
cm = &fm->feature_config_mains[arc];
if (PREDICT_FALSE (vnet_have_features (arc, sw_if_index)))/* 该接口是否使能了该arc */
{
vnet_buffer (b)->feature_arc_index = arc;
b->current_config_index =/* 根据接口索引获取对应的配置索引 */
vec_elt (cm->config_index_by_sw_if_index, sw_if_index);
return vnet_get_config_data (&cm->config_main, &b->current_config_index,
next, n_data_bytes);
}
return 0;
}
always_inline void *
vnet_get_config_data (vnet_config_main_t * cm,
u32 * config_index, u32 * next_index, u32 n_data_bytes)
{
u32 i, n, *d;
i = *config_index;
/* 从堆中根据配置索引获取配置起始地址 */
d = heap_elt_at_index (cm->config_string_heap, i);
/* n_data_bytes传入的是0,所以n等于0 */
n = round_pow2 (n_data_bytes, sizeof (d[0])) / sizeof (d[0]);
/* Last 32 bits are next index. */
/* 该内存的-1下标存储的是config索引,d[n]=d[0]然后是第一个下一个节点的索引 */
*next_index = d[n];
/* Advance config index to next config. */
/* 没看明白,这是干啥,估计与堆的实现有关,先不管,后续分析 */
*config_index = (i + n + 1);
/* Return config data to user for this feature. */
return (void *) d;
}
起始节点通过vnet_feature_arc_start函数即可改变原来的下一个节点的流程,切入feature框架
feature节点如何运行下一个feature节点
我们以snat为例进行分析
/* Hook up ip4-local features */
VNET_FEATURE_INIT (ip4_nat_hairpinning, static) =
{
.arc_name = "ip4-local",
.node_name = "nat44-hairpinning",
.runs_before = VNET_FEATURES("ip4-local-end-of-arc"),
};
对应的节点和功能函数为
VLIB_NODE_FN (nat44_hairpinning_node) (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
return nat44_hairpinning_fn_inline (vm, node, frame, 0);
}
/* *INDENT-OFF* */
VLIB_REGISTER_NODE (nat44_hairpinning_node) = {
.name = "nat44-hairpinning",
.vector_size = sizeof (u32),
.type = VLIB_NODE_TYPE_INTERNAL,
.n_errors = ARRAY_LEN(nat44_hairpin_error_strings),
.error_strings = nat44_hairpin_error_strings,
.n_next_nodes = NAT_HAIRPIN_N_NEXT,
.next_nodes = {
[NAT_HAIRPIN_NEXT_DROP] = "error-drop",
[NAT_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup",
},
};
/* *INDENT-ON* */
nat44_hairpinning_fn_inline
static inline uword
nat44_hairpinning_fn_inline (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame, int is_ed)
{
u32 n_left_from, *from, *to_next, stats_node_index;
nat_hairpin_next_t next_index;
u32 pkts_processed = 0;
snat_main_t *sm = &snat_main;
vnet_feature_main_t *fm = &feature_main;
u8 arc_index = vnet_feat_arc_ip4_local.feature_arc_index;
vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_index];
stats_node_index = is_ed ? sm->ed_hairpinning_node_index :
sm->hairpinning_node_index;
from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;
next_index = node->cached_next_index;
while (n_left_from > 0)
{
u32 n_left_to_next;
vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
while (n_left_from > 0 && n_left_to_next > 0)
{
u32 bi0;
vlib_buffer_t *b0;
u32 next0;
ip4_header_t *ip0;
u32 proto0;
udp_header_t *udp0;
tcp_header_t *tcp0;
/* speculatively enqueue b0 to the current next frame */
bi0 = from[0];
to_next[0] = bi0;
from += 1;
to_next += 1;
n_left_from -= 1;
n_left_to_next -= 1;
b0 = vlib_get_buffer (vm, bi0);
ip0 = vlib_buffer_get_current (b0);
udp0 = ip4_next_header (ip0);
tcp0 = (tcp_header_t *) udp0;
proto0 = ip_proto_to_snat_proto (ip0->protocol);
vnet_get_config_data (&cm->config_main, &b0->current_config_index,
&next0, 0);/* 直接使用该函数选择下一个feature节点,这个函数在上面已经分析过了 */
if (snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0, is_ed))
next0 = NAT_HAIRPIN_NEXT_LOOKUP;
pkts_processed += next0 != NAT_HAIRPIN_NEXT_DROP;
/* verify speculative enqueue, maybe switch current next frame */
vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
to_next, n_left_to_next,
bi0, next0);
}
vlib_put_next_frame (vm, node, next_index, n_left_to_next);
}
vlib_node_increment_counter (vm, stats_node_index,
NAT44_HAIRPIN_ERROR_PROCESSED, pkts_processed);
return frame->n_vectors;
}
小结
对于feature节点可以调用如下函数进行下一个节点的选取,feature机制比较灵活,不是所有feature节点都会调用下面的函数,
机制比较复杂,后续继续分析
static_always_inline void *
vnet_feature_next_with_data (u32 * next0, vlib_buffer_t * b0,
u32 n_data_bytes)
{
vnet_feature_main_t *fm = &feature_main;
u8 arc = vnet_buffer (b0)->feature_arc_index;
vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc];
return vnet_get_config_data (&cm->config_main,
&b0->current_config_index, next0,
n_data_bytes);
}
static_always_inline void
vnet_feature_next (u32 * next0, vlib_buffer_t * b0)
{
vnet_feature_next_with_data (next0, b0, 0);
}
always_inline void *
vnet_get_config_data (vnet_config_main_t * cm,
u32 * config_index, u32 * next_index, u32 n_data_bytes)
{
u32 i, n, *d;
i = *config_index;
d = heap_elt_at_index (cm->config_string_heap, i);
n = round_pow2 (n_data_bytes, sizeof (d[0])) / sizeof (d[0]);
/* Last 32 bits are next index. */
*next_index = d[n];
/* Advance config index to next config. */
*config_index = (i + n + 1);
/* Return config data to user for this feature. */
return (void *) d;
}
feature实战
后续补齐
参考文档
https://blog.csdn.net/shaoyunzhe/article/details/53694092
https://wiki.fd.io/view/VPP/Feature_Arcs