vpp中plugin的api编程

时间:2018-06-12 18:33:13   收藏:0   阅读:1097
API简介

vpp其实也有自己的control-plane。它们之间的就是使用API来交互,底层是用的共享内存机制。
control-plane可以是使用不同的语言来写,支持C/python/java/go
在这里了解的是用C语言与vpp通信。如图1所示。VAT通过命令行来控制VPP。

图1,VAT(vpp api test)与vpp通信:
    +---------------------+    
    |                           | 
    | VPP API TEST + 
    |                           |
    +----------+---------+           
                   |                   
binary API  |                   
  (shmem)  |       
                  |   
    +----------+-------+
    |                        |   
    |         VPP        |  
    |                        |   
    +-------------------+

图2是将要用的go与vpp通信的方式,可以看到就是把VAT换成了go的机制。

技术分享图片

了解vat与vpp使用API编程有以下几个意义。
1.了解消息传递的大致原理
2.知道xxx.api的写法
3.知道VPP部分的代码写法
4.在一些情况下可以使用VAT进行调试

步骤

我们通过acl_del这个命令来当例子了解vat与vpp是如何使用api编程的,在vpp_api_test中有这个命令

vat# help acl_del
usage: acl_del <acl-idx>

可以看到,需要在vat中解析一个acl-index,传给vpp,接着vpp会删除这个index的acl,然后告知vat。

添加一个api需要修改三个文件。代码路径是vpp/src/plugins/acl下
acl.api   --  vat 与vpp 通信的结构体定义
acl_test.c  --  vat使用
acl.c    --  vpp使用

我们只需修改3个文件,6个部分就能完成,干货可以直接看==总结==部分

1.acl.api

acl.api中定义vat与vpp通信的结构体,然后由vppapigen文件处理,最终生成acl.api.h的头文件。两边都包含这个头文件,这样vat与vpp就使用了相同的结构体通信了。
我们看一下acl.api中的定义

/** \brief Delete an ACL
    @param client_index - opaque cookie to identify the sender
    @param context - sender context, to match reply w/ request
    @param acl_index - ACL index to delete
*/

autoreply manual_print define acl_del
{
  u32 client_index;  //系统使用
  u32 context;       //系统使用
  u32 acl_index;     //通过命令acl_del <acl-idx>输入的acl-idx
};

这个结构体的定义由3个关键字(autoreply 、manual_print、define )加上名称再加成员构成,最终会被转化为

typedef VL_API_PACKED(struct _vl_api_acl_del {
    u16 _vl_msg_id;      
    u32 client_index;
    u32 context;
    u32 acl_index;
}) vl_api_acl_del_t;

typedef VL_API_PACKED(struct _vl_api_acl_del_reply {
    u16 _vl_msg_id;
    u32 context;
    i32 retval;
}) vl_api_acl_del_reply_t;

这样就可以用使用vl_api_acl_del_t与vl_api_acl_del_reply这个结构体通信了。
具体说一下每个部分

关键字

196 static inline void *
197 vl_api_acl_del_t_print (vl_api_macip_acl_del_t * a, void *handle)
198 {
199   u8 *s;
200 
201   s = format (0, "SCRIPT: acl_del %d ",
202               clib_host_to_net_u32 (a->acl_index));
203 
204   PRINT_S;
205   return handle;
206 }

名称

这个结构体的名称为acl_del,非常重要,最终会使用它生成各种相关的结构体、函数。

结构体成员

见注释。

2.acl_test.c

这个文件是vat使用。
有三件事要做,1.写cli的help 2.写函数 3.函数加载

2.1写cli的help

#define foreach_vpe_api_msg _(acl_del, "<acl-idx>")

2.2写函数

我们需要写两个函数
api_acl_del与vl_api_acl_del_reply_t_handler
这两个函数是配合使用的,来一个一个看

 526 static int api_acl_del (vat_main_t * vam)
 527 {
 528     unformat_input_t * i = vam->input;
         //这个结构体就是在acl.api中定义的消息传递结构体
 529     vl_api_acl_del_t * mp; 
 530     u32 acl_index = ~0;
 531     int ret;
 532     
         //解析字符串,跟vpp的命令行解析一样
 533     if (!unformat (i, "%d", &acl_index)) {
 534       errmsg ("missing acl index\n");
 535       return -99;
 536     }
 537 
         //给mp分配内存,然后填写要传递的值
 538     /* Construct the API message */
 539     M(ACL_DEL, mp);
 540     mp->acl_index = ntohl(acl_index);
 541 
 542     /* send it... */
 543     S(mp);
 544 
 545     /* Wait for a reply... */
 546     W (ret);
 547     return ret;
 548 }

在这里把这几个宏的实现也贴一下。对应一下,就能看明白了。

/* M: construct, but don‘t yet send a message */
#define M(T, mp)                                                do {                                                                vam->result_ready = 0;                                          mp = vl_msg_api_alloc_as_if_client(sizeof(*mp));                memset (mp, 0, sizeof (*mp));                                   mp->_vl_msg_id = ntohs (VL_API_##T+__plugin_msg_base);          mp->client_index = vam->my_client_index;                    } while(0);

/* S: send a message */
#define S(mp) (vl_msg_api_send_shmem (vam->vl_input_queue, (u8 *)&mp))

/* W: wait for results, with timeout */
#define W(ret)                  do {                                                f64 timeout = vat_time_now (vam) + 1.0;         ret = -99;                                                                                      while (vat_time_now (vam) < timeout) {              if (vam->result_ready == 1) {                       ret = vam->retval;                              break;                                      }                                               vat_suspend (vam->vlib_main, 1e-5);         }                                           } while(0);
#define foreach_standard_reply_retval_handler   _(acl_del_reply) 

#define _(n)                                                static void vl_api_##n##_t_handler                      (vl_api_##n##_t * mp)                                   {                                                           vat_main_t * vam = acl_test_main.vat_main;           i32 retval = ntohl(mp->retval);                         if (vam->async_mode) {                                      vam->async_errors += (retval < 0);                  } else {                                                    vam->retval = retval;                                   vam->result_ready = 1;                              }                                                   }
foreach_standard_reply_retval_handler;
#undef _

2.3加载函数

需要把写的两个函数挂载上。只需要在对应的宏下按格式写就好了。

2.3.1 在宏中添加定义

/*
 * List of messages that the api test plugin sends,
 * and that the data plane plugin processes
 */
#define foreach_vpe_api_msg 
_(acl_del, "<acl-idx>") \

2.3.2 函数挂载

上一节提到的宏,都是在acl_vat_api_hookup这个函数中使用的,我们不需要做任何修改。

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 _

    /* API messages we can send */
#define _(n,h) hash_set_mem (vam->function_by_name, #n, api_##n);
    foreach_vpe_api_msg;
#undef _

    /* Help strings */
#define _(n,h) hash_set_mem (vam->help_by_name, #n, h);
    foreach_vpe_api_msg;
#undef _
}

3.acl.c

这个文件是vpp使用,它用来接收vat(vpp-api-test)发送的消息,然后处理,最后回应给vat。
我们需要写对应的函数,然后挂上就可以了

/* List of message types that this plugin understands */

#define foreach_acl_plugin_api_msg      _(ACL_DEL, acl_del) 
static void
vl_api_acl_del_t_handler (vl_api_acl_del_t * mp)
{
  acl_main_t *am = &acl_main;
  //这个结构体就是在acl.api中定义的消息应答传递结构体,用于给VAT发送应答消息
  vl_api_acl_del_reply_t *rmp;
  int rv;

  //mp中就是VAT发送来的结构体,我们可以从中取得配置的acl_index使用。然后调用相应的处理函数。
  rv = acl_del_list (ntohl (mp->acl_index));

  //这里是消息处理完毕后的应答消息,VAT会在那里等待回应。也是通过共享内存的方式来通信。
  //如果需要在回应消息里传递参数,可以使用另一个宏 ---  REPLY_MACRO2
  REPLY_MACRO (VL_API_ACL_DEL_REPLY);
}

acl_test.c

1.定义命令行帮助

#define foreach_vpe_api_msg _(acl_del, "<acl-idx>")

2.实现2个函数

static int api_acl_del (vat_main_t * vam)
static void vl_api_acl_del_reply_t_handler(vl_api_acl_del_t * mp)

3.分别写宏

#define foreach_vpe_api_msg   //此宏与命令行帮助宏共用
_(acl_del, "<acl-idx>")

#define foreach_vpe_api_reply_msg
_(ACL_DEL_REPLY, acl_del_reply)

acl.c

1.实现函数

static void
vl_api_acl_del_t_handler (vl_api_acl_del_t * mp)
{
  acl_main_t *am = &acl_main;
  vl_api_acl_del_reply_t *rmp;
  int rv;

  rv = acl_del_list (ntohl (mp->acl_index));

  REPLY_MACRO (VL_API_ACL_DEL_REPLY);
}

2.写宏

#define foreach_acl_plugin_api_msg      _(ACL_DEL, acl_del)
评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!