前段时间有人在微信群询问通过vlib_frame_t地址,如果获取到报文vlib_buffer_t结构信息。本文以ping回应报文处理流程中节点ip4_icmp_input为例简单分析一下:
下面是ip4_icmp_input节点中获取获取vlib_buffer_t结构的代码简化流程,主要分为2步:1、获取报文缓冲区的索引。2、通过索引获取到vlib_buffer_t结构地址。
static uword
ip4_icmp_input (vlib_main_t * vm,
vlib_node_runtime_t * node, vlib_frame_t * frame)
{
//1、获取报文缓冲区的索引
u32 *from = vlib_frame_vector_args (frame);
//2、通过索引获取到vlib_buffer_t结构地址。
vlib_buffer_t *p0 = vlib_get_buffer (vm, from[0]);
下面通过gdb调试打印vlib_buffer_t结构信息。 1、获取报文缓冲区索引,将vlib_frame_vector_args 函数展开。
/*返回数值等于x四舍五入下一个2的次幂(以pow2对齐)*/
always_inline uword round_pow2 (uword x, uword pow2)
{
return (x + pow2 - 1) & ~(pow2 - 1);
}
/* Byte alignment for vector arguments.
* 16字节对齐*/
#define VLIB_FRAME_VECTOR_ALIGN (1 << 4)
always_inline u32 vlib_frame_vector_byte_offset (u32 scalar_size)
{
return round_pow2 (sizeof (vlib_frame_t) + scalar_size,
VLIB_FRAME_VECTOR_ALIGN);
}
always_inline void * vlib_frame_vector_args (vlib_frame_t * f)
{
return (void *) f + vlib_frame_vector_byte_offset (f->scalar_size);
}
关于f->scalar_size的大小,取决于node节点声明时填充的scalar_size大小,如下面ip4-icmp-input对应node节点全局变量scalar_size未进行赋值也就是0。
VLIB_REGISTER_NODE (ip4_icmp_input_node) = {
.function = ip4_icmp_input,
.name = "ip4-icmp-input",
.vector_size = sizeof (u32),
.format_trace = format_icmp_input_trace,
.n_errors = ARRAY_LEN (icmp_error_strings),
.error_strings = icmp_error_strings,
.n_next_nodes = 1,
.next_nodes = {
[ICMP_INPUT_NEXT_ERROR] = "ip4-punt",
},
};
目前vpp代码中node节点赋值scalar_size,大概用途是用于存储node节点向下一个node节点传递一些私有数据。目前只有接口interface-tx节点中有使用,用于interface-output节点赋值一些私有信息给interface-tx节点有使用。在vnet_register_interface节点函数中对node节点的参数r.scalar_size = sizeof (vnet_hw_if_tx_frame_t)进行了赋值。没有过多研究,感兴趣的可以深入一下代码。 所以我们可以计算出scalar_size为0时,vlib_frame_vector_byte_offset(0)的大小为16.
(gdb) p round_pow2 (sizeof (vlib_frame_t),16)
$3 = 16
所有我们可以查询到from[0]的大小。
(gdb) bt
#0 ip4_icmp_input (vm=0x7fffb68e0680, node=0x7fffb71b0600, frame=0x7fffb7c80400)
at /home/jinsh/workspace/vpp/src/vnet/ip/icmp4.c:150
#1 0x00007ffff6ea41a9 in dispatch_node (vm=0x7fffb68e0680, node=0x7fffb71b0600,
type=VLIB_NODE_TYPE_INTERNAL, dispatch_state=VLIB_NODE_STATE_POLLING,
frame=0x7fffb7c80400, last_time_stamp=172750623838647)
at /home/jinsh/workspace/vpp/src/vlib/main.c:1024
(gdb) p frame[0]
$4 = {frame_flags = 6, flags = 0, scalar_size = 0 '\000', vector_size = 4 '\004',
n_vectors = 1, #表示当前值存储一个报文。
arguments = 0x7fffb7c7fac8 "\376\376\376\376\376\376\376\376\203\272\t"}
//由此我们可以查询到from[0]的大小。
(gdb) p ((u32*)((void *) ((void *)frame + 16)))[0]
$5 = 637571
2、通过索引获取到vlib_buffer_t结构地址。 同样先看一下代码
#define uword_to_pointer(u,type) ((type) (clib_address_t) (u))
always_inline void *
vlib_buffer_ptr_from_index (uword buffer_mem_start, u32 buffer_index,
uword offset)
{
offset += ((uword) buffer_index) << CLIB_LOG2_CACHE_LINE_BYTES;
return uword_to_pointer (buffer_mem_start + offset, vlib_buffer_t *);
}
always_inline vlib_buffer_t *
vlib_get_buffer (vlib_main_t * vm, u32 buffer_index)
{
vlib_buffer_main_t *bm = vm->buffer_main;
vlib_buffer_t *b;
b = vlib_buffer_ptr_from_index (bm->buffer_mem_start, buffer_index, 0);
//验证b是否正确。
vlib_buffer_validate (vm, b);
return b;
}
对应gdb调试代码如下:
(gdb) p (vlib_buffer_t *)((void *)(vm->buffer_main.buffer_mem_start+ ($5 <<6)))
$15 = (vlib_buffer_t *) 0x10026ea0c0
(gdb) p *$15
$16 = {{cacheline0 = 0x10026ea0c0 "\016", current_data = 14, current_length = 96,
flags = 3222798348, flow_id = 0, ref_count = 1 '\001',
buffer_pool_index = 0 '\000', error = 3043, next_buffer = 0, {
current_config_index = 0, punt_reason = 0}, opaque = {1, 4294967295, 917504, 0,
7, 13, 0, 0, 0, 1}, template_end = 0x10026ea100 "",
second_half = 0x10026ea100 "", trace_handle = 0,
total_length_not_including_first_buffer = 0, opaque2 = {0 <repeats 14 times>},
headroom = 0x10026ea140 "", pre_data = '\000' <repeats 127 times>,
data = 0x10026ea1c0 ""}, as_u8x16 = {{14, 0, 96, 0, 12, 0, 24, 192, 0, 0, 0, 0, 1,
0, 227, 11}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 255, 255, 255, 255}, {0, 0, 14,
0, 0, 0, 0, 0, 7, 0, 0, 0, 13, 0, 0, 0}, {0 <repeats 12 times>, 1, 0, 0, 0}}}
另外还有两个宏定义就是通过vlib_buffer_t和rte_mbuf之间的转换
#define rte_mbuf_from_vlib_buffer(x) (((struct rte_mbuf *)x) - 1)
#define vlib_buffer_from_rte_mbuf(x) ((vlib_buffer_t *)(x+1))
我们查询获取到rte_mbuf的地址。
(gdb) p (((struct rte_mbuf *)$15) - 1)
$17 = (struct rte_mbuf *) 0x10026ea040
(gdb) p *(((struct rte_mbuf *)$15) - 1)
$18 = {cacheline0 = 0x10026ea040, buf_addr = 0x10026ea140, buf_iova = 487498048,
rearm_data = 0x10026ea050, data_off = 128, refcnt = 1, nb_segs = 1, port = 0,
ol_flags = 0, rx_descriptor_fields1 = 0x10026ea060, {packet_type = 0, {
l2_type = 0 '\000', l3_type = 0 '\000', l4_type = 0 '\000', tun_type = 0 '\000', {
inner_esp_next_proto = 0 '\000', {inner_l2_type = 0 '\000',
inner_l3_type = 0 '\000'}}, inner_l4_type = 0 '\000'}}, pkt_len = 110,
data_len = 110, vlan_tci = 0, {hash = {rss = 0, fdir = {{{hash = 0, id = 0}, lo = 0},
hi = 0}, sched = {queue_id = 0, traffic_class = 0 '\000', color = 0 '\000',
reserved = 0}, txadapter = {reserved1 = 0, reserved2 = 0, txq = 0}, usr = 0}},
vlan_tci_outer = 0, buf_len = 2176, pool = 0xac021ec80, cacheline1 = 0x10026ea080,
next = 0x0, {tx_offload = 0, {l2_len = 0, l3_len = 0, l4_len = 0, tso_segsz = 0,
outer_l3_len = 0, outer_l2_len = 0}}, shinfo = 0x0, priv_size = 128,
timesync = 0, dynfield1 = {0, 0, 0, 0, 0, 0, 0, 0, 0}}
还有一些比较常用的gdb调试方法,如下:
1、查询vector结构数据的长度。
#define _vec_find(v) ((vec_header_t *) (v) - 1)
#define _vec_len(v) (_vec_find(v)->len)
简化完就是
((vec_header_t *) (v) - 1)->len
2、vlib_buffer_t结构中私有字段opaque参数内容。
#define vnet_buffer(b) ((vnet_buffer_opaque_t *) (b)->opaque)
(gdb) p *((vnet_buffer_opaque_t *) ($15)->opaque)
$21 = {sw_if_index = {1, 4294967295}, l2_hdr_offset = 0, l3_hdr_offset = 14,
l4_hdr_offset = 0, feature_arc_index = 0 '\000', oflags = (unknown: 0), {ip = {
adj_index = {7, 13}, {{flow_hash = 0, {save_protocol = 0, fib_index = 0},
save_rewrite_length = 0 '\000', {rx_sw_if_index = 1, rpf_id = 1}}, icmp = {
type = 0 '\000', code = 0 '\000', data = 0}, reass = {{{next_index = 0,
error_next_index = 0}, {owner_thread_index = 0}}, {{{l4_src_port = 0,
l4_dst_port = 0, tcp_ack_number = 0, save_rewrite_length = 0 '\000',
ip_proto = 0 '\000', icmp_type_or_tcp_flags = 0 '\000',
is_non_first_fragment = 0 '\000', l4_layer_truncated = 0 '\000',
tcp_seq_number = 1}, {estimated_mtu = 0}}}, {fragment_first = 0,
fragment_last = 0, range_first = 0, range_last = 0, next_range_bi = 0,
ip6_frag_hdr_offset = 1}}}}, mpls = {pad = {7, 13, 0}, ttl = 0 '\000',
exp = 0 '\000', first = 0 '\000', pyld_proto = 0 '\000', rsvd = 0 '\000',
save_rewrite_length = 0 '\000', mpls_hdr_length = 0 '\000', bier = {
n_bytes = 0 '\000'}}, l2 = {feature_bitmap = 7, bd_index = 13, l2fib_sn = 0,
l2_len = 0 '\000', shg = 0 '\000', bd_age = 0 '\000'}, l2t = {pad = {7, 13, 0,
0}, next_index = 0 '\000', session_index = 1}, l2_classify = {pad = {
feature_bitmap = 7, bd_index = 13, l2fib_sn = 0, l2_len = 0 '\000',
shg = 0 '\000', bd_age = 0 '\000'}, {table_index = 0, opaque_index = 0},
hash = 4294967296}, policer = {pad = {7, 13, 0, 0, 0}, index = 1}, ipsec = {
__pad = {7, 13, 0}, sad_index = 0, protect_index = 0, thread_index = 1}, map = {
mtu = 7}, map_t = {map_domain_index = 7, v6 = {saddr = 13, daddr = 0,
frag_offset = 0, l4_offset = 0, l4_protocol = 0 '\000'}, checksum_offset = 1,
mtu = 0}, ip_frag = {pad = {7, 13}, mtu = 0, next_index = 0 '\000',
flags = 0 '\000'}, cop = {current_config_index = 7}, lisp = {overlay_afi = 7},
tcp = {connection_index = 7, {seq_number = 13, next_node_opaque = 13}, seq_end = 0,
ack_number = 0, hdr_offset = 0, data_offset = 0, data_len = 1, flags = 0 '\000'},
snat = {flags = 7, required_thread_index = 13}, unused = {7, 13, 0, 0, 0, 1}}}
3、vlib_buffer_t结构中私有字段2 opaque2参数内容。
#define vnet_buffer2(b) ((vnet_buffer_opaque2_t *) (b)->opaque2)
(gdb) p ((vnet_buffer_opaque2_t *) ($15)->opaque2)
$22 = (vnet_buffer_opaque2_t *) 0x10026ea108
(gdb) p *((vnet_buffer_opaque2_t *) ($15)->opaque2)
$23 = {qos = {bits = 0 '\000', source = 0 '\000'}, loop_counter = 0 '\000',
__unused = "", gbp = {__unused = 0 '\000', flags = 0 '\000', sclass = 0}, {
gso_size = 0, gso_l4_hdr_sz = 0, outer_l3_hdr_offset = 0, outer_l4_hdr_offset = 0},
nat = {arc_next = 0, {cached_session_index = 0, cached_dst_nat_session_index = 0}}, {{
pad = {0}, pg_replay_timestamp = 0}, unused = {0, 0, 0, 0, 0, 0, 0, 0}}}
上面分析完之后,是不是感觉也很简单,只是把一些常用的gdb定位手段总结以提高定位效率。当然还有很多,欢迎加入微信群一起讨论。
本文分享自 DPDK VPP源码分析 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!