//如下初学vpp时的一些资料梳理
相关基础知识
Arc、 feature
//Arc可以看成以第一个startnode开始的一组具有优先级的Node的排序,所以看到arc要先关注startnode是哪个node。
//在xxnode中调用vnet_feature_arc_start方法可以将xxnode的报文传递到这个arc的第一个node
一个feature等价于一个node,用户通过VNET_FEATURE_INIT宏定义自己的feature,指定需要加入哪个arc,以及在arc中的哪个相对位置(指定在某些node前面,和某些node后面
)。每个feature都可以通过外部命令行启用/停止。
vnet_feature_arc_init 会根据前一步的位置配置把属于某arc的无序的feature排序成有序状态。
此时只是排列好顺序,并未插入feature_arc中,也就是不会被调用到。调用vnet_feature_enable_disable即可,此时才是将要用到的feature真正的插入feature_arc中。
现在各个feature node已经连接好了,连接信息也保存到了config中,接下来就是在业务node中使用了。
在需要开始使用feature机制的业务node中调用如下函数即可,注意该业务node必须是arc中的起始node。
static_always_inline void
vnet_feature_arc_start (u8 arc, u32 sw_if_index, u32 * next0,
vlib_buffer_t * b0)
{
vnet_feature_arc_start_with_data (arc, sw_if_index, next0, b0, 0);
}
此函数会将next0修改为下一跳feature。feature的顺序在上文中已经确定。
节点feature的使用
在node的处理函数里调用vnet_feature_arc_start就好了。
通过命令show features可以看到成功加载的feature。
通过命令show interface features xxx,可以看到interface的feature
Arc举例
ip4-output
代码里并没有ip4-output这个节点,它只是一个arc_name。它的起始结点是使用.start_nodes挂上的两个。也就是ip4-rewrite与ip4-midchain。 在ip4-rewrite里跳转的feature
此处多说一句,"ip4-rewrite", "ip4-midchain"两个节点(最终调用的一个函数ip4_rewrite_inline),都有name为ip4-output的feature arc。具体在vnet_feature_init中实现的。
VNET_FEATURE_ARC_INIT (ip4_output, static) =
{
.arc_name = "ip4-output",
.start_nodes = VNET_FEATURES ("ip4-rewrite", "ip4-midchain"),
.arc_index_ptr = &ip4_main.lookup_main.output_feature_arc_index,
};
2.使用
在函数ip4_rewrite_inline中
ip4_rewrite_inline()
{
......
if (PREDICT_TRUE (error0 == IP4_ERROR_NONE))
{
p0->current_data -= rw_len0;
p0->current_length += rw_len0;
"从adj的rewrite取得sw_if_index"
tx_sw_if_index0 = adj0[0].rewrite_header.sw_if_index;
vnet_buffer (p0)->sw_if_index[VLIB_TX] = tx_sw_if_index0;
next0 = adj0[0].rewrite_header.next_index;
if (is_midchain)
{
adj0->sub_type.midchain.fixup_func (vm, adj0, p0);
}
"若adj的VNET_REWRITE_HAS_FEATURES被置位,则跳转到feature节点"
if (PREDICT_FALSE
(adj0[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
vnet_feature_arc_start (lm->output_feature_arc_index,
tx_sw_if_index0, &next0, p0);
}
......
}
范例分析
范例1: 这个和使用vpp框架自动生成的例子一样
参考:VPP之插件框架分析 – 九霄天空-IT技术分享学习 VPP之插件框架分析:
在device-input arc中注册的myplugi feature
/* *这里面对结点myplugin的初始化,让其* */
VNET_FEATURE_INIT (myplugin, static) =
{
.arc_name = "device-input",
.node_name = "myplugin",
.runs_before = VNET_FEATURES ("ethernet-input"),
};
/* *INDENT-ON */
使用vnet_feature_enable_disable ("device-input", "myplugin",sw_if_index, enable_disable, 0, 0);,函数参数是个重点。
VNET_FEATURE_ARC_INIT (device_input, static) =
{
.arc_name = "device-input",
.start_nodes = VNET_FEATURES ("device-input"),
.last_in_arc = "ethernet-input",
.arc_index_ptr = &feature_main.device_input_feature_arc_index,
};
/* *INDENT-OFF* */
VLIB_REGISTER_NODE (device_input_node) = {
.function = device_input_fn,
.name = "device-input",
.runtime_data_bytes = sizeof (vnet_hw_if_rx_node_runtime_t),
.type = VLIB_NODE_TYPE_INPUT,
.state = VLIB_NODE_STATE_DISABLED,
.n_next_nodes = VNET_DEVICE_INPUT_N_NEXT_NODES,
.next_nodes = VNET_DEVICE_INPUT_NEXT_NODES,
};
范例2:
在分析vpp代码snat插件时,看到了以feature模式添加节点的方式,于是简单分析了一下feature 节点的使用
ARC (Argonaut RISC Core)
我把一个feature集合(arc)叫做feature类,这只是一个名称而已,一个feature类需要初始化,需要指明开始和结束节点,然后我们就可以在开始节点和结束节点之间添加我们自己的业务节点。现在要以feature节点模式添加节点,如不改动vpp源码,就只能添加到vpp现有的feature类里,下面我主要介绍ip4-unicast类,vpp还有其他feature类。
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、测试结果:
//后面一个是要把范例的基本处理函数代码搞清楚
另外就是要把上面获取到tcp、udp头的代码搞清楚
后面把下面这三篇好文章搞熟悉就行了:
https://www.cnblogs.com/ouyangxibao/articles/12431083.html
VPP Feature机制分析 | SDNLAB | 专注网络创新技术
https://blog.csdn.net/wh_computers/article/details/121432873
发送报文
- 先从esp加密开始看,看报文怎么到esp4-encrypt这个节点的?
看代码应该是先到ipsec4-output-feature,再由ipsec4-output-feature分发到esp4-encrypt
ipsec4-output-feature 属于ip4-output 这个arc, arc是从startnode开始调度的,ip4_output的startnode是ip4-rewrite与ip4-midchain
//没找到报文从那个node到esp4-encrypt这个node的只能加断点看了
可以看到,在interface-output节点处,程序发现网络帧是ESP协议帧后,将其组成 Packet Vector传递给ipsec-if-output节点,再将其交给esp-encrypt/esp-decrypt做Crypto工作。
1、Ping一个报文出去经过哪些流程?
Ping一个报文从网卡进来经过哪些流程, 从网卡出去经过哪些流程。
报文进来: 1、触发协商 2、加密转发 3、直接转发
IKEV2报文从哪里进来,何时开始协商
明文报文从哪里进来,如何判断是否加密
开启ipsec feature
在ipsec_set_interface_spd()函数中
/* enable IPsec on TX */
vnet_feature_enable_disable ("ip4-output", "ipsec4-output-feature",
sw_if_index, is_add, 0, 0);
ipsec4-input-feature 节点的功能函数就是和范例代码差不多,所以还是要必须看懂的。
加解密引擎
同步异步你说了算:VPP 的异步Crypto框架-CSDN博客
DPDK 加解密引擎:https://www.sdnlab.com/community/article/899