(转)vpp feature node使用分析总结(snat)

vpp node框架的分析请参考 http://blog.csdn.net/jqh9804

feature node实现分析请参考 http://blog.csdn.net/jqh9804/article/details/54772764

理解feature模式最好要理解vpp node框架!

在分析vpp代码snat插件时,看到了以feature模式添加节点的方式,于是简单分析了一下feature 节点的使用

ARC (Argonaut RISC Core) 

    我把一个feature集合(arc)叫做feature类,这只是一个名称而已,一个feature类需要初始化,需要指明开始和结束节点,然后我们就可以在开始节点和结束节点之间添加我们自己的业务节点。现在要以feature节点模式添加节点,如不改动vpp源码,就只能添加到vpp现有的feature类里,下面我主要介绍ip4-unicast类,vpp还有其他feature类。

    现在时间是20161216,vpp代码是现在最新的的master 代码!

1、feature类的注册

文件:ip4_forward.c

/* Built-in ip4 unicast rx feature path definition */
/* *INDENT-OFF* */
VNET_FEATURE_ARC_INIT (ip4_unicast, static) =
{
  .arc_name = "ip4-unicast",          /*arc 类 名字*/
  .start_nodes = VNET_FEATURES ("ip4-input", "ip4-input-no-checksum"),  /*开始node*/
  .end_node = "ip4-lookup",/*结束node*/
  .arc_index_ptr = &ip4_main.lookup_main.ucast_feature_arc_index,
};

    这个宏里的构造函数(在源码里找到这个宏的全部代码)会在main执行前执行,大体意思就是初始化ip4-unicast类的feature并添加到vnet_feature_main_t feature_main->next_feature的链表上,这是vpp固定代码里实现的,ip4-unicast初始化后,我们就可以在ip4-unicast的开始node和结束node之间添加我们自己的node节点,我在测试时,关注的是结束node ip4-lookup,

文件:ip4_input.c

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (ip4_input_node) = {
  .function = ip4_input,
  .name = "ip4-input",
  .vector_size = sizeof (u32),
  .n_errors = IP4_N_ERROR,
  .error_strings = ip4_error_strings,
  .n_next_nodes = IP4_INPUT_N_NEXT,
  .next_nodes = {
    [IP4_INPUT_NEXT_DROP] = "error-drop",
    [IP4_INPUT_NEXT_PUNT] = "error-punt",
    [IP4_INPUT_NEXT_LOOKUP] = "ip4-lookup",
    [IP4_INPUT_NEXT_LOOKUP_MULTICAST] = "ip4-lookup-multicast",
    [IP4_INPUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
  },

  .format_buffer = format_ip4_header,
  .format_trace = format_ip4_input_trace,
};

节点通过函数  .function执行决定下一个节点是.next_nodes中的哪一个,然后通过名字关联下一个节点。当处理流程到ip4-input节点时执行ip4_input函数:ip4_input->ip4_input_inline->vnet_feature_arc_start (arc0, sw_if_index0, &next0, p0);vnet_feature_arc_start函数从ip4-input所在的ip4-unicast类feature里找到优先级最高的feature 节点并执行。

2、feature节点注册

以snat里的snat-out2in节点为例

2.1、首先是节点注册

文件:out2in.c

VLIB_REGISTER_NODE (snat_out2in_node) = {
  .function = snat_out2in_node_fn,
  .name = "snat-out2in",
  .vector_size = sizeof (u32),
  .format_trace = format_snat_out2in_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,
  .n_errors = ARRAY_LEN(snat_out2in_error_strings),
  .error_strings = snat_out2in_error_strings,
  .runtime_data_bytes = sizeof (snat_runtime_t),
  .n_next_nodes = SNAT_OUT2IN_N_NEXT,
  /* edit / add dispositions here */
  .next_nodes = {
    [SNAT_OUT2IN_NEXT_DROP] = "error-drop",
    [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup",
  },
};

 这个只是将节点注册到vpp的节点管理链表里,并不会产生关联关系。

2.2、feature节点关联

文件:snat.c

VNET_FEATURE_INIT (ip4_snat_out2in, static) = {
  .arc_name = "ip4-unicast",
  .node_name = "snat-out2in",
  .runs_before = VNET_FEATURES ("ip4-lookup"),
};

VNET_FEATURE_INIT的执行会将snat-out2in节点添加到ip4-unicast类的feature链表里,.runs_before指定snat-out2in在ip4-lookup前执行,这个关联就是在给节点设置优先级,排在越前越先执行(已经端口使能了)。

2.3、指定端口使能
    但feature节点真正生效还需要在指定端口(网络接口)上使能(注意vnet_feature_arc_start (arc0, sw_if_index0, &next0, p0)函数的第2个参数是端口索引)。

vnet_feature_enable_disable (arc_name, feature_name, sw_if_index, !is_del, 0, 0)
函数就是在指定 sw_if_index端口上添加arc_name feature类的feature_name feature节点。

snat里,假设使能索引是1的端口:

vnet_feature_enable_disable ("ip4-unicast", "snat-out2in",  1,  !is_del, 0, 0)
的意思就是在端口1上的ip4-unicast feature类里添加feature节点snat-out2in。

到现在为止,一个feature节点才正式注册成功。

在feature节点关联处理时,还有一个.runs_after,源码里没有用到,暂时没分析,估计是想注册到指定feature类里某个节点之后,我测试了一下在ip4-lookup后添加节点,没有成功。

3、snat分析

    vpp在实现snat时,他是把in2out节点注册到内网进口ip4-lookup节点前面的,也就是只要是内网口的报文,无条件把源ip修改成地址池里的出口ip,这就会导致内网到内网的数据包也会修改。out2in节点注册到外网口的ip4-lookup前面的,这个没什么问题。个人觉得应该把in2out节点注册到外网口的ip4-lookup后面,所有处理只是针对外网口经过的数据包。

    那么问题来了,怎么把in2out节点注册到ip4-lookup后面呢?最笨的办法就是在ip4-lookup函数里在查找路由代码前后写死,把out2in写死到路由代码前,把in2out写死到路由代码后,功能能实现,但这种办法要修改源码,不利于以后开发。我测试发现把in2out节点直接添加到ip4-unicast feature类的ip4-lookup节点后,并没有成功,虽然show vlib graph里的显示的关联图中ip-lookup节点后确实有in2out节点,但并没有执行。代码实现中,p4-lookup已经是最后一个节点,那他后面的节点就不会执行了。

 经过测试分析,只要数据包正常经过ip4-lookup节点,下一节点肯定是ip4-rewrite节点,并且ip4-rewrite存在与ip4-output feature类中:

文件:ip4_forward.c

/* Source and port-range check ip4 tx feature path definition */
VNET_FEATURE_ARC_INIT (ip4_output, static) =
{
  .arc_name = "ip4-output",
  .start_nodes = VNET_FEATURES ("ip4-rewrite", "ip4-midchain"),
  .end_node = "interface-output",
  .arc_index_ptr = &ip4_main.lookup_main.output_feature_arc_index,
};

那是不是可以把in2out节点加到ip4-output feature类的ip4-rewrite节点后呢,答案是可以的,但有几个问题得注意:

3.1、问题1

    在in2out节点的处理函数调用snat_in2out_node_fn_inline中会取出数据包,

          ip0 = vlib_buffer_get_current (b0) ;
          udp0 = ip4_next_header (ip0);
          tcp0 = (tcp_header_t *) udp0;
          icmp0 = (icmp46_header_t *) udp0;

vlib_buffer_get_current取出的数据报文已经是从mac头开始的,而不是ip4-unicast feature类里从ip报文头开始,所以在取出报文时得

  ip0 = vlib_buffer_get_current (b0) + 14;

这样才能正确解析报文,当然修改b0->current_data也行。

3.2、问题2

在snat代码里,多个地方会修改数据报文的出口索引

vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index;

这就会导致出口有问题,我在测试时注释掉这个,测试成功,但具体怎么修改,要根据自己的需求定制了。

3.4、测试结果:

猜你喜欢

转载自blog.csdn.net/armlinuxww/article/details/94735083