vpp中plugin的api编程

2023-10-31 13:50
文章标签 编程 api vpp plugin

本文主要是介绍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_MACRO2REPLY_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

 

 

这篇关于vpp中plugin的api编程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/315801

相关文章

使用SpringBoot创建一个RESTful API的详细步骤

《使用SpringBoot创建一个RESTfulAPI的详细步骤》使用Java的SpringBoot创建RESTfulAPI可以满足多种开发场景,它提供了快速开发、易于配置、可扩展、可维护的优点,尤... 目录一、创建 Spring Boot 项目二、创建控制器类(Controller Class)三、运行

C#反射编程之GetConstructor()方法解读

《C#反射编程之GetConstructor()方法解读》C#中Type类的GetConstructor()方法用于获取指定类型的构造函数,该方法有多个重载版本,可以根据不同的参数获取不同特性的构造函... 目录C# GetConstructor()方法有4个重载以GetConstructor(Type[]

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

Go Playground 在线编程环境

For all examples in this and the next chapter, we will use Go Playground. Go Playground represents a web service that can run programs written in Go. It can be opened in a web browser using the follow

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

函数式编程思想

我们经常会用到各种各样的编程思想,例如面向过程、面向对象。不过笔者在该博客简单介绍一下函数式编程思想. 如果对函数式编程思想进行概括,就是f(x) = na(x) , y=uf(x)…至于其他的编程思想,可能是y=a(x)+b(x)+c(x)…,也有可能是y=f(x)=f(x)/a + f(x)/b+f(x)/c… 面向过程的指令式编程 面向过程,简单理解就是y=a(x)+b(x)+c(x)

【LabVIEW学习篇 - 21】:DLL与API的调用

文章目录 DLL与API调用DLLAPIDLL的调用 DLL与API调用 LabVIEW虽然已经足够强大,但不同的语言在不同领域都有着自己的优势,为了强强联合,LabVIEW提供了强大的外部程序接口能力,包括DLL、CIN(C语言接口)、ActiveX、.NET、MATLAB等等。通过DLL可以使用户很方便地调用C、C++、C#、VB等编程语言写的程序以及windows自带的大

Java并发编程之——BlockingQueue(队列)

一、什么是BlockingQueue BlockingQueue即阻塞队列,从阻塞这个词可以看出,在某些情况下对阻塞队列的访问可能会造成阻塞。被阻塞的情况主要有如下两种: 1. 当队列满了的时候进行入队列操作2. 当队列空了的时候进行出队列操作123 因此,当一个线程试图对一个已经满了的队列进行入队列操作时,它将会被阻塞,除非有另一个线程做了出队列操作;同样,当一个线程试图对一个空