Linux内核分析 - 网络[三]:从netif_receive_skb()说起

本文详细解析了以太网报头的处理过程,以及如何通过链路层报头跳过机制,进而深入探讨了ARP协议的具体处理流程,包括报文的接收、解析、路由查找、路由缓存更新以及最终的ARP响应发送。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

netif_receive_skb()函数中,可以看出处理的是像ARPIP这些链路层以上的协议,那么,链路层报头是在哪里去掉的呢?答案是网卡驱动中,在调用netif_receive_skb()前,

skb->protocol = eth_type_trans(skb, bp->dev);

该函数对处理后skb>data跳过以太网报头,由mac_header指示以太网报头:

进入netif_receive_skb()函数

list_for_each_entry_rcu(ptype,&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list)

按照协议类型依次由相应的协议模块进行处理,而所以的协议模块处理都会注册在ptype_base中,实际是链表结构。

net/core/dev.c

static struct list_head ptype_base __read_mostly;   /* Taps */

 

而相应的协议模块是通过dev_add_pack()函数加入的:

void dev_add_pack(struct packet_type *pt)

{

     int hash;

 

     spin_lock_bh(&ptype_lock);

     if (pt->type == htons(ETH_P_ALL))

              list_add_rcu(&pt->list, &ptype_all);

     else {

              hash = ntohs(pt->type) & PTYPE_HASH_MASK;

              list_add_rcu(&pt->list, &ptype_base[hash]);

     }

     spin_unlock_bh(&ptype_lock);

}

 

ARP处理为例

该模块的定义,它会在arp_init()中注册进ptype_base链表中:

static struct packet_type arp_packet_type __read_mostly = {

     .type =      cpu_to_be16(ETH_P_ARP),

     .func =      arp_rcv,

};

 

然后在根据报文的TYPE来在ptype_base中查找相应协议模块进行处理时,实际调用arp_rcv()进行接收

arp_rcv() --> arp_process()

arp = arp_hdr(skb);

……

arp_ptr= (unsigned char *)(arp+1);

sha= arp_ptr;

arp_ptr += dev->addr_len;

memcpy(&sip, arp_ptr, 4);

arp_ptr += 4;

arp_ptr += dev->addr_len;

memcpy(&tip, arp_ptr, 4);

操作后这指针位置:

然后判断是ARP请求报文,这时先查询路由表ip_route_input()

if (arp->ar_op == htons(ARPOP_REQUEST) &&

         ip_route_input(skb, tip, sip, 0, dev) == 0)

ip_route_input()函数中,先在cache中查询是否存在相应的路由表项:

hash = rt_hash(daddr, saddr, iif, rt_genid(net));

缓存的路由项在内核中组织成hash表的形式,因此在查询时,先算出的hash值,再用该项- rt_hash_table[hash].chain即可。这里可以看到,缓存路由项包括了源IP地址、目的IP地址、网卡号。

 

如果在缓存中没有查到匹配项,或指定不查询cache,则查询路由表ip_route_input_slow()

进入ip_route_input_slow()函数,最终调用fib_lookup()得到查询结果fib_result

if ((err = fib_lookup(net, &fl, &res)) != 0)

如果结果fib_result合法,则需要更新路由缓存,将此次查询结果写入缓存

hash = rt_hash(daddr, saddr, fl.iif, rt_genid(net));

err = rt_intern_hash(hash, rth, NULL, skb, fl.iif);

 

在查找完路由表后,回到arp_process()函数,如果路由项指向本地,则应由本机接收该报文:

if (addr_type == RTN_LOCAL) {

              ……

              if (!dont_send) {

                       n = neigh_event_ns(&arp_tbl, sha, &sip, dev);

                       if (n) {

                                 arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);

                                 neigh_release(n);

                       }

              }

              goto out;

     }

首先更新邻居表neigh_event_ns(),然后发送ARP响应 – arp_send

至此,大致的ARP流程完成。由于ARP部分涉及到路由表以及邻居表,这都是很大的概念,在下一篇中介绍,这里直接略过了。

<think>我们正在讨论Linux内核中的跟踪点(tracepoint)`trace_netif_receive_skb_entry`。这个跟踪点用于追踪网络接口接收数据包的事件,具体是在数据包进入网络堆栈的接收路径时被触发。 ### 跟踪点`netif_receive_skb_entry`的作用 在Linux内核中,当网络设备接收到一个数据包(通常以`struct sk_buff *`的形式表示)并准备将其传递到网络协议栈的上层时,会调用`netif_receive_skb`函数。`trace_netif_receive_skb_entry`跟踪点就放置在这个函数的入口处,用于记录数据包被接收的事件。 这个跟踪点对于以下情况非常有用: - 调试网络数据包的接收路径 - 分析网络性能瓶颈 - 监控特定数据包的流动 ### 使用方式 要使用这个跟踪点,可以通过内核的ftrace接口或者使用像`perf`、`trace-cmd`这样的工具。 #### 1. 使用ftrace ```bash # 切换到ftrace目录 cd /sys/kernel/debug/tracing # 启用跟踪点 echo 1 > events/net/netif_receive_skb_entry/enable # 开始跟踪 echo 1 > tracing_on # ... 运行一些网络操作 ... # 停止跟踪 echo 0 > tracing_on echo 0 > events/net/netif_receive_skb_entry/enable # 查看跟踪结果 cat trace ``` #### 2. 使用trace-cmd ```bash # 记录事件 trace-cmd record -e net:netif_receive_skb_entry # 查看报告 trace-cmd report ``` #### 3. 使用perf ```bash # 记录事件 perf record -e net:netif_receive_skb_entry -a # 查看报告 perf script ``` ### 跟踪点的输出信息 当这个跟踪点被触发时,它会记录以下信息(具体字段可能因内核版本而异): - 设备名(dev) - 数据包长度(skb->len) - 数据包的协议类型(skb->protocol) - 源IP和目的IP(如果可用) - 等等 这些信息可以帮助开发者了解数据包何时进入协议栈,以及经过哪些处理。 ### 示例输出 一个典型的输出可能如下: ``` <idle>-0 [000] d.s. 12345.678901: netif_receive_skb_entry: dev=eth0 skbaddr=0xffff880123456789 len=1500 protocol=0x0800 ``` 这表示在eth0设备上接收到一个长度为1500字节的IPv4协议数据包。 ### 注意事项 - 启用跟踪点会带来一定的性能开销,特别是在高流量环境下。 - 需要root权限或相应的能力(CAP_SYS_ADMIN)来操作跟踪点。 - 内核必须配置了`CONFIG_TRACING`和`CONFIG_NETWORK_TRACING`选项。 ### 相关内核代码 在Linux内核源代码中,这个跟踪点通常定义在`include/trace/events/net.h`中,并在`net/core/dev.c`中的`netif_receive_skb`函数开头被调用。 例如,在`net/core/dev.c`中: ```c static int netif_receive_skb(struct sk_buff *skb) { trace_netif_receive_skb_entry(skb); // ... 其他处理 ... } ``` ### 总结 `trace_netif_receive_skb_entry`是一个用于跟踪网络数据包接收事件的跟踪点,它对于网络开发和调试非常有用。通过ftrace、perf或trace-cmd等工具,开发者可以监控和分析数据包在内核协议栈中的流动。 §§相关问题§§ 1. Linux内核中还有哪些与网络接收相关的跟踪点? 2. 如何分析`netif_receive_skb_entry`跟踪点产生的数据? 3. 在高流量环境下使用跟踪点有什么注意事项? 4. 如何编写自定义的内核模块来扩展跟踪点的功能?</think>### Linux 内核跟踪点 `trace_netif_receive_skb_entry` 详解 #### **1. 作用与目的** `trace_netif_receive_skb_entry` 是 Linux 内核网络栈的关键跟踪点,用于监控数据包(`sk_buff`)**进入网络协议栈的初始阶段**。它在以下场景被触发: - 当 NIC 驱动通过 NAPI 轮询机制将数据包传递给内核- 在数据包进入 `netif_receive_skb()` 函数时立即记录[^1] - 主要用于调试**数据包接收路径**的延迟、丢包或异常行为 #### **2. 核心功能** ```c // 内核调用示例(简化) trace_netif_receive_skb_entry(skb); ``` 跟踪点记录的关键信息包括: - `skb` 的内存地址 - 接收设备名(如 eth0) - 数据包长度(`skb->len`) - 协议类型(IPv4/IPv6) - 时间戳(用于性能分析) #### **3. 典型使用场景** 1. **性能分析** 通过计算 `netif_receive_skb_entry` 和 `netif_receive_skb_exit` 的时间差,测量协议栈处理延迟: ```bash perf record -e net:netif_receive_skb_entry,net:netif_receive_skb_exit -a ``` 2. **丢包调试** 结合 `trace_netif_rx` 和 `trace_kfree_skb`,定位丢包位置: ```bash trace-cmd record -e netif* -e kfree_skb ``` 3. **CPU 负载均衡** 验证 RPS(Receive Packet Steering)是否生效[^2]: ```bash # 查看数据包被分配到哪个CPU trace-cmd report | grep "netif_receive_skb_entry.*cpu" ``` #### **4. 使用示例** **通过 ftrace 捕获数据包:** ```bash # 启用跟踪点 echo 1 > /sys/kernel/debug/tracing/events/net/netif_receive_skb_entry/enable # 捕获数据(持续5秒) cat /sys/kernel/debug/tracing/trace_pipe > trace.log & sleep 5 killall cat # 解析输出示例 # ksoftirqd/0-7 [000] ...1 1234.567: netif_receive_skb_entry: dev=eth0 skbaddr=0xffff888003aab700 len=1500 ``` #### **5. 关键注意事项** - **软中断上下文**:该跟踪点通常在 `ksoftirqd` 线程中触发[^3],需关注 CPU 亲和性 - **性能影响**:高频跟踪可能增加延迟,生产环境建议采样使用 - **依赖配置**:需内核启用 `CONFIG_TRACING` 和 `CONFIG_NETWORK_TRACING` - **与 `netif_rx` 的区别**:`netif_receive_skb_entry` 是 NAPI 路径,`netif_rx` 用于传统中断模式 #### **6. 高级调试技巧** - **关联堆栈**:启用 `stacktrace` 选项定位调用路径: ```bash echo stacktrace > /sys/kernel/debug/tracing/events/net/netif_receive_skb_entry/trigger ``` - **过滤设备**:仅监控 eth0: ```bash echo 'dev==1' > /sys/kernel/debug/tracing/events/net/netif_receive_skb_entry/filter # 设备ID通过 ifconfig eth0 获取 ``` > **提示**:完整接收路径涉及多个跟踪点: > `netif_receive_skb_entry` → `netif_receive_skb` → `__netif_receive_skb` → 协议层处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值