早期的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