您的位置:首页 > 运维架构 > Linux

Cisco VPP(7) 插件开发

2016-04-22 10:44 633 查看
还在初学阶段,如果有误,希望多批评指正。

在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 dpdk vpp 思科 api