問題描述
在使用eBPF程序跟蹤napi_gro_receive_entry
內核跟蹤點時,發現獲取到的IP頭部字段(如saddr
、daddr
、protocol
)為空值。
代碼如下:
/* 自定義結構體來映射 napi_gro_receive_entry tracepoint 的 format */
struct napi_gro_receive_entry_data {unsigned short common_type;unsigned char common_flags;unsigned char common_preempt_count;int common_pid;/* 以下字段根據 format 來定義 */char name[4]; // __data_loc char[]unsigned int napi_id;unsigned short queue_mapping;const void *skbaddr; // skbaddr 字段,用來訪問 skb 數據bool vlan_tagged;unsigned short vlan_proto;unsigned short vlan_tci;unsigned short protocol;unsigned char ip_summed;unsigned int hash;bool l4_hash;unsigned int len;unsigned int data_len;unsigned int truesize;bool mac_header_valid;int mac_header;unsigned char nr_frags;unsigned short gso_size;unsigned short gso_type;
};
//ip頭里面的信息為空
SEC("tp/net/napi_gro_receive_entry")
int trace_napi(struct napi_gro_receive_entry_data *ctx)
{// 獲取當前進程 PIDint pid = bpf_get_current_pid_tgid();bpf_printk("Hello!!! \n");// 讀取 IP 頭中的 protocol 字段u8 protocol; struct sk_buff *skb = (struct sk_buff *)ctx->skbaddr;if(skb == NULL){bpf_printk("skb is NULL \n");return 0;}struct iphdr *ip = (struct iphdr *)(BPF_CORE_READ(skb, head) +BPF_CORE_READ(skb, network_header));if(ip == NULL){bpf_printk("ip is NULL \n");return 0;}protocol = BPF_CORE_READ(ip, protocol);// 如果協議字段無效,返回if (protocol < 0) {bpf_printk("protocol is NULL \n");return 0;}//輸出協議源IP字段__u32 saddr = BPF_CORE_READ(ip, saddr);bpf_printk("Source IP: %d.%d.%d.%d\n",(saddr >> 24) & 0xFF,(saddr >> 16) & 0xFF,(saddr >> 8) & 0xFF,saddr & 0xFF);//輸出協議協議字段bpf_printk("protocol: %d \n", protocol);return 0;}
?輸出結果如下:
?發現讀取到的IP頭部信息(iphdr結構中的內容)是空的,但是可以確定獲取到了skb以及iphdr結構體。
問題分析:(deepseek解答)
-
?GRO合并未完成?
GRO機制會對多個分片報文進行重組,此時network_header
可能僅指向首個分片的頭部位置。當處理后續分片時,skb->network_header
可能尚未更新為有效偏移量?。 -
?協議頭初始化時序?
在GRO處理路徑中,skb->transport_header
和skb->network_header
的初始化可能延遲到分片重組完成后進行。過早訪問會導致讀取到內核未初始化的內存區域?。 -
?SKB共享狀態干擾?
當skb
被克隆(skb_clone()
)時,多個副本共享數據緩沖區。此時直接訪問skb->head + skb->network_header
可能觸發內存越界,因為克隆操作的元數據更新存在延遲?。
?以上給出了deepseek的解釋,但是本人并沒有驗證,后期會去深入源碼實現來探討這個問題。