vpp中plugin的api編程(2) - VPE vs. Plugin Messages id

msgid是唯一識別api函數的標識,但是在使用的時候,plugin與vpe有一些區別,需要注意。
在代碼中以VL_API_XXX來顯示,具體的值是在編譯的時候才確定的。

VPE註冊時,使用的是全局宏,模塊中第一個msg id不是0

static clib_error_t *
ipsec_api_hookup (vlib_main_t * vm)
{
  api_main_t *am = &api_main;

#define _(N,n)                                                  \
    vl_msg_api_set_handlers(VL_API_##N, #n,                     \
                           vl_api_##n##_t_handler,              \
                           vl_noop_handler,                     \
                           vl_api_##n##_t_endian,               \
                           vl_api_##n##_t_print,                \
                           sizeof(vl_api_##n##_t), 1);
  foreach_vpe_api_msg;
#undef _

  /*
   * Set up the (msg_name, crc, message-id) table
   */
  setup_message_id_table (am);

  return 0;
}

plugin註冊時,使用的是基址+偏移,模塊中的第一個msg_id是0,必須加上基址才能使用。

static
void acl_vat_api_hookup (vat_main_t *vam)
{
    acl_test_main_t * sm = &acl_test_main;
    /* Hook up handlers for replies from the data plane plug-in */
#define _(N,n)                                                  \
    vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base),     \
                           #n,                                  \
                           vl_api_##n##_t_handler,              \
                           vl_noop_handler,                     \
                           vl_api_##n##_t_endian,               \
                           vl_api_##n##_t_print,                \
                           sizeof(vl_api_##n##_t), 1);
    foreach_vpe_api_reply_msg;
#undef _

}

爲什麼會有這樣的區別,我們來看一下。

plugin的message id

生成偏移

plugin是通過頭文件包含的方式生成了偏移,即 VL_API_XXXX
以acl爲例
是通過這3個.h來實現的。

acl_msg_enum.h  -- 定義枚舉結構,每個模塊都是從0開始的
acl_all_api_h.h   -- 所有acl api相關的.h文件都放在這裏
acl.api.h      -- acl使用的VL_API_XXX按順序排布

  • acl_msg_enum.h
    創建了枚舉結構,包含了acl_all_api_h.h
    
    20 #define vl_msg_id(n,h) n,
    21 typedef enum {
    22 #include <acl/acl_all_api_h.h>
    23     /* We'll want to know how many messages IDs we need... */
    24     VL_MSG_FIRST_AVAILABLE,
    25 } vl_msg_id_t;
    26 #undef vl_msg_id
+ acl_all_api_h.h  
所有acl api有關頭文件都放在這裏,統一管理    
包含了acl.api.h
```c

 16 #include <acl/acl.api.h>
 17 
 18 #ifdef vl_printfun
 19 #include <acl/manual_fns.h>
 20 #endif
  • acl.api.h
    由acl.api生成的文件,按順序排布了VL_API_XXX
    
    18 /****** Message ID / handler enum ******/
    19 
    20 #ifdef vl_msg_id
    21 vl_msg_id(VL_API_ACL_ENABLE_CONFIG, vl_api_acl_enable_config_t_handler)
    22 vl_msg_id(VL_API_ACL_ENABLE_CONFIG_REPLY, vl_api_acl_enable_config_reply_t_handler)
    23 vl_msg_id(VL_API_ACL_PLUGIN_GET_VERSION, vl_api_acl_plugin_get_version_t_handler)
    24 vl_msg_id(VL_API_ACL_PLUGIN_GET_VERSION_REPLY, vl_api_acl_plugin_get_version_reply_t_handler)
    25 vl_msg_id(VL_API_ACL_PLUGIN_CONTROL_PING, vl_api_acl_plugin_control_ping_t_handler)
    26 vl_msg_id(VL_API_ACL_PLUGIN_CONTROL_PING_REPLY, vl_api_acl_plugin_control_ping_reply_t_handler)
    27 /* typeonly: acl_rule */
    28 /* typeonly: macip_acl_rule */
    29 vl_msg_id(VL_API_ACL_ADD_REPLACE, vl_api_acl_add_replace_t_handler)
    30 vl_msg_id(VL_API_ACL_ADD_REPLACE_REPLY, vl_api_acl_add_replace_reply_t_handler)
    31 vl_msg_id(VL_API_ACL_DEL, vl_api_acl_del_t_handler)
    32 vl_msg_id(VL_API_ACL_DEL_REPLY, vl_api_acl_del_reply_t_handler)
    33 vl_msg_id(VL_API_ACL_INTERFACE_ADD_DEL, vl_api_acl_interface_add_del_t_handler)
    34 vl_msg_id(VL_API_ACL_INTERFACE_ADD_DEL_REPLY, vl_api_acl_interface_add_del_reply_t_handler)
    35 vl_msg_id(VL_API_ACL_INTERFACE_SET_ACL_LIST, vl_api_acl_interface_set_acl_list_t_handler)
    ...
    51 #endif
 這樣就生成了模塊內api函數的偏移。

### 獲得基址   
調用vl_msg_api_get_msg_ids函數獲得
```c
u16
vl_msg_api_get_msg_ids (const char *name, int n)
{
  api_main_t *am = &api_main;
  u8 *name_copy;
  vl_api_msg_range_t *rp;
  uword *p;
  u16 rv;

  if (am->msg_range_by_name == 0)
    am->msg_range_by_name = hash_create_string (0, sizeof (uword));

  //獲得乾淨的模塊名(無數數字)
  name_copy = format (0, "%s%c", name, 0);

  //查看是否註冊過
  p = hash_get_mem (am->msg_range_by_name, name_copy);
  if (p)
    {
      clib_warning ("WARNING: duplicate message range registration for '%s'",
            name_copy);
      vec_free (name_copy);
      return ((u16) ~ 0);
    }

  //msg號的範圍判斷
  if (n < 0 || n > 1024)
    {
      clib_warning
    ("WARNING: bad number of message-IDs (%d) requested by '%s'",
     n, name_copy);
      vec_free (name_copy);
      return ((u16) ~ 0);
    }

  //am->msg_ranges這個結構很重要,管理了所有的msg的範圍
  //獲得rp
  vec_add2 (am->msg_ranges, rp, 1);

  //獲取模塊的msg_id。這個id是用全局的first_available_msg_id的
  //每次加載一個模塊後,就會增加響應的個數,這樣就把所有的msg_id串起來了。
  rv = rp->first_msg_id = am->first_available_msg_id;
  //增加全局msg_id個數,供下個模塊使用
  am->first_available_msg_id += n;
  rp->last_msg_id = am->first_available_msg_id - 1;
  rp->name = name_copy;

  //以名字爲key,存入。clinet會用這個名字請求index
  hash_set_mem (am->msg_range_by_name, name_copy, rp - am->msg_ranges);

  return rv;
}

使用

VPE下的message id

生成全局msg_id

VPE也是通過頭文件包含的方式生成的
每個模塊內部的第一個msg_id不是從0排布的,而是全局的,因爲包含了所有的頭文件

以ipsec爲例
是通過這3個.h來實現的。
vnet_msg_enum.h  -- 定義枚舉結構
vnet_all_api_h.h   -- 所有VPE api相關的.h文件都放在這裏
ipsec.api.h      -- ipsec使用的VL_API_XXX按順序排布

  • vnet_msg_enum.h
    定義的枚舉結構,包含了vnet_all_api_h.h
    
    18 #include <vppinfra/byte_order.h>
    19 
    20 #define vl_msg_id(n,h) n,
    21 typedef enum
    22 {
    23   VL_ILLEGAL_MESSAGE_ID = 0,
    24 #include <vnet/vnet_all_api_h.h>
    25   VL_MSG_FIRST_AVAILABLE,
    26 } vl_msg_id_t;
    27 #undef vl_msg_id
    28 
    29 #endif /* included_vnet_msg_enum_h */

+ vnet_all_api_h.h  
所有vnet的模塊都在裏面

```c
28 #ifndef included_from_layer_3
 29 #include <vlibmemory/vl_memory_api_h.h>
 30 #endif /* included_from_layer_3 */
 31 
 32 #include <vnet/devices/af_packet/af_packet.api.h>
 33 #include <vnet/devices/netmap/netmap.api.h>
 34 #include <vnet/devices/virtio/vhost_user.api.h>
 35 #include <vnet/gre/gre.api.h>
 36 #include <vnet/interface.api.h>
 37 #include <vnet/map/map.api.h>
 38 #include <vnet/l2/l2.api.h>
 39 #include <vnet/l2tp/l2tp.api.h>
 40 #include <vnet/span/span.api.h>
 41 #include <vnet/ip/ip.api.h>
 42 #include <vnet/unix/tap.api.h>
 43 #include <vnet/vxlan/vxlan.api.h>
 44 #include <vnet/lldp/lldp.api.h>
 45 #include <vnet/vxlan-gpe/vxlan_gpe.api.h>
 46 #include <vnet/bfd/bfd.api.h>
 47 #include <vnet/ipsec/ipsec.api.h>
 48 #include <vnet/ipsec-gre/ipsec_gre.api.h>
  • ipsec.api.h
    ipsec所需的msg id
    18 /****** Message ID / handler enum ******/
    19 
    20 #ifdef vl_msg_id
    21 vl_msg_id(VL_API_IPSEC_SPD_ADD_DEL, vl_api_ipsec_spd_add_del_t_handler)
    22 vl_msg_id(VL_API_IPSEC_SPD_ADD_DEL_REPLY, vl_api_ipsec_spd_add_del_reply_t_handler)
    23 vl_msg_id(VL_API_IPSEC_INTERFACE_ADD_DEL_SPD, vl_api_ipsec_interface_add_del_spd_t_handler)
    24 vl_msg_id(VL_API_IPSEC_INTERFACE_ADD_DEL_SPD_REPLY, vl_api_ipsec_interface_add_del_spd_reply_t_handler)
    25 vl_msg_id(VL_API_IPSEC_SPD_ADD_DEL_ENTRY, vl_api_ipsec_spd_add_del_entry_t_handler)
    26 vl_msg_id(VL_API_IPSEC_SPD_ADD_DEL_ENTRY_REPLY, vl_api_ipsec_spd_add_del_entry_reply_t_handler)
    27 vl_msg_id(VL_API_IPSEC_SAD_ADD_DEL_ENTRY, vl_api_ipsec_sad_add_del_entry_t_handler)
    28 vl_msg_id(VL_API_IPSEC_SAD_ADD_DEL_ENTRY_REPLY, vl_api_ipsec_sad_add_del_entry_reply_t_handler)
    29 vl_msg_id(VL_API_IPSEC_SA_SET_KEY, vl_api_ipsec_sa_set_key_t_handler)
    30 vl_msg_id(VL_API_IPSEC_SA_SET_KEY_REPLY, vl_api_ipsec_sa_set_key_reply_t_handler)
    31 vl_msg_id(VL_API_IKEV2_PROFILE_ADD_DEL, vl_api_ikev2_profile_add_del_t_handler)
    32 vl_msg_id(VL_API_IKEV2_PROFILE_ADD_DEL_REPLY, vl_api_ikev2_profile_add_del_reply_t_handler)

    這樣所有VPE有關全局的msg id就創建好了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章