VPP ARC和feature初始化

文章详细描述了在VPP系统中如何初始化feature_main结构,为ARC分配索引,使用哈希表存储和查找特征,以及处理全局特征和约束列表。主要内容涉及vnet_feature_init函数、feature链表的创建和显示功能路径等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

遍历feature_main主结构的next_arc单向链表,按照顺序为每个ARC注册结构分配索引(feature_arc_index),如果注册ARC的时候为成员arc_index_ptr附了值,将ARC索引写入此值。

将arc_name作为key,areg注册结构为value,写入arc_index_by_name的哈希中,方便之后查找。计算ARC中开始节点的数量,递增ARC索引,遍历下一个ARC注册结构。

vnet_feature_init (vlib_main_t * vm)
{
  vnet_feature_main_t *fm = &feature_main;
  vnet_feature_registration_t *freg;
  vnet_feature_arc_registration_t *areg;
  vnet_feature_constraint_registration_t *creg;
  u32 arc_index = 0;

  fm->arc_index_by_name = hash_create_string (0, sizeof (uword));
  areg = fm->next_arc;
  while (areg) {
      char *s;
      int i = 0;
      areg->feature_arc_index = arc_index;
      if (areg->arc_index_ptr)
        *areg->arc_index_ptr = arc_index;
      hash_set_mem (fm->arc_index_by_name, areg->arc_name, pointer_to_uword (areg));

      while ((s = areg->start_nodes[i]))
        i++;
      areg->n_start_nodes = i;
      areg = areg->next;
      arc_index++;
  }

按照最大的ARC索引值,分配以下的向量。

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

遍历全局features单向链表next_feature,根据feature中的ARC名称,在哈希arc_index_by_name中找到ARC注册结构,进而找到ARC的feature链表头next_feature_by_arc[arc_index],为索引arc_index的ARC创建feature链表。

遍历结束之后,为每个ARC创建了feature链表next_feature_by_arc[arc_index]。

  freg = fm->next_feature;
  while (freg) {
      vnet_feature_registration_t *next;
      uword *p = hash_get_mem (fm->arc_index_by_name, freg->arc_name);
      if (p == 0) {
        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;
      freg->next_in_arc = fm->next_feature_by_arc[arc_index];
      fm->next_feature_by_arc[arc_index] = freg;

      freg = next;
  }

遍历全局的next_constraint链表,最终为每个ARC创建单独的constraint链表,链表头部为next_constraint_by_arc[arc_index],最后添加的位于链表的头部。

  /* Move bulk constraints to the constraint by arc lists */
  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) {
        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;
      creg = next;
  }

最后,再次遍历next_arc链表,对于每个ARC集合,检测其中的features是否满足定义的次序,如果last_in_arc不等于排序之后的最后一个feature,表明发生错误。

  areg = fm->next_arc;
  while (areg)
  {
      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;
      if ((error = vnet_feature_arc_init (vm, vcm, areg->start_nodes, areg->n_start_nodes,
          areg->last_in_arc, fm->next_feature_by_arc[arc_index],
          fm->next_constraint_by_arc[arc_index], &fm->feature_nodes[arc_index]))) {
        os_exit (1);
      }
      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的所有feature初始化next_feature_by_name[arc_index]哈希结构,之后可通过feature名称找到feature注册结构。

      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;
      }
      areg = areg->next;
  }

显示ARC

命令:show features [verbose] 用于显示VPP系统中的注册的ARCs以及每个ARC包含的features集合。所有的信息都保存在全局结构feature_main中,其成员next_arc为保存了ARC注册信息的单向链表,以下函数遍历此链表。

static clib_error_t *
show_features_command_fn (vlib_main_t * vm,
              unformat_input_t * input, vlib_cli_command_t * cmd)
{
  vnet_feature_main_t *fm = &feature_main;
  vnet_feature_arc_registration_t *areg;
  vnet_feature_registration_t *freg;
  vnet_feature_registration_t *feature_regs = 0;

  areg = fm->next_arc;
  while (areg) {
    if (verbose)
      vlib_cli_output (vm, "[%2d] %s:", areg->feature_arc_index, areg->arc_name);
    else
      vlib_cli_output (vm, "%s:", areg->arc_name);

ARC中的features链表保存在feature_main的成员next_feature_by_arc中,单向链表,next_in_arc指向下一个feature注册结构。遍历过程中将所有的feature注册结构保存到feature_regs向量中,按照feature索引值由小到大进行排序之后,输出feature索引和名称信息。

    freg = fm->next_feature_by_arc[areg->feature_arc_index];
    while (freg) {
      vec_add1 (feature_regs, freg[0]);
      freg = freg->next_in_arc;
    }
    vec_sort_with_function (feature_regs, feature_cmp);

    vec_foreach (freg, feature_regs) {
      if (verbose)
        vlib_cli_output (vm, "  [%2d]: %s\n", freg->feature_index, freg->node_name);
      else
        vlib_cli_output (vm, "  %s\n", freg->node_name);
    }
    vec_reset_length (feature_regs);
    areg = areg->next;
  }

如下显示:

vpp# show features verbose
Available feature paths
[ 0] nsh-eth-output:
  [ 0]: interface-output
  [ 1]: error-drop
[ 1] arp:
  [ 0]: vrrp4-arp-input
  [ 1]: linux-cp-arp-phy
  [ 2]: linux-cp-arp-host
  [ 3]: arping-input
  [ 4]: arp-reply
  [ 5]: arp-proxy
  [ 6]: arp-disabled
  [ 7]: error-drop
[ 2] nsh-output:
  [ 0]: error-drop
[ 3] mpls-input:
  [ 0]: vlan-mpls-qos-record
  [ 1]: mpls-qos-record
  [ 2]: mpls-not-enabled
  [ 3]: mpls-lookup

接口feature配置

函数vnet_interface_features_show显示在指定接口上激活的feature集合,这里也是由遍历feature_main结构的成员next_arc开头的单链表开始。

void
vnet_interface_features_show (vlib_main_t * vm, u32 sw_if_index, int verbose)
{
  vnet_feature_main_t *fm = &feature_main;
  vnet_feature_config_main_t *cm = fm->feature_config_mains;
  vnet_feature_arc_registration_t *areg;
  vnet_config_main_t *vcm;
  vnet_config_t *cfg;
  vnet_config_feature_t *feat;
  vlib_node_t *n;

  vlib_cli_output (vm, "Feature paths configured on %U...",
           format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index);

  areg = fm->next_arc;

根据ARC索引找到对应的配置结构vcm,如果接口sw_if_index完全没有激活此ARC,显示"none configured"。否则,检查此接口在此ARC上激活了哪些features。

  while (areg) {
      feature_arc = areg->feature_arc_index;
      vcm = &(cm[feature_arc].config_main);

      vlib_cli_output (vm, "\n%s:", areg->arc_name);
      areg = areg->next;

      if (!vnet_have_features (feature_arc, sw_if_index)) {
        vlib_cli_output (vm, "  none configured");
        continue;
      }

先根据sw_if_index接口索引在向量config_index_by_sw_if_index中找到ARC配置索引(current_config_index),再根据配置索引在向量config_pool_index_by_user_index中找到pool索引,最终,在config_pool中取得相应配置cfg。

遍历cfg结构向量成员features,根据其中的节点索引node_index,找到feature的节点结构。打印输出feature索引和节点名称。

      current_config_index =
    vec_elt (cm[feature_arc].config_index_by_sw_if_index, sw_if_index);
      cfg_index =
    vec_elt (vcm->config_pool_index_by_user_index, current_config_index);
      cfg = pool_elt_at_index (vcm->config_pool, cfg_index);

      for (i = 0; i < vec_len (cfg->features); i++) {
        feat = cfg->features + i;
        node_index = feat->node_index;
        n = vlib_get_node (vm, node_index);
        if (verbose)
          vlib_cli_output (vm, "  [%2d] %v", feat->feature_index, n->name);
        else
          vlib_cli_output (vm, "  %v", n->name);
      }

ARC的最后一个节点索引保存在end_node_indices_by_user_index的current_config_index索引位置,vlib_get_node根据最后节点索引找到节点结构,打印其名称。

      if (verbose) {
        n = vlib_get_node (vm, vcm->end_node_indices_by_user_index[current_config_index]);
        vlib_cli_output (vm, "  [end] %v", n->name);
      }

如下显示接口features配置:

vpp# show interface features eth0
Feature paths configured on eth0...

nsh-eth-output:
  none configured

arp:
  linux-cp-arp-phy

feature开启关闭

feature开启关闭操作需要指定ARC和Feature的名称,以及要开启的接口索引。如下函数,根据ARC和Feature名称找到ARC索引和feature索引。

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_index = vnet_get_feature_arc_index (arc_name); 
   
  if (arc_index == (u8) ~ 0)
    return VNET_API_ERROR_INVALID_VALUE;
   
  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);                 

在feature_main结构中,根据ARC名称,在哈希arc_index_by_name找到ARC的注册结构,其中保存着ARC的索引feature_arc_index。

vnet_get_feature_arc_index (const char *s)
{
  vnet_feature_main_t *fm = &feature_main;
  vnet_feature_arc_registration_t *reg;
  uword *p;

  p = hash_get_mem (fm->arc_index_by_name, s);
  if (p == 0)
    return ~0;

  reg = uword_to_pointer (p[0], vnet_feature_arc_registration_t *);
  return reg->feature_arc_index;

在feature_main主结构中,根据ARC索引,和feature名称,在哈希next_feature_by_name[arc]中找到feature的注册结构,其中保存着feature的索引feature_index。

vnet_get_feature_index (u8 arc, const char *s)
{
  vnet_feature_main_t *fm = &feature_main;
  vnet_feature_registration_t *reg;
  uword *p;

  if (s == 0) return ~0;

  p = hash_get_mem (fm->next_feature_by_name[arc], s);
  if (p == 0)
    return ~0;

  reg = uword_to_pointer (p[0], vnet_feature_registration_t *);
  return reg->feature_index;

根据ARC索引找到对应配置结构cm,再根据接口索引sw_if_index在config_index_by_sw_if_index找到配置池索引ci,检查一下接口当前开启的feature数量,如果为零,并且当前为disable操作,直接返回,不需要disable了。

vnet_feature_enable_disable_with_index (u8 arc_index, u32 feature_index,
                    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;

  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);
  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添加或者删除feature。将返回的配置池索引ci保存到接口对应的config_index_by_sw_if_index中。递增接口的feature数量。

ARC对应的向量sw_if_index_has_features[arc_index]中保存接口是否开启有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);

  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));
  fm->feature_count_by_sw_if_index[arc_index][sw_if_index] = feature_count;

  vnet_feature_reg_invoke (sw_if_index, arc_index, (feature_count > 0));

如下增加feature函数,如果config_string_heap_index为有效值,据此获得之前添加的vnet_config_t结构old(这里将p进行了减一操作,之后会再次看着这个值进行了加一保存),将其中的features复制一份。

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, end_node_index;
  u32 node_index = vec_elt (cm->node_index_by_feature_index, feature_index);

  if (config_string_heap_index == ~0) {
      old = 0;
      new_features = 0;
      end_node_index = cm->default_end_node_index;
  } else {
      u32 *p = vnet_get_config_heap (cm, config_string_heap_index);
      old = pool_elt_at_index (cm->config_pool, p[-1]);
      new_features = old->features;
      end_node_index = cm->end_node_indices_by_user_index[config_string_heap_index];
      if (new_features)
        new_features = duplicate_feature_vector (new_features);
  }

分配一个新的feature结构vnet_config_feature_t,将要添加的feature索引和节点索引赋值到新feature结构中。如果指定了配置字节,保存到新feature结构中。

  vec_add2 (new_features, f, 1);
  f->feature_index = feature_index;
  f->node_index = node_index;

  if (n_feature_config_bytes) {
      n_feature_config_u32s = round_pow2 (n_feature_config_bytes, sizeof (f->feature_config[0])) / sizeof (f->feature_config[0]);
      vec_validate (f->feature_config, n_feature_config_u32s - 1);
      clib_memcpy_fast (f->feature_config, feature_config, n_feature_config_bytes);
  }

如果new_features向量元素大于1,进行排序。释放旧的vnet_config结构。函数find_config_with_features分配一个新的vnet_config结构new。

  /* Sort (prioritize) features. */
  if (vec_len (new_features) > 1)
    vec_sort_with_function (new_features, feature_cmp);

  if (old)
    remove_reference (cm, old);

  new = find_config_with_features (vm, cm, new_features, end_node_index);
  new->reference_count += 1;

分配config_pool_index_by_user_index索引,将配置池索引进行保存。返回配置池索引值(进行了加一操作)。

  /* 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;
我们先从整体上分析这段代码的用途作用,然后逐步展开并解释宏的具体内容。 --- ### 宏的作用 `VNET_FEATURE_INIT` 是一个用于注册功能模块(features)到 VPP 网络栈的宏。它允许开发者将某个节点(如 `snort-enq` 节点)插入到指定弧线(arc)的工作流中,并可以规定该节点在其他节点之前运行。 在这个例子中: ```c VNET_FEATURE_INIT(snort_enq, static) = { .arc_name = "ip4-unicast", .node_name = "snort-enq", .runs_before = VNET_FEATURES("ip4-lookup"), }; ``` 这条声明的意思是: - 注册名为 `"snort-enq"` 的节点。 - 将其加入到弧线 `"ip4-unicast"` 中。 - 并且保证该节点会在 `"ip4-lookup"` 节点之前运行。 接下来,我们将逐步展开 `VNET_FEATURE_INIT` 宏的内容。 --- ### 展开宏的过程 #### 第一步:定义静态变量 ```c __VA_ARGS__ vnet_feature_registration_t vnet_feat_snort_enq; ``` 这里通过 `__VA_ARGS__` 替换掉传入的第一个参数 `static`,因此等价于: ```c static vnet_feature_registration_t vnet_feat_snort_enq; ``` 这是一个全局范围内的静态变量,类型为 `vnet_feature_registration_t`,它的名字由 `vnet_feat_ + x` 组成(这里是 `vnet_feat_snort_enq`)。 --- #### 第二步:构造函数——添加功能注册表项 ```c static void __vnet_add_feature_registration_snort_enq (void) __attribute__((__constructor__)); ``` 这是一段 **构造函数**,利用 GCC 扩展 `__attribute__((__constructor__))` 实现。这意味着当程序启动时,这段代码会被自动执行一次。 进入函数体内: ```c { vnet_feature_main_t * fm = &feature_main; vnet_feat_snort_enq.next = fm->next_feature; fm->next_feature = &vnet_feat_snort_enq; } ``` 上述代码实现了以下步骤: 1. 获取当前特征管理主结构体指针 `&feature_main`。 2. 把新创建的功能条目 `vnet_feat_snort_enq` 添加到链表头部(即将其作为第一个元素)。 --- #### 第三步:析构函数——移除功能注册表项 ```c static void __vnet_rm_feature_registration_snort_enq (void) __attribute__((__destructor__)); ``` 这是对应的 **析构函数**,同样借助 GCC 扩展 `__attribute__((__destructor__))` 实现。当进程退出时,这段代码会被自动调用。 进入函数体内: ```c { vnet_feature_main_t * fm = &feature_main; vnet_feature_registration_t *r = &vnet_feat_snort_enq; VLIB_REMOVE_FROM_LINKED_LIST(fm->next_feature, r, next); } ``` 上述代码完成了以下任务: 1. 再次获取特征管理主结构体地址 `&feature_main`。 2. 查找需要删除的功能条目 `vnet_feat_snort_enq`。 3. 使用辅助宏 `VLIB_REMOVE_FROM_LINKED_LIST` 将该条目从链表中安全地卸载。 --- #### 最终生成完整的静态变量定义 最后,宏还会补充完成这个静态变量的实际赋值过程: ```c static vnet_feature_registration_t vnet_feat_snort_enq = { .arc_name = "ip4-unicast", /* 弧线名称 */ .node_name = "snort-enq", /* 节点名称 */ .runs_before = {"ip4-lookup"}, /* 需要在 ip4-lookup 之前运行 */ }; ``` 至此,整个宏已经被完整展开! --- ### 总结 经过展开后的结果如下所示: ```c // 全局静态变量 static vnet_feature_registration_t vnet_feat_snort_enq; // 构造函数:注册 feature 到链表头 static void __vnet_add_feature_registration_snort_enq(void) __attribute__((__constructor__)); static void __vnet_add_feature_registration_snort_enq(void) { vnet_feature_main_t *fm = &feature_main; vnet_feat_snort_enq.next = fm->next_feature; fm->next_feature = &vnet_feat_snort_enq; } // 析构函数:从链表中移除已注册的 feature static void __vnet_rm_feature_registration_snort_enq(void) __attribute__((__destructor__)); static void __vnet_rm_feature_registration_snort_enq(void) { vnet_feature_main_t *fm = &feature_main; vnet_feature_registration_t *r = &vnet_feat_snort_enq; VLIB_REMOVE_FROM_LINKED_LIST(fm->next_feature, r, next); } // 初始化静态变量 static vnet_feature_registration_t vnet_feat_snort_enq = { .arc_name = "ip4-unicast", .node_name = "snort-enq", .runs_before = {"ip4-lookup"}, }; ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值