vpp插件-flowtable plugin

本专栏知识点是通过零声教育的线上课学习,进行梳理总结写下文章,对c/c++linux课程感兴趣的读者,可以点击链接https://xxetb.xetslk.com/s/12PH1r C/C++后台高级服务器课程介绍 详细查看课程的服务

flowtable的plugin的运行流程

plugin的基本流程

flowtable plugin代码实现

flowtable plugin的初始化

注册节点

在vpp终端输入:show vlib graph

flowtable的get flowinfo的实现

//当node执行到这个回调函数的时候,vpp数据其实已经在内存中了,这个数据是在vlib_buffer_t中存储的
//参数1--vm:当前处理node的链表头;参数3--frame:内存中要处理的数据
static uword flowtable_getinfo (struct vlib_main_t * vm,
				      struct vlib_node_runtime_t * node, struct vlib_frame_t * frame) {

	u32 n_left_from, *from, *to_next;
    u32 next_index  = node->cached_next_index;

    from        = vlib_frame_vector_args(frame);//from指针指向的内存中可能会含有多个mbuf(vpp数据)
	//一个vector就是一帧数据,参数frame是一个数组,数组的每一个元素就代表一帧数据
    n_left_from = frame->n_vectors;

    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);
                    
					ip4_header_t *ip0 = vlib_buffer_get_current(b0);
					ip4_address_t ip_src = ip0->src_address;
					ip4_address_t ip_dst = ip0->dst_address;

					//数据报文中包含的ip地址和端口号信息只需要解析一次
					u32 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];

					struct in_addr addr;
					addr.s_addr = ip_src.as_u32;
					printf("sw_if_index0: %d, ip_src: %s ", sw_if_index0, inet_ntoa(addr));
					
					addr.s_addr = ip_dst.as_u32;
					printf(" ip_dst: %s \n", inet_ntoa(addr));
/*
					if (ip_src.as_u32 % 2) {
						next0 = FT_NEXT_LOAD_BALANCER;
					}
                  */ 
                    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

}

static char *flowtable_error_strings[] = {
#define _(sym,string) string,
  foreach_flowtable_error
#undef _
};


VLIB_REGISTER_NODE(flowtable_node) = {

	.function = flowtable_getinfo,//执行node时候的回调函数
	.name = "flow-table",
	.vector_size = sizeof(u32),
	.format_trace = format_flowtable_getinfo,//调试追踪
	.type = VLIB_NODE_TYPE_INTERNAL,//当前节点是个中间节点
	.n_errors = FLOWTABLE_N_ERROR,
	.error_strings = flowtable_error_strings,
	.n_next_nodes = FT_NEXT_N_NEXT,
	.next_nodes = { //node的名字如ip4-lookup等都是固定的
		[FT_NEXT_IP4]    = "ip4-lookup",
		[FT_NEXT_DROP] = "error-drop",//返回值error决定了node的执行流
		[FT_NEXT_ETHERNET_INPUT] = "ethernet-input",
		[FT_NEXT_LOAD_BALANCER] = "load-balance",
		[FT_NEXT_INTERFACE_OUTPUT] = "interface-output",
	}
};

vlib_buffer_t结构体

/* VLIB buffer representation. */
typedef struct
{
  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
  STRUCT_MARK (template_start);
  /* Offset within data[] that we are currently processing.
     If negative current header points into predata area. */
  i16 current_data;  /**< signed offset in data[], pre_data[]
                        that we are currently processing.
                        If negative current header points into predata area.
                     */
  u16 current_length;  /**< Nbytes between current data and
                          the end of this buffer.
                       */
  u32 flags; /**< buffer flags:
                <br> VLIB_BUFFER_FREE_LIST_INDEX_MASK: bits used to store free list index,
                <br> VLIB_BUFFER_IS_TRACED: trace this buffer.
                <br> VLIB_BUFFER_NEXT_PRESENT: this is a multi-chunk buffer.
                <br> VLIB_BUFFER_TOTAL_LENGTH_VALID: as it says
                <br> VLIB_BUFFER_REPL_FAIL: packet replication failure
                <br> VLIB_BUFFER_RECYCLE: as it says
                <br> VLIB_BUFFER_FLOW_REPORT: buffer is a flow report,
                <br> VLIB_BUFFER_EXT_HDR_VALID: buffer contains valid external buffer manager header,
                set to avoid adding it to a flow report
                <br> VLIB_BUFFER_FLAG_USER(n): user-defined bit N
             */

/* any change to the following line requres update of
 * vlib_buffer_get_free_list_index(...) and
 * vlib_buffer_set_free_list_index(...) functions */
#define VLIB_BUFFER_FREE_LIST_INDEX_MASK ((1 << 5) - 1)

#define VLIB_BUFFER_IS_TRACED (1 << 5)
#define VLIB_BUFFER_LOG2_NEXT_PRESENT (6)
#define VLIB_BUFFER_NEXT_PRESENT (1 << VLIB_BUFFER_LOG2_NEXT_PRESENT)
#define VLIB_BUFFER_IS_RECYCLED (1 << 7)
#define VLIB_BUFFER_TOTAL_LENGTH_VALID (1 << 8)
#define VLIB_BUFFER_REPL_FAIL (1 << 9)
#define VLIB_BUFFER_RECYCLE (1 << 10)
#define VLIB_BUFFER_FLOW_REPORT (1 << 11)
#define VLIB_BUFFER_EXT_HDR_VALID (1 << 12)

  /* User defined buffer flags. */
#define LOG2_VLIB_BUFFER_FLAG_USER(n) (32 - (n))
#define VLIB_BUFFER_FLAG_USER(n) (1 << LOG2_VLIB_BUFFER_FLAG_USER(n))

    STRUCT_MARK (template_end);

  u32 next_buffer;   /**< Next buffer for this linked-list of buffers.
                        Only valid if VLIB_BUFFER_NEXT_PRESENT flag is set.
                     */

  vlib_error_t error;	/**< Error code for buffers to be enqueued
                           to error handler.
                        */
  u32 current_config_index; /**< Used by feature subgraph arcs to
                               visit enabled feature nodes
                            */

  u8 feature_arc_index;	/**< Used to identify feature arcs by intermediate
                           feature node
                        */

  u8 n_add_refs; /**< Number of additional references to this buffer. */

  u8 buffer_pool_index;	/**< index of buffer pool this buffer belongs. */
  u8 dont_waste_me[1]; /**< Available space in the (precious)
                          first 32 octets of buffer metadata
                          Before allocating any of it, discussion required!
                       */

  u32 opaque[10]; /**< Opaque data used by sub-graphs for their own purposes.
                    See .../vnet/vnet/buffer.h
                 */
    CLIB_CACHE_LINE_ALIGN_MARK (cacheline1);

  u32 trace_index; /**< Specifies index into trace buffer
                      if VLIB_PACKET_IS_TRACED flag is set.
                   */
  u32 recycle_count; /**< Used by L2 path recycle code */

  u32 total_length_not_including_first_buffer;
  /**< Only valid for first buffer in chain. Current length plus
     total length given here give total number of bytes in buffer chain.
  */
  u32 align_pad; /**< available */
  u32 opaque2[12];  /**< More opaque data, see ../vnet/vnet/buffer.h */

  /***** end of second cache line */
    CLIB_CACHE_LINE_ALIGN_MARK (cacheline2);
  u8 pre_data[VLIB_BUFFER_PRE_DATA_SIZE];  /**< Space for inserting data
                                               before buffer start.
                                               Packet rewrite string will be
                                               rewritten backwards and may extend
                                               back before buffer->data[0].
                                               Must come directly before packet data.
                                            */

  u8 data[0]; /**< Packet data. Hardware DMA here */
} vlib_buffer_t;		/* Must be a multiple of 64B. */

vlib_frame_t与mbuf的关系

flowtable的loadbalancer的框架

命令的初始化

node handler的实现

命令的注册

节点回调函数的实现

//node handler
static uword load_balance (vlib_main_t *vm,
	vlib_node_runtime_t *node, vlib_frame_t *frame) {

	//vlib_frame_vector_args从上一帧frame到下一帧frame的参数都有哪些
	u32 *to_next, *from = vlib_frame_vector_args(frame);
	u32 n_left_from = frame->n_vectors;
	u32 next_index = node->cached_next_index;

	u32 pkts_processed = 0;
	loadbalancer_runtime_t *rt = vlib_node_get_runtime_data(vm, loadbalancer_node.index);

	while (n_left_from > 0) {

		u32 pi0;
		u32 next0;
		u32 n_left_to_next;

		//debug("debug:[%s:%s:%d] next_index:%d, n_left_to_next: %d\n", __FILE__, __func__, __LINE__, next_index, n_left_to_next);
		vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
		//debug("debug:[%s:%s:%d] next_index:%d, n_left_to_next: %d\n", __FILE__, __func__, __LINE__, next_index, n_left_to_next);
		
		while (n_left_from > 0 && n_left_to_next > 0) {

			next0 = LB_NEXT_INTERFACE_OUTPUT;
			pi0 = to_next[0] = from[0];

			vlib_buffer_t *b0 = vlib_get_buffer(vm, pi0);

			pkts_processed ++;

			flow_info_t *fi = *(flow_info_t**)b0->opaque;

			u32 sw_if_index0 = fi->lb.sw_if_index_current;
			fi->offloaded = 1;
			fi->cached_next_node = FT_NEXT_INTERFACE_OUTPUT;
			fi->lb.sw_if_index_rev = rt->sw_if_index_source;

			rt->last_target_index ++;

			if (rt->last_target_index >= vec_len(rt->sw_if_target)) {
				rt->last_target_index = 0;
			}

			fi->lb.sw_if_index_dst = rt->sw_if_target[rt->last_target_index];

			vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0;

			if (sw_if_index0 == rt->sw_if_index_source) {
				vnet_buffer(b0)->sw_if_index[VLIB_TX] = fi->lb.sw_if_index_rev;
			} else {
				vnet_buffer(b0)->sw_if_index[VLIB_TX] = fi->lb.sw_if_index_dst;
			}

			from ++;
			to_next ++;
			n_left_from --;
			n_left_to_next --;

			if (b0->flags & VLIB_BUFFER_IS_TRACED) {
				lb_trace_t *t = vlib_add_trace(vm, node, b0, sizeof(*t));
				t->sw_if_in = sw_if_index0;
				t->next_index = next0;
				t->sw_if_out = vnet_buffer(b0)->sw_if_index[VLIB_TX];
			}

			vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, pi0, next0);
		}

		//处理完buffer中的数据后,一定要将数据set回buffer中去,这样下一个节点运行的时候也就可以使用这个数据
		vlib_put_next_frame(vm, node, next_index, n_left_to_next);
	}

	vlib_node_increment_counter(vm, loadbalancer_node.index, LOADBALANCER_ERROR_PROCESSED, pkts_processed);

	return frame->n_vectors;
}

static char *loadbalancer_error_strings[] = {
#define _(sym,string) string,
  foreach_loadbalancer_error
#undef _
};

命令的设置

int loadbalancer_set_targets(loadbalancer_main_t *lb, u32 sw_if_index_source, 
		u32 *sw_if_index_targets) {

	u32 *sw_if_index;

	debug("debug:[%s:%s:%d] loadbalancer_node.index:%d\n", __FILE__, __func__, __LINE__, loadbalancer_node.index);
	loadbalancer_runtime_t *rt = vlib_node_get_runtime_data(lb->vlib_main, loadbalancer_node.index);

	debug("debug:[%s:%s:%d] sw_if_index_source:%d\n", __FILE__, __func__, __LINE__, sw_if_index_source);
	
	rt->sw_if_index_source = sw_if_index_source;
	vec_foreach(sw_if_index, sw_if_index_targets) {
		vec_add1(rt->sw_if_target, *sw_if_index);//往vec向量中增加一个节点
	}
	return 0;
}


static clib_error_t *set_lb_target_command_fn (vlib_main_t * vm,
                                     unformat_input_t * input,
                                     vlib_cli_command_t * cmd) {
	flowtable_main_t *fm = &flowtable_main;
	loadbalancer_main_t *lb = &loadbalancer_main;
	
	u32 sw_if_index_source = ~0;
	u32 sw_if_index = ~0;//vpp命令show int中显示的网口的Idx
	u32 *sw_if_index_targets = NULL;
	
	int source = 1;
	vec_alloc(sw_if_index_targets, 10);
	//vec_validate(sw_if_index_targets, 10);

	while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) {
		if (unformat(input, "to")) {
			source = 0;
		} else if (unformat(input, "%U", unformat_vnet_sw_interface, 
			fm->vnet_main, &sw_if_index)) {
			debug("debug:[%s:%s:%d] sw_if_index:%d\n", __FILE__, __func__, __LINE__, sw_if_index);
			if (source) {
				sw_if_index_source = sw_if_index;
			} else {
                //将网卡GigabitEthernetb/0/0 GigabitEthernet13/0/0对应的Idx添加到GigabitEthernet3/0/0
				vec_add1(sw_if_index_targets, sw_if_index);
			} 
		} else break;
	} 

	if (sw_if_index == ~0) {
		return clib_error_return(0, "No Source Interface specified");
	}

	if (vec_len(sw_if_index_targets) <= 0) {
		return clib_error_return(0, "No Target Interface specified");
	}

	int rv = flowtable_enable(fm, sw_if_index_source, 1);
	u32 *v;
	vec_foreach(v, sw_if_index_targets) {
		rv = flowtable_enable(fm, *v, 1);
	}

	
	debug("debug:[%s:%s:%d] rv:%d\n", __FILE__, __func__, __LINE__, rv);

	switch(rv) {
		case 0: break;
		case VNET_API_ERROR_INVALID_SW_IF_INDEX: return clib_error_return(0, "Invalid interface");
		case VNET_API_ERROR_UNIMPLEMENTED: return clib_error_return(0, "Device driver doesn't support redirection"); break;
		default : return clib_error_return(0, "flowtable_enable return %d", rv); break;
	}

	rv = loadbalancer_set_targets(lb, sw_if_index_source, sw_if_index_targets);
	if (rv) {
		return clib_error_return(0, "set interface loadbalancer return %d", rv);
	}
	return 0;
}

//命令的设置
//set interface loadbalanced GigabitEthernet3/0/0 to GigabitEthernetb/0/0 GigabitEthernet13/0/0
VLIB_CLI_COMMAND(set_interface_loadbalanced_command) = {
	.path = "set interface loadbalanced",
	.short_help = "set interface loadbalanced <interface> to <interfaces-list>",
	.function = set_lb_target_command_fn,
};

flowtable流程分析

从flowtable节点到loadbalancer节点的跳转

根据flowtable_getinfo回调函数的返回值,来决定跳转到loadbalancer节点中去:

节点的运行时参数的设置

  • 在一个节点中通过调用vlib_node_get_runtime_data设置运行时参数,在另一个节点中调用vlib_node_get_runtime_data就可以获取到其他节点设置的运行时参数
  • 在loadbalancer节点初始化的时候设置运行时参数

  • 在loadbalancer设置命令的时候设置运行时的参数:

  • 在flowtable的节点中也可以获取到在loadbalancer节点中设置的运行时参数

flowtable完整代码


vppplugins_LTLIBRARIES += flowtable_plugin.la

flowtable_plugin_la_SOURCES = \
	flowtable/flowtable.c \
	flowtable/flowtable_node.c \
	flowtable/loadbalancer.c \
	flowtable/loadbalancer_node.c 

nobase_apiinclude_HEADERS += \
	flowtable/flowtable.h \
	flowtable/loadbalancer.h

#include <vppinfra/error.h>
#include <vnet/vnet.h>
#include <vnet/ip/ip.h>
#include <vppinfra/pool.h>
#include <vppinfra/bihash_8_8.h>



#define foreach_flowtable_error				\
 _(THRU, "Flowtable packets gone thru") \
 _(FLOW_CREATE, "Flowtable packets which created a new flow") \
 _(FLOW_HIT, "Flowtable packets with an existing flow") 


typedef enum {

#define _(sym,str) FLOWTABLE_ERROR_##sym,
	foreach_flowtable_error
#undef _

	FLOWTABLE_N_ERROR

} flowtable_error_t;


typedef enum {

	FT_NEXT_IP4,
	FT_NEXT_DROP,
	FT_NEXT_ETHERNET_INPUT,
	FT_NEXT_LOAD_BALANCER,
	FT_NEXT_INTERFACE_OUTPUT,
	FT_NEXT_N_NEXT
	
} flowtable_next_t;


typedef struct {

	u64 hash;

	u32 cached_next_node;
	u16 offloaded;

	u16 sig_len;

	union {
		struct {
			ip6_address_t src, dst;
			u8 proto;
			u16 src_port, dst_port;
		} ip6;

		struct {
			ip4_address_t src, dst;
			u8 proto;
			u16 src_port, dst_port;
		} ip4;
		u8 data[32];
	} signature;

	u64 last_ts;

	struct {
		u32 straight;
		u32 reverse;
	} packet_stats;

	union {
		struct {

			u32 SYN:1;
			u32 SYN_ACK:1;
			u32 SYN_ACK_ACK:1;

			u32 FIN:1;
			u32 FIN_ACK:1;
			u32 FIN_ACK_ACK:1;

			u32 last_seq_number, last_ack_number;

		} tcp;
		
		struct {

			uword flow_info_ptr;
			u32 sw_if_index_dst;
			u32 sw_if_index_rev;
			u32 sw_if_index_current;
			
		} lb;

		u8 flow_data[CLIB_CACHE_LINE_BYTES];
	} ;

} flow_info_t;


typedef struct {

	flow_info_t *flows;

	BVT(clib_bihash) flows_ht;

	vlib_main_t *vlib_main;
	vnet_main_t *vnet_main;

	u32 ethernet_input_next_index;

} flowtable_main_t;


#define FM_NUM_BUCKETS	4
#define FM_MEMORY_SIZE		(256<<16)



flowtable_main_t flowtable_main;

extern vlib_node_registration_t flowtable_node;

int flowtable_enable(flowtable_main_t *fm, u32 sw_if_index, int enable);




#include "flowtable.h"
#include <vnet/plugin/plugin.h>

#include <arpa/inet.h>


vlib_node_registration_t flowtable_node;


typedef struct {
  u64 hash;
  u32 sw_if_index;
  u32 next_index;
  u32 offloaded;
} flow_trace_t;



static u8 *format_flowtable_getinfo (u8 * s, va_list * args) {

	CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
  	CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
  	flow_trace_t *t = va_arg(*args, flow_trace_t*);

	s = format(s, "FlowInfo - sw_if_index %d, hash = 0x%x, next_index = %d, offload = %d",
			t->sw_if_index,
			t->hash, t->next_index, t->offloaded);

	return s;

}


u64 hash4(ip4_address_t ip_src, ip4_address_t ip_dst, u8 protocol, u16 port_src, u16 port_dst) {

	return ip_src.as_u32 ^ ip_dst.as_u32 ^ protocol ^ port_src ^ port_dst;

}

//当node执行到这个回调函数的时候,vpp数据其实已经在内存中了,这个数据是在vlib_buffer_t中存储的
//参数1--vm:当前处理node的链表头;参数3--frame:内存中要处理的数据
static uword flowtable_getinfo (struct vlib_main_t * vm,
				      struct vlib_node_runtime_t * node, struct vlib_frame_t * frame) {

	u32 n_left_from, *from, *to_next;
    u32 next_index  = node->cached_next_index;

    from        = vlib_frame_vector_args(frame);//from指针指向的内存中可能会含有多个mbuf(vpp数据)
	//一个vector就是一帧数据,参数frame是一个数组,数组的每一个元素就代表一帧数据
    n_left_from = frame->n_vectors;

    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);
                    
					ip4_header_t *ip0 = vlib_buffer_get_current(b0);
					ip4_address_t ip_src = ip0->src_address;
					ip4_address_t ip_dst = ip0->dst_address;

					//数据报文中包含的ip地址和端口号信息只需要解析一次
					u32 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];

					struct in_addr addr;
					addr.s_addr = ip_src.as_u32;
					printf("sw_if_index0: %d, ip_src: %s ", sw_if_index0, inet_ntoa(addr));
					
					addr.s_addr = ip_dst.as_u32;
					printf(" ip_dst: %s \n", inet_ntoa(addr));
/*
					loadbalancer_runtime_t *rt = vlib_node_get_runtime_data(vm, loadbalancer_node.index);
					int target_count = vec_len(rt->sw_if_target);
					if (ip_src.as_u32 % 2) {
						next0 = FT_NEXT_LOAD_BALANCER;
					}
                  */ 
                    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

}

static char *flowtable_error_strings[] = {
#define _(sym,string) string,
  foreach_flowtable_error
#undef _
};


VLIB_REGISTER_NODE(flowtable_node) = {

	.function = flowtable_getinfo,//执行node时候的回调函数
	.name = "flow-table",
	.vector_size = sizeof(u32),
	.format_trace = format_flowtable_getinfo,//调试追踪
	.type = VLIB_NODE_TYPE_INTERNAL,//当前节点是个中间节点
	.n_errors = FLOWTABLE_N_ERROR,
	.error_strings = flowtable_error_strings,
	.n_next_nodes = FT_NEXT_N_NEXT,
	.next_nodes = { //node的名字如ip4-lookup等都是固定的
		[FT_NEXT_IP4]    = "ip4-lookup",
		[FT_NEXT_DROP] = "error-drop",//返回值error决定了node的执行流
		[FT_NEXT_ETHERNET_INPUT] = "ethernet-input",
		[FT_NEXT_LOAD_BALANCER] = "load-balance",
		[FT_NEXT_INTERFACE_OUTPUT] = "interface-output",
	}
};


VLIB_PLUGIN_REGISTER() = {

	.version = "1.0",
	.description = "sample of flowtable",

};

//flowtable插件加载到vpp框架中的时候,会调用flowtable_init这个函数
static clib_error_t *flowtable_init(vlib_main_t *vm) {

	clib_error_t *error = 0;

	flowtable_main_t *fm = &flowtable_main;
	fm->vnet_main = vnet_get_main();
	fm->vlib_main = vm;

	flow_info_t *flow;
	pool_get_aligned(fm->flows, flow, CLIB_CACHE_LINE_BYTES);
	pool_put(fm->flows, flow);

	BV(clib_bihash_init)(&fm->flows_ht, "flow hash table", FM_NUM_BUCKETS, FM_MEMORY_SIZE);

	return error;
}
//每一个node的初始化函数
VLIB_INIT_FUNCTION(flowtable_init);



#include "flowtable.h"
#include <vnet/plugin/plugin.h>



int flowtable_enable(flowtable_main_t *fm, u32 sw_if_index, int enable) {

	u32 node_index = enable ? flowtable_node.index : ~0;
	
	printf("debug:[%s:%s:%d] node_index:%d\n", __FILE__, __func__, __LINE__, flowtable_node.index);
	
	return vnet_hw_interface_rx_redirect_to_node(fm->vnet_main, sw_if_index, node_index);
}


static clib_error_t *flowtable_command_enable_fn (struct vlib_main_t * vm,
		unformat_input_t * input, struct vlib_cli_command_t * cmd) {

	flowtable_main_t *fm = &flowtable_main;

	u32 sw_if_index = ~0;
	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, fm->vnet_main, &sw_if_index))
			;
		else break;
	}

	if (sw_if_index == ~0) {
		return clib_error_return(0, "No Interface specified");
	}

	int rv = flowtable_enable(fm, sw_if_index, enable_disable);
	if (rv) {
		if (rv == VNET_API_ERROR_INVALID_SW_IF_INDEX) {
			return clib_error_return(0, "Invalid interface");
		} else if (rv == VNET_API_ERROR_UNIMPLEMENTED) {
			return clib_error_return (0, "Device driver doesn't support redirection");
		} else {
			return clib_error_return (0, "flowtable_enable_disable returned %d", rv);
		}
	}

	return 0;
}



VLIB_CLI_COMMAND(flowtable_interface_enable_disable_command) = {

	.path = "flowtable",
	.short_help = "flowtable <interface> [disable]",
	.function = flowtable_command_enable_fn,

};



#include "flowtable.h"
#include "loadbalancer.h"
#include <vnet/plugin/plugin.h>


int loadbalancer_set_targets(loadbalancer_main_t *lb, u32 sw_if_index_source, 
		u32 *sw_if_index_targets) {

	u32 *sw_if_index;

	debug("debug:[%s:%s:%d] loadbalancer_node.index:%d\n", __FILE__, __func__, __LINE__, loadbalancer_node.index);
	//获取到的rt就是运行时的参数
	loadbalancer_runtime_t *rt = vlib_node_get_runtime_data(lb->vlib_main, loadbalancer_node.index);

	debug("debug:[%s:%s:%d] sw_if_index_source:%d\n", __FILE__, __func__, __LINE__, sw_if_index_source);
	
	rt->sw_if_index_source = sw_if_index_source;
	vec_foreach(sw_if_index, sw_if_index_targets) {
		vec_add1(rt->sw_if_target, *sw_if_index);//往vec向量中增加一个节点
	}
	return 0;
}


static clib_error_t *set_lb_target_command_fn (vlib_main_t * vm,
                                     unformat_input_t * input,
                                     vlib_cli_command_t * cmd) {
	flowtable_main_t *fm = &flowtable_main;
	loadbalancer_main_t *lb = &loadbalancer_main;
	
	u32 sw_if_index_source = ~0;
	u32 sw_if_index = ~0;//vpp命令show int中显示的网口的Idx
	u32 *sw_if_index_targets = NULL;
	
	int source = 1;
	vec_alloc(sw_if_index_targets, 10);
	//vec_validate(sw_if_index_targets, 10);

	while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) {
		if (unformat(input, "to")) {
			source = 0;
		} else if (unformat(input, "%U", unformat_vnet_sw_interface, 
			fm->vnet_main, &sw_if_index)) {
			debug("debug:[%s:%s:%d] sw_if_index:%d\n", __FILE__, __func__, __LINE__, sw_if_index);
			if (source) {
				sw_if_index_source = sw_if_index;
			} else {
				//将网卡GigabitEthernetb/0/0 GigabitEthernet13/0/0对应的Idx添加到GigabitEthernet3/0/0
				vec_add1(sw_if_index_targets, sw_if_index);
			} 
		} else break;
	} 

	if (sw_if_index == ~0) {
		return clib_error_return(0, "No Source Interface specified");
	}

	if (vec_len(sw_if_index_targets) <= 0) {
		return clib_error_return(0, "No Target Interface specified");
	}

	int rv = flowtable_enable(fm, sw_if_index_source, 1);
	u32 *v;
	vec_foreach(v, sw_if_index_targets) {
		rv = flowtable_enable(fm, *v, 1);
	}

	
	debug("debug:[%s:%s:%d] rv:%d\n", __FILE__, __func__, __LINE__, rv);

	switch(rv) {
		case 0: break;
		case VNET_API_ERROR_INVALID_SW_IF_INDEX: return clib_error_return(0, "Invalid interface");
		case VNET_API_ERROR_UNIMPLEMENTED: return clib_error_return(0, "Device driver doesn't support redirection"); break;
		default : return clib_error_return(0, "flowtable_enable return %d", rv); break;
	}

	rv = loadbalancer_set_targets(lb, sw_if_index_source, sw_if_index_targets);
	if (rv) {
		return clib_error_return(0, "set interface loadbalancer return %d", rv);
	}
	return 0;
}

//命令的设置
//set interface loadbalanced GigabitEthernet3/0/0 to GigabitEthernetb/0/0 GigabitEthernet13/0/0
VLIB_CLI_COMMAND(set_interface_loadbalanced_command) = {
	.path = "set interface loadbalanced",
	.short_help = "set interface loadbalanced <interface> to <interfaces-list>",
	.function = set_lb_target_command_fn,
};



#define debug		printf			

#define foreach_loadbalancer_error  _(PROCESSED, "Loadbalancer packets gone thru") 


typedef enum {

#define _(sym,str) LOADBALANCER_ERROR_##sym,

	foreach_loadbalancer_error
#undef _

	LOADBALANCER_N_ERROR
	
} loadbalancer_error_t;

typedef struct {

	vlib_main_t *vlib_main;
	vnet_main_t *vnet_main;

} loadbalancer_main_t;


typedef struct {

	loadbalancer_main_t *lbm;

	u32 sw_if_index_source;
	u32 *sw_if_target;
	u32 last_target_index;

} loadbalancer_runtime_t;


loadbalancer_main_t loadbalancer_main;


extern vlib_node_registration_t loadbalancer_node;




#include <vppinfra/types.h>
#include "flowtable.h"
#include "loadbalancer.h"


vlib_node_registration_t loadbalancer_node;

typedef enum {
  LB_NEXT_DROP,
  LB_NEXT_INTERFACE_OUTPUT,
  LB_NEXT_N_NEXT
} loadbalancer_next_t;


typedef struct {
  u32 sw_if_in;
  u32 sw_if_out;
  u32 next_index;
} lb_trace_t;


static u8 * format_loadbalance(u8 *s, va_list *args) {
	CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
	CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
	
	lb_trace_t *t = va_arg(*args, lb_trace_t*);

	s = format(s, "LoadBalance - sw_if_in %d, sw_if_out %d, next_index = %d",
			t->sw_if_in, t->sw_if_out, t->next_index);
	
  	return s;

}

//node handler
static uword load_balance (vlib_main_t *vm,
	vlib_node_runtime_t *node, vlib_frame_t *frame) {

	//vlib_frame_vector_args从上一帧frame到下一帧frame的参数都有哪些
	u32 *to_next, *from = vlib_frame_vector_args(frame);
	u32 n_left_from = frame->n_vectors;
	u32 next_index = node->cached_next_index;

	u32 pkts_processed = 0;
	loadbalancer_runtime_t *rt = vlib_node_get_runtime_data(vm, loadbalancer_node.index);

	while (n_left_from > 0) {

		u32 pi0;
		u32 next0;
		u32 n_left_to_next;

		//debug("debug:[%s:%s:%d] next_index:%d, n_left_to_next: %d\n", __FILE__, __func__, __LINE__, next_index, n_left_to_next);
		vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
		//debug("debug:[%s:%s:%d] next_index:%d, n_left_to_next: %d\n", __FILE__, __func__, __LINE__, next_index, n_left_to_next);
		
		while (n_left_from > 0 && n_left_to_next > 0) {

			next0 = LB_NEXT_INTERFACE_OUTPUT;
			pi0 = to_next[0] = from[0];

			vlib_buffer_t *b0 = vlib_get_buffer(vm, pi0);

			pkts_processed ++;

			flow_info_t *fi = *(flow_info_t**)b0->opaque;

			u32 sw_if_index0 = fi->lb.sw_if_index_current;
			fi->offloaded = 1;
			fi->cached_next_node = FT_NEXT_INTERFACE_OUTPUT;
			fi->lb.sw_if_index_rev = rt->sw_if_index_source;

			rt->last_target_index ++;

			if (rt->last_target_index >= vec_len(rt->sw_if_target)) {
				rt->last_target_index = 0;
			}

			fi->lb.sw_if_index_dst = rt->sw_if_target[rt->last_target_index];

			vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0;

			if (sw_if_index0 == rt->sw_if_index_source) {
				vnet_buffer(b0)->sw_if_index[VLIB_TX] = fi->lb.sw_if_index_rev;
			} else {
				vnet_buffer(b0)->sw_if_index[VLIB_TX] = fi->lb.sw_if_index_dst;
			}

			from ++;
			to_next ++;
			n_left_from --;
			n_left_to_next --;

			if (b0->flags & VLIB_BUFFER_IS_TRACED) {
				lb_trace_t *t = vlib_add_trace(vm, node, b0, sizeof(*t));
				t->sw_if_in = sw_if_index0;
				t->next_index = next0;
				t->sw_if_out = vnet_buffer(b0)->sw_if_index[VLIB_TX];
			}

			vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, pi0, next0);
		}

		//处理完buffer中的数据后,一定要将数据set回buffer中去,这样下一个节点运行的时候也就可以使用这个数据
		vlib_put_next_frame(vm, node, next_index, n_left_to_next);
	}

	vlib_node_increment_counter(vm, loadbalancer_node.index, LOADBALANCER_ERROR_PROCESSED, pkts_processed);

	return frame->n_vectors;
}

static char *loadbalancer_error_strings[] = {
#define _(sym,string) string,
  foreach_loadbalancer_error
#undef _
};


VLIB_REGISTER_NODE(loadbalancer_node) = {

	.function = load_balance,
	.name = "load-balance",
	.vector_size = sizeof(u32),
	.format_trace = format_loadbalance,
	.type = VLIB_NODE_TYPE_INTERNAL,
	.runtime_data_bytes = sizeof(loadbalancer_runtime_t),
	.n_errors = LOADBALANCER_N_ERROR,
	.error_strings = loadbalancer_error_strings,
	.n_next_nodes = LB_NEXT_N_NEXT,
	.next_nodes = {
		[LB_NEXT_DROP] = "error-drop",
		[LB_NEXT_INTERFACE_OUTPUT] = "interface-output",
	}
};

//命令初始化的时候对命令的参数进行初始化,loadbalancer_init是在VLIB_REGISTER_NODE执行之后执行的
static clib_error_t *loadbalancer_init(vlib_main_t *vm) {

	loadbalancer_main_t *lbm = &loadbalancer_main;

	//获取每个node的runtime参数
	loadbalancer_runtime_t *rt = vlib_node_get_runtime_data(vm, loadbalancer_node.index);

	rt->lbm = lbm;
	lbm->vlib_main = vm;
	lbm->vnet_main = vnet_get_main();
	
	vec_alloc(rt->sw_if_target, 16);//vec:对数组的操作

	return 0;
}

//命令初始化
VLIB_INIT_FUNCTION(loadbalancer_init);

VPP多网口数据接收与转发

bridgeloadbalancer完整代码实现


vppplugins_LTLIBRARIES += bridgeloadbalancer_plugin.la

bridgeloadbalancer_plugin_la_SOURCES = \
	bridgeloadbalancer/bridge.c	\
	bridgeloadbalancer/bridge_node.c	\	
	bridgeloadbalancer/loadbalancer.c	\
	bridgeloadbalancer/loadbalancer_node.c

nobase_apiinclude_HEADERS	+=	\
	bridgeloadbalancer/bridge.h	\
	bridgeloadbalancer/loadbalancer.h



#include "loadbalancer.h"
#include <vnet/plugin/plugin.h>


int loadbalancer_set_targets(loadbalancer_main_t *lb, u32 sw_if_index_source, 
		u32 *sw_if_index_targets) {

	u32 *sw_if_index;

	debug("debug:[%s:%s:%d] loadbalancer_node.index:%d\n", __FILE__, __func__, __LINE__, loadbalancer_node.index);
	loadbalancer_runtime_t *rt = vlib_node_get_runtime_data(lb->vlib_main, loadbalancer_node.index);

	debug("debug:[%s:%s:%d] sw_if_index_source:%d\n", __FILE__, __func__, __LINE__, sw_if_index_source);
	
	rt->sw_if_index_source = sw_if_index_source;
	vec_foreach(sw_if_index, sw_if_index_targets) {
		vec_add1(rt->sw_if_target, *sw_if_index);
	}
	return 0;
}


static clib_error_t *set_lb_target_command_fn (vlib_main_t * vm,
                                     unformat_input_t * input,
                                     vlib_cli_command_t * cmd) {
	//flowtable_main_t *fm = &flowtable_main;
	loadbalancer_main_t *lb = &loadbalancer_main;
	loadbalancer_runtime_t *rt = vlib_node_get_runtime_data(vm, loadbalancer_node.index);
	//loadbalancer_main_t *lb = rt->lbm;
	
	u32 sw_if_index_source = ~0;
	u32 sw_if_index = ~0;
	u32 *sw_if_index_targets = rt->sw_if_target;
	
	int source = 1;

	debug("debug:[%s:%s:%d] input: %s, vnet_main: %p\n", __FILE__, __func__, __LINE__, 
		input->buffer, lb->vnet_main);

	while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) {
		
		if (unformat(input, "to")) {
			source = 0;
		} else if (unformat(input, "%U", unformat_vnet_sw_interface, 
			lb->vnet_main, &sw_if_index)) {
			debug("debug:[%s:%s:%d] sw_if_index:%d\n", __FILE__, __func__, __LINE__, sw_if_index);
			if (source) {
				sw_if_index_source = sw_if_index;
			} else {
				vec_add1(sw_if_index_targets, sw_if_index);
			} 
		} else break;

		debug("[%s:%s:%d]: token: %s\n", __FILE__, __func__, __LINE__, input->buffer);
	} 

	if (sw_if_index == ~0) {
		return clib_error_return(0, "No Source Interface specified");
	}

	if (vec_len(sw_if_index_targets) <= 0) {
		return clib_error_return(0, "No Target Interface specified");
	}


	int rv = loadbalancer_set_targets(lb, sw_if_index_source, sw_if_index_targets);
	if (rv) {
		return clib_error_return(0, "set interface loadbalancer return %d", rv);
	}
	return 0;
}


VLIB_CLI_COMMAND(set_interface_loadbalanced_command) = {
	.path = "set interface bridge loadbalanced",
	.short_help = "set interface bridge loadbalanced <interface> to <interfaces-list>",
	.function = set_lb_target_command_fn,
};





#include <vppinfra/error.h>
#include <vnet/vnet.h>
#include <vnet/ip/ip.h>
#include <vppinfra/pool.h>
#include <vppinfra/bihash_8_8.h>



#define debug		printf			

#define foreach_loadbalancer_error  _(PROCESSED, "Loadbalancer packets gone thru") 

typedef enum {

#define _(sym,str) LOADBALANCER_ERROR_##sym,

	foreach_loadbalancer_error
#undef _

	LOADBALANCER_N_ERROR
	
} loadbalancer_error_t;

typedef struct {

	vlib_main_t *vlib_main;
	vnet_main_t *vnet_main;

} loadbalancer_main_t;


typedef struct {

	loadbalancer_main_t *lbm;

	u32 sw_if_index_source;
	u32 *sw_if_target;
	u32 last_target_index;

} loadbalancer_runtime_t;


loadbalancer_main_t loadbalancer_main;


vlib_node_registration_t loadbalancer_node; //


#include "loadbalancer.h"

#include <vppinfra/types.h>

#include <flowtable/flowtable.h>

//vlib_node_registration_t loadbalancer_node;

typedef enum {
  LB_NEXT_DROP,
  LB_NEXT_INTERFACE_OUTPUT,
  LB_NEXT_N_NEXT
} loadbalancer_next_t;


typedef struct {
  u32 sw_if_in;
  u32 sw_if_out;
  u32 next_index;
} lb_trace_t;


static u8 * format_loadbalance(u8 *s, va_list *args) {
	CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
	CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
	
	lb_trace_t *t = va_arg(*args, lb_trace_t*);

	s = format(s, "LoadBalance - sw_if_in %d, sw_if_out %d, next_index = %d",
			t->sw_if_in, t->sw_if_out, t->next_index);
	
  	return s;

}


static uword load_balance (vlib_main_t *vm,
	vlib_node_runtime_t *node, vlib_frame_t *frame) {

	u32 *to_next, *from = vlib_frame_vector_args(frame);
	u32 n_left_from = frame->n_vectors;
	u32 next_index = node->cached_next_index;

	u32 pkts_processed = 0;
	loadbalancer_runtime_t *rt = vlib_node_get_runtime_data(vm, loadbalancer_node.index);

	while (n_left_from > 0) {

		u32 pi0;
		u32 next0;
		u32 n_left_to_next;

		//debug("debug:[%s:%s:%d] next_index:%d, n_left_to_next: %d\n", __FILE__, __func__, __LINE__, next_index, n_left_to_next);
		vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
		//debug("debug:[%s:%s:%d] next_index:%d, n_left_to_next: %d\n", __FILE__, __func__, __LINE__, next_index, n_left_to_next);
		
		while (n_left_from > 0 && n_left_to_next > 0) {

			next0 = LB_NEXT_INTERFACE_OUTPUT;
			pi0 = to_next[0] = from[0];

			vlib_buffer_t *b0 = vlib_get_buffer(vm, pi0);

			pkts_processed ++;

			flow_info_t *fi = *(flow_info_t**)b0->opaque;

			u32 sw_if_index0 = fi->lb.sw_if_index_current;
			fi->offloaded = 1;
			fi->cached_next_node = FT_NEXT_INTERFACE_OUTPUT;
			fi->lb.sw_if_index_rev = rt->sw_if_index_source;

			rt->last_target_index ++;

			if (rt->last_target_index >= vec_len(rt->sw_if_target)) {
				rt->last_target_index = 0;
			}

			fi->lb.sw_if_index_dst = rt->sw_if_target[rt->last_target_index];

			vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0;

			if (sw_if_index0 == rt->sw_if_index_source) {
				vnet_buffer(b0)->sw_if_index[VLIB_TX] = fi->lb.sw_if_index_rev;
			} else {
				vnet_buffer(b0)->sw_if_index[VLIB_TX] = fi->lb.sw_if_index_dst;
			}

			from ++;
			to_next ++;
			n_left_from --;
			n_left_to_next --;

			if (b0->flags & VLIB_BUFFER_IS_TRACED) {
				lb_trace_t *t = vlib_add_trace(vm, node, b0, sizeof(*t));
				t->sw_if_in = sw_if_index0;
				t->next_index = next0;
				t->sw_if_out = vnet_buffer(b0)->sw_if_index[VLIB_TX];
			}

			vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, pi0, next0);
		}

		vlib_put_next_frame(vm, node, next_index, n_left_to_next);
	}

	vlib_node_increment_counter(vm, loadbalancer_node.index, LOADBALANCER_ERROR_PROCESSED, pkts_processed);

	return frame->n_vectors;
}

static char *loadbalancer_error_strings[] = {
#define _(sym,string) string,
  foreach_loadbalancer_error
#undef _
};


VLIB_REGISTER_NODE(loadbalancer_node) = {

	.function = load_balance,
	.name = "load-balance",
	.vector_size = sizeof(u32),
	.format_trace = format_loadbalance,
	.type = VLIB_NODE_TYPE_INTERNAL,
	.runtime_data_bytes = sizeof(loadbalancer_runtime_t),
	.n_errors = LOADBALANCER_N_ERROR,
	.error_strings = loadbalancer_error_strings,
	.n_next_nodes = LB_NEXT_N_NEXT,
	.next_nodes = {
		[LB_NEXT_DROP] = "error-drop",
		[LB_NEXT_INTERFACE_OUTPUT] = "interface-output",
	}
};

static clib_error_t *loadbalancer_init(vlib_main_t *vm) {

	loadbalancer_main_t *lbm = &loadbalancer_main;
	loadbalancer_runtime_t *rt = vlib_node_get_runtime_data(vm, loadbalancer_node.index);

	printf("loadbalancer_init\n\n");
	rt->lbm = lbm;
	lbm->vlib_main = vm;
	lbm->vnet_main = vnet_get_main();
	
	vec_alloc(rt->sw_if_target, 16);

	return 0;
}


VLIB_INIT_FUNCTION(loadbalancer_init);
#include <vlib/vlib.h>
#include <vnet/vnet.h>
#include <vnet/pg/pg.h>
#include <vnet/ethernet/ethernet.h>
#include <vppinfra/error.h>
#include <pktdump/pktdump.h>

#include <arpa/inet.h>

typedef enum
{
  CK_SAMPLE_NEXT_IP4,
  CK_SAMPLE_DROP,
  CK_SAMPLE_NEXT_N,
} ck_sample_next_t;

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

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

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


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

extern vlib_node_registration_t ck_sample_node;

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

static uword ck_sample_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node,
        vlib_frame_t * frame)
{
        u32 n_left_from, *from, *to_next;
        ck_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);
                        
						ip4_header_t *ip0 = vlib_buffer_get_current(b0);
                        ip4_address_t ip_src = ip0->src_address;
                        ip4_address_t ip_dst = ip0->dst_address;
                        u32 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];

                        struct in_addr addr;
                        addr.s_addr = ip_src.as_u32;
                        printf("sw_if_index0: %d, ip_src: %s ", sw_if_index0, inet_ntoa(addr));
                        
                        addr.s_addr = ip_dst.as_u32;
                        printf(" ip_dst: %s \n", inet_ntoa(addr));

                        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;
}


VLIB_REGISTER_NODE (bridge_node) = {
        .name		= "bridge-node",
        .function       = ck_sample_node_fn,
        .vector_size    = sizeof(u32),
        .format_trace   = format_ck_sample_trace,
        .type           = VLIB_NODE_TYPE_INTERNAL,
        .n_errors       = ARRAY_LEN(ck_sample_error_strings),
        .error_strings  = ck_sample_error_strings,
        .n_next_nodes   = CK_SAMPLE_NEXT_N,
        .next_nodes     = {
                [CK_SAMPLE_NEXT_IP4]    = "ip4-lookup",
                [CK_SAMPLE_DROP]        = "error-drop",
        },
};
#ifndef __included_ck_sample_h__
#define __included_ck_sample_h__

#include <vnet/vnet.h>
#include <vnet/ip/ip.h>

#include <vppinfra/hash.h>
#include <vppinfra/error.h>
#include <vppinfra/elog.h>

typedef struct {
    /* API message ID base */
    u16 msg_id_base;

    /* convenience */
    vnet_main_t * vnet_main;
} ck_sample_main_t;

extern ck_sample_main_t ck_sample_main;

extern vlib_node_registration_t bridge_node;

#define CK_SAMPLE_PLUGIN_BUILD_VER "1.0"

#endif /* __included_ck_sample_h__ */

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

ck_sample_main_t ck_sample_main;


int ck_sample_enable_disable(u32 sw_if_index, int enable)
{
        if (pool_is_free_index (ck_sample_main.vnet_main->interface_main.sw_interfaces, 
                                sw_if_index))
                return VNET_API_ERROR_INVALID_SW_IF_INDEX;

        vnet_feature_enable_disable("ip4-unicast",
                "ck_sample",
                sw_if_index, enable, 0, 0);
        return 0;
}


static clib_error_t*
ck_sample_enable_disable_command_fn(vlib_main_t* vm,
                                    unformat_input_t *input,
                                    vlib_cli_command_t *cmd)
{
        u32 sw_if_index = ~0;
        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,
                        ck_sample_main.vnet_main, &sw_if_index));
                else
                        break;
        }

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

        ck_sample_enable_disable(sw_if_index, enable_disable);

        return 0;
}

VLIB_CLI_COMMAND (ck_sample_command, static) = {
    .path = "bridge",
    .short_help = 
    "bridge <interface-name> [disable]",
    .function = ck_sample_enable_disable_command_fn,
};


VLIB_PLUGIN_REGISTER () = {
    .version = CK_SAMPLE_PLUGIN_BUILD_VER,
    .description = "Sample of VPP Plugin",
};

static clib_error_t *ck_sample_init(vlib_main_t* vm)
{
	ck_sample_main.vnet_main = vnet_get_main();
	printf("ck_sample_init\n\n");
	return 0;
}

VLIB_INIT_FUNCTION(ck_sample_init);


/*
VNET_FEATURE_INIT(ck_sample, static) = 
{
	.arc_name = "ip4-unicast",
	.node_name = "ck_sample",
	.runs_before = VNET_FEATURES("ip4-lookup"),
};
*/

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值