1. plugin_sample.c

在此文件中定义feature和cli

#include <vnet/plugin/plugin.h>
#include <plugin_sample/plugin_sample.h>

plugin_sample_main_t plugin_sample_main;

//开关实现
int plugin_sample_enable_disable(u32 sw_if_index, //index
								int enable_disable)	//开关标识
{
	vnet_sw_interface_t *sw;
	int ret = 0;

	/* Utterly wrong? */
	if (pool_is_free_index (plugin_sample_main.vnet_main->interface_main.sw_interfaces, //vnet_main结构中的interface_main结构中的sw接口
							sw_if_index)) //接口索引
		return VNET_API_ERROR_INVALID_SW_IF_INDEX;
        
	/* Not a physical port? */
	sw = vnet_get_sw_interface(plugin_sample_main.vnet_main,	//vnet_main结构
							sw_if_index);	
	if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
		return VNET_API_ERROR_INVALID_SW_IF_INDEX;

	vnet_feature_enable_disable("ip4-unicast", //挂载节点
								"plugin_sample", 
								sw_if_index, 
								enable_disable, 0, 0);
    
	return ret;
}


static clib_error_t*
plugin_sample_enable_disable_command_fn(vlib_main_t* vm,	//vlib_main结构
                                    unformat_input_t *input,
                                    vlib_cli_command_t *cmd)
{
    u32 sw_if_index = ~0;	//~0 取反= 1
    int enable_disable = 1;

    while(unformat_check_input(input) != UNFORMAT_END_OF_INPUT) //非空则继续输入
	{
        if (unformat(input, "disable"))
            enable_disable = 0;
        else if (unformat(input, "%U",
            unformat_vnet_sw_interface,
            plugin_sample_main.vnet_main, &sw_if_index));
        else
            break;
    }

    if (sw_if_index == ~0)
        return clib_error_return(0, "Please specify an interface...");

    plugin_sample_enable_disable(sw_if_index, enable_disable);	//调用plugin_sample_enable_disable()

    return 0;
}

//注册开关CLI
//指定interface的开关
VLIB_CLI_COMMAND (plugin_sample_command, static) = {
    .path = "plugin sample",
    .short_help = 
    "plugin sample <interface-name> [disable]",
    .function = plugin_sample_enable_disable_command_fn,
};

/*注册插件*****start*****/
VLIB_PLUGIN_REGISTER () = {
    .version = PLUGIN_SAMPLE_PLUGIN_BUILD_VER,
    .description = "Sample of VPP Plugin",
};

static clib_error_t *plugin_sample_init(vlib_main_t* vm)
{
	plugin_sample_main.vnet_main = vnet_get_main();
	return 0;
}

VLIB_INIT_FUNCTION(plugin_sample_init);
/*注册插件*****end*****/


//将node注册在ip4-unicast的arc中,指定ip-lookup之前Hook到数据包
VNET_FEATURE_INIT(plugin_sample, static) = 
{
	.arc_name = "ip4-unicast",
	.node_name = "plugin_sample",
	.runs_before = VNET_FEATURES("ip4-lookup"),
};

2. plugin_sample_node.c

在此文件中定义node和包处理函数

#include <vlib/vlib.h>
#include <vnet/vnet.h>
#include <vnet/pg/pg.h>
#include <vnet/ethernet/ethernet.h>
#include <vppinfra/error.h>
#include <plugin_sample/plugin_sample.h>

typedef enum
{
  PLUGIN_SAMPLE_NEXT_IP4,
  PLUGIN_SAMPLE_DROP,
  PLUGIN_SAMPLE_NEXT_N,
} plugin_sample_next_t;

typedef struct
{
  u32 next_index;
  u32 sw_if_index;
  u8 new_src_mac[6];
  u8 new_dst_mac[6];
} plugin_sample_trace_t;

#define foreach_plugin_sample_error \
_(SHOWED, "show packets processed")

typedef enum
{
#define _(sym,str) SAMPLE_ERROR_##sym,
  foreach_plugin_sample_error
#undef _
    SAMPLE_N_ERROR,
} plugin_sample_error_t;


static char *plugin_sample_error_strings[] = {
#define _(sym, str) str,
        foreach_plugin_sample_error
#undef _
};

extern vlib_node_registration_t plugin_sample_node;

static u8 *
format_plugin_sample_trace (u8 * s, va_list * args)
{
        s = format(s, "To Do!\n");
        return s;
}

//Node处理packet主函数
static uword plugin_sample_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node,
        vlib_frame_t * frame)
{
        u32 n_left_from, *from, *to_next;
        plugin_sample_next_t     next_index;

        from        = vlib_frame_vector_args(frame);
        n_left_from = frame->n_vectors;
        next_index  = node->cached_next_index;

        while(n_left_from > 0){
                u32 n_left_to_next;
				// 从流程中获取包
                vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);

                while(n_left_from > 0 && n_left_to_next > 0){
                        vlib_buffer_t  *b0;
                        u32             bi0, next0 = 0;
                        bi0 = to_next[0] = from[0];
                        from           += 1;
                        to_next        += 1;
                        n_left_to_next -= 1;
                        n_left_from    -= 1;

                        b0 = vlib_get_buffer(vm, bi0);
						//获取到IP header
						void *en0 = vlib_buffer_get_current(b0);
                        int i = 0;
						//打印前20 bytes, 即ip header
                        for (i = 0; i < 20; i++)
                        {
                                printf("%02x ", *(u8*)(en0+i));
                        }
                        printf("\n");
                        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);
        }

        return frame->n_vectors;
}

//注册NODE
//node初始化信息,生成一堆该Node的构造/析构函数
VLIB_REGISTER_NODE (plugin_sample_node) = {
        .name		= "plugin_sample",
        .function       = plugin_sample_node_fn,
        .vector_size    = sizeof(u32),
        .format_trace   = format_plugin_sample_trace,
        .type           = VLIB_NODE_TYPE_INTERNAL,
        .n_errors       = ARRAY_LEN(plugin_sample_error_strings),
        .error_strings  = plugin_sample_error_strings,
        .n_next_nodes   = PLUGIN_SAMPLE_NEXT_N,
        .next_nodes     = {
                [PLUGIN_SAMPLE_NEXT_IP4]    = "ip4-lookup",
                [PLUGIN_SAMPLE_DROP]        = "error-drop",
        },
};


3. 项目地址

https://github.com/workerwork/vpp-plugin-sample