vpp中plugin的api编程

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这个结构体通信了。
具体说一下每个部分

关键字

  • 关键字 autoreply
    在这里需要先提一下reply
    正常情况下,vat发送一个请求消息,然后等待一个reply消息。所以xxx_reply_t结构是不可少的,可以自己写,也可自动生成。
    而这个关键字表示了自动生成xxx_reply_t结构体,但是自动生成的结构体只有默认的参数_vl_msg_id,context,retval。如上所示vl_api_acl_del_reply_t。
    这个转换的函数实现如下。

    void autoreply (void *np_arg)
    {
    static u8 *s;
    node_t *np = (node_t *)np_arg;
    int i;
    
    vec_reset_length (s);
    
    s = format (0, " define %s_reply\n", (char *)(np->data[0]));
    s = format (s, "{\n");
    s = format (s, "    u32 context;\n");
    s = format (s, "    i32 retval;\n");
    s = format (s, "};\n");
    
    for (i = 0; i < vec_len (s); i++)
        clib_fifo_add1 (push_input_fifo, s[i]);
    }
  • 关键字manual_print
    xxx_print函数是用来打印消息结构体内容的。默认情况下会自动生成。如果你想自己来实现,就需要加入这个关键字,然后在模块路径下的manual_fns.h中实现。
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 }
  • 关键字define
    define 关键字是转化的关键。每个定义都要加上。
  • 其他关键字
    还有一些其他的关键字,大家对比一下定义与生成的结果也基本都能看出来,在这里就不赘述了。

名称

这个结构体的名称为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
这两个函数是配合使用的,来一个一个看

  • api_acl_del
    这个函数是与cli直接关联,命令输入后就调用的就是这个函数.
 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);
  • vl_api_acl_del_reply_t_handler
    这个函数是在vpp回复消息后,clinet接收回应的函数。
    由于大多数都一样,就直接用宏来实现了。
#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 _
  • 这两个函数的关系
    这两个函数是在不同的线程,在宏M中,是在等待result_ready被置位;而result_ready 就是在xxx_reply_handler中被置位的。

2.3加载函数

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

2.3.1 在宏中添加定义

  • api_acl_del

    在foreach_vpe_api_msg宏下定义
    其实这个在“2.1写cli的help”中已经写过,就不用再写了

/*
 * 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>") \
  • vl_api_acl_del_reply_t_handler
    在foreach_vpe_api_reply_msg宏下定义

    /*
    * Table of message reply handlers, must include boilerplate handlers
    * we just generated
    */
    #define foreach_vpe_api_reply_msg                                       _(ACL_DEL_REPLY, acl_del_reply)

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) 
  • 写函数
    这里的函数也得遵循格式,vl_api_xxx_t_handler,函数如下所示
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);
}
  • 挂钩子
    使用定义的宏来挂载函数。这里也不用做任何的改变。

    /* Set up the API message handling tables */
    static clib_error_t *
    acl_plugin_api_hookup (vlib_main_t * vm)
    {
    acl_main_t *am = &acl_main;
    #define _(N,n)                                                  vl_msg_api_set_handlers((VL_API_##N + am->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_acl_plugin_api_msg;
    #undef _
    
    return 0;
    }

    总结

    这样我们就完成了从vat到vpp的通信。现在回顾一下。
    需要修改3个文件6个步骤。
    acl.api acl_test.c acl.c

    acl.api

    定义通信结构体

    autoreply manual_print define acl_del
    {
    u32 client_index;
    u32 context;
    u32 acl_index;
    };

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)

原文地址:http://blog.51cto.com/zhangchixiang/2128565

时间: 2024-11-13 09:04:12

vpp中plugin的api编程的相关文章

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

【Socket编程】Java中网络相关API的应用

Java中网络相关API的应用 一.InetAddress类 InetAddress类用于标识网络上的硬件资源,表示互联网协议(IP)地址. InetAddress类没有构造方法,所以不能直接new出一个对象: InetAddress类可以通过InetAddress类的静态方法获得InetAddress的对象: 1 InetAddress.getLocalHost();//获取本地对象 2 InetAddress.getByName("");//获取指定名称对象 主要方法使用: 1 /

Maya API编程快速入门

一.Maya API编程简介 Autodesk? Maya? is an open product. This means that anyone outside of Autodesk can change Maya's existing features or add entirely new features. There are several ways you can modify Maya: · MEL?-(Maya Embedded Language) is a powerful

Windows API 编程学习记录&lt;二&gt;

恩,开始写Windows API编程第二节吧. 上次介绍了几个关于Windows API编程最基本的概念,但是如果只是看这些概念,估计还是对Windows API不是很了解.这节我们就使用Windows API 让大家来了解下Windows API的用法. 第一个介绍的Windows API 当然是最经典的MessageBox,这个API 的作用就是在电脑上显示一个对话框,我们先来看看这个API的定义吧: int WINAPI MessageBox(HWND hWnd, LPCTSTR lpTe

Windows API 编程学习记录&lt;三&gt;

恩,开始写API编程的第三节,其实马上要考试了,但是不把这节写完,心里总感觉不舒服啊.写完赶紧去复习啊       在前两节中,我们介绍了Windows API 编程的一些基本概念和一个最基本API函数 MessageBox的使用,在这节中,我们就来正式编写一个Windows的窗口程序. 在具体编写代码之前,我们必须先要了解一下API 编写窗口程序具体的三个基本步骤:             1. 注册窗口类:             2.创建窗口:             3.显示窗口: 恩,

【转】Windows 中不规则窗体的编程实现

一.序言 在绝大多数的Windows应用程序中,其窗体都是使用的正规正矩的矩形窗体,例如我们常用的,“记事本”,“扫雷”,等等.矩形窗体,具有编程实现简单,风格简洁的优点,所以在普通文档应用程序和简单小游戏中使用足矣.但在某些娱乐游戏程序中使用就略显呆板些了,这时若用不规则窗体替代原先的矩形窗体,将会使这类程序更添情趣.典型的例子有windows 自代的Media Player,新版本的Media Player有个控制面板的选项,选中这些面板,播放器就以选中的面板形状出现,这时的播放器比以前版本

Linux统系统开发11 Socket API编程2 多进程 多线程 高并发处理

[本文谢绝转载原文来自http://990487026.blog.51cto.com] <纲要> Linux统系统开发11 Socket API编程2 多进程 多线程 高并发处理 UDP服务器 客户端最小模型,处理字符转大写 TCP 多进程并发服务器模型,为每个客户端开启一个进程: TCP 多线程服务器模型,使用wrap函数封装 作业: ---------------------------------------------------- UDP服务器 客户端最小模型,处理字符转大写 [em

Flink Program Guide (2) -- DataStream API编程指导 -- For Java

v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} 张安 张安 2 1 2016-08-02T10:56:00Z 2016-08-02T10:56:00Z 1 2945 16790 139 39 19696 16.00 false false false false

Linux统系统开发12 Socket API编程3 TCP状态转换 多路IO高并发select poll epoll udp组播 线程池

[本文谢绝转载原文来自http://990487026.blog.51cto.com] Linux统系统开发12 Socket API编程3 TCP状态转换 多路IO高并发select  poll  epoll udp组播 线程池 TCP 11种状态理解: 1,客户端正常发起关闭请求 2,客户端与服务端同时发起关闭请求 3,FIN_WAIT1直接转变TIME_WAIT 4,客户端接收来自服务器的关闭连接请求 多路IO转接服务器: select模型 poll模型 epoll模型 udp组播模型 线