Cisco VPP(7) 插件开发
2016-04-22 10:44
633 查看
还在初学阶段,如果有误,希望多批评指正。
在VPP中,插件可以在程序启动的时候加载,一般我们会往里面加入node,实现一些功能。
首先介绍怎么直接重定义硬件接口RX到我们的node
这个接口主要是将指定的接口重定向到我们自己实现的node上。
具体的实现我们可以看sample-plugin,或者看以下简单的例子:
这是注册node的代码,里面主要是function、name和next_nodes
以上是node.c的基本展示。
下面介绍下插件注册时需要的一些函数
首先是注册初始化函数
以上是直接重定向RX的操作。
另外还有需要插入node连接其他node时候,VPP提供了几个常用接口:
资料来源:https://fd.io/
欢迎加入VPP讨论群:417538415
在VPP中,插件可以在程序启动的时候加载,一般我们会往里面加入node,实现一些功能。
首先介绍怎么直接重定义硬件接口RX到我们的node
vnet_hw_interface_rx_redirect_to_node(vnet_main,hw_if_index, my_graph_node.index /* redirect to my_graph_node */);
这个接口主要是将指定的接口重定向到我们自己实现的node上。
具体的实现我们可以看sample-plugin,或者看以下简单的例子:
这是注册node的代码,里面主要是function、name和next_nodes
//注册node VLIB_REGISTER_NODE(my_node) = { .function = my_node_fn, //node功能函数 .name = "zzx-test", //node名字,唯一 .vector_size = 4, .format_trace = format_my_trace, //show trace显示 .type = VLIB_NODE_TYPE_INTERNAL, //node类型 .n_errors = ARRAY_LEN(my_error_strings), .error_strings = my_error_strings, .n_next_nodes = MY_N_NEXT, .next_nodes = { [MY_ETHERNET_INPUT] = "ethernet-input", //下一级node名字 }, };node功能如下,这里是拿到数据包,执行功能,设置下一级的node三个主要功能
static uint64_t my_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { uint32_t *from = vlib_frame_vector_args(frame); uint32_t n_left_from = frame->n_vectors; my_next_t next_index = node->cached_next_index; uint32_t pkts_showed = 0; while(n_left_from > 0) { uint32_t *to_next; uint32_t n_left_to_next; vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next); //n_left_from表示vector拿到多少个数据包 //n_left_to_next表示上次缓存的下一级node剩下的vector位置 //所以循环表示的是当前node还能拿到多少个包,下一级能接收多少包,以少的为准。有0的话就停止执行 while(n_left_from > 0 && n_left_to_next > 0) { uint32_t next0 = MY_ETHERNET_INPUT; uint32_t bi0 = from[0]; to_next[0] = bi0; from += 1; to_next += 1; n_left_from -= 1; n_left_to_next -= 1; //拿到数据包 vlib_buffer_t *b0 = vlib_get_buffer(vm, bi0); //当前node的主要功能是输出数据包内容到终端 void *en0 = vlib_buffer_get_current(b0); show_packet(en0); //将数据包入队给下一级node pkts_showed += 1; vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, bi0, next0); } vlib_put_next_frame(vm, node, next_index, n_left_to_next); } //增加计数器,主要是加在show err命令了,vpp本身的node也是加在那里了 vlib_node_increment_counter(vm, my_node.index, MY_ERROR_SHOW, pkts_showed); return frame->n_vectors; }
以上是node.c的基本展示。
下面介绍下插件注册时需要的一些函数
首先是注册初始化函数
//我们没有什么特别的功能,所以初始化没有做任何事情 static clib_error_t *my_init(vlib_main_t *vm) { return 0; } //此处是我们将初始化函数加载到vpp里面,以供程序运行的时候初始化 VLIB_INIT_FUNCTION(my_init);接下来是必备函数,vpp在加载插件会调用这个函数,传递给我们一些必须的数据
//vm里面记录了我们需要的大部分东西,好多接口里面都会调用 //vnet_main是我们连接其他node或者网络相关的数据 //这里因为运行的比较早,DPDK硬件初始化还没开始。 //所以我们在连接硬件接口RX的时候,这里不能做,这里最好就是获取一些基本的数据 clib_error_t *vlib_plugin_register(vlib_main_t *vm, vnet_plugin_handoff_t *h, int32_t from_early_init) { my_main_t *mm = &my_main; mm->vlib_main = vm; mm->vnet_main = h->vnet_main; mm->ethernet_main = h->ethernet_main; return NULL; }因为如上说到硬件还没初始化,所以我们不能在这里直接去重定向硬件RX,但是其他地方也没有合适的接口被调用,所以我们考虑采用命令行去配置需要重定向的接口,命令行的支持如下:
//命令行注册,path表示的是命令,function是执行函数 VLIB_CLI_COMMAND(show_packet_command, static) = { .path = "show packet", .short_help = "show packet <interface name> [disable]", .function = my_command_enable_disable_fn, };
static clib_error_t *my_command_enable_disable_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) { my_main_t *mm = &my_main; int32_t enable_disable = 1; uint32_t sw_if_index = UINT32_MAX; while(unformat_check_input(input) != UNFORMAT_END_OF_INPUT) { //判定是开还是关 if(unformat(input, "disable")) enable_disable = 0; //获取需要重定向的RX else if(unformat(input, "%U", unformat_vnet_sw_interface, mm->vnet_main, &sw_if_index)) ; else break; } if(sw_if_index == UINT32_MAX) return clib_error_return(0, "Please specify an interface..."); int32_t rv = my_show_packet_enable_disable(mm, sw_if_index, enable_disable); switch(rv) { case 0: break; case VNET_API_ERROR_INVALID_SW_IF_INDEX: return clib_error_return(0, "Invalid interface, only works on physical ports"); break; case VNET_API_ERROR_UNIMPLEMENTED: return clib_error_return(0, "Device driver doesn't support redirection"); break; default: return clib_error_return(0, "%s returned %d", __FUNCTION__, rv); } return NULL; }
int32_t my_show_packet_enable_disable(my_main_t *mm, uint32_t sw_if_index, int32_t enable_disable) { //释放sw index if(pool_is_free_index(mm->vnet_main->interface_main.sw_interfaces, sw_if_index)) return VNET_API_ERROR_INVALID_SW_IF_INDEX; //获取RX的结构 vnet_sw_interface_t *sw = vnet_get_sw_interface(mm->vnet_main, sw_if_index); if(sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE) { return VNET_API_ERROR_INVALID_SW_IF_INDEX; } //node_index是my_node.index时是重定向到我的node,否则~0表示关闭该RX的重定向 uint32_t node_index = enable_disable ? my_node.index : UINT32_MAX; int32_t rv = vnet_hw_interface_rx_redirect_to_node(mm->vnet_main, sw_if_index, node_index); return rv; }
以上是直接重定向RX的操作。
另外还有需要插入node连接其他node时候,VPP提供了几个常用接口:
ethernet_register_input_type (vm, ETHERNET_TYPE_ARP, arp_input_node.index); 这个是ARP从ethernet-input node注册ARP协议的下一级node,还有ipv4等等也是这样注册,我们可以注册其他的协议。
ip4_register_protocol (IP_PROTOCOL_UDP, udp4_input_node.index); 这个是从ip-local node注册4层协议的下一级node,其他协议比如tcp也可以涨注册。 ip6_register_protocol (IP_PROTOCOL_L2TP, l2t_decap_node.index);
udp_register_dst_port (vm, UDP_DST_PORT_vxlan, vxlan_input_node.index, 1 /* is_ip4 */);
资料来源:https://fd.io/
欢迎加入VPP讨论群:417538415
相关文章推荐
- Linux socket 初步
- Android Manifest 用法
- Linux Kernel 4.0 RC5 发布!
- 什么是 GraphQL?
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- Spark RDD API详解(一) Map和Reduce
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制