1. 背景和概念

  • 早期的VPP本身的node框架比较固定,各个node之间逻辑连接已经固化,为此新版本增加了feature机制.

  • 每个feature是一个node,用户可以启用/停止某个或某些feature

  • 用户也可以自己写插件,把自定义node(自己的业务逻辑)加入到指定位置

2. 重要数据结构和操作函数

# 
# vnet_feature_arc_registration_t
# ----------------------------------
# vpp将feature分成不同的组,每组feature称为一个arc
# arc中的feature按照代码指定的顺序串接起来
# arc结构将记录这组feature的起始node和结束node
# 系统初始化时完成每个feature的连接

# vnet/feature/feature.h +38

/** feature registration object */
typedef struct _vnet_feature_arc_registration
{
  /** next registration in list of all registrations*/
  struct _vnet_feature_arc_registration *next;
  /** Feature Arc name */
  char *arc_name;
  /** Start nodes */
  char **start_nodes;
  int n_start_nodes;
  /** End of the arc (optional, for consistency-checking) */
  char *last_in_arc;
  /* Feature arc index, assigned by init function */
  u8 feature_arc_index;
  u8 *arc_index_ptr;
} vnet_feature_arc_registration_t;

# VNET_FEATURE_ARC_INIT宏用来注册arc
# ----------------------------------
# 在arc中指定的起始node中,必须调用vnet_feature_arc_start函数,
# 才能正式进入feature机制业务流程,该函数会将下一跳强行指定为arc中的下一个feature

# 先初始化arc
VNET_FEATURE_ARC_INIT (device_input, static) =
{
  .arc_name  = "device-input",
  .start_nodes = VNET_FEATURES ("device-input"),
  .arc_index_ptr = &feature_main.device_input_feature_arc_index,
};

# 再初始化这个arc下的feature,通过.runs_before来控制feature的先后关系,当然也有runs_after
VNET_FEATURE_INIT (worker_handoff, static) = {
  .arc_name = "device-input",
  .node_name = "worker-handoff",
  .runs_before = VNET_FEATURES ("ethernet-input"),
};

VNET_FEATURE_INIT (span_input, static) = {
  .arc_name = "device-input",
  .node_name = "span-input",
  .runs_before = VNET_FEATURES ("ethernet-input"),
};

VNET_FEATURE_INIT (ethernet_input, static) = {
  .arc_name = "device-input",
  .node_name = "ethernet-input",
  .runs_before = 0, /* not before any other features */
};
# 把这些node都挂在对应的构造函数上,在初始化的时候就会调用
# 然后初始化相应的配置,也就是vnet_feature_arc_init 这个函数,
# 会根据之前的配置,将各feature的先后关系排列好
# 此时只是排列好顺序,并未插入feature_arc中,也就是不会被调用到

# 调用vnet_feature_enable_disable,此时才是将要用到的feature真正的插入feature_arc中

# vnet/feature/feature.h +122 

#define VNET_FEATURE_ARC_INIT(x,...)                \
  __VA_ARGS__ vnet_feature_arc_registration_t vnet_feat_arc_##x;\
static void __vnet_add_feature_arc_registration_##x (void)  \
  __attribute__((__constructor__)) ;                \
static void __vnet_add_feature_arc_registration_##x (void)  \
{                               \
  vnet_feature_main_t * fm = &feature_main;         \
  vnet_feat_arc_##x.next = 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_feature_arc_start
# ------------------------------

# vnet/feature/feature.h +275

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