linux内核网络协议栈报文的处理过程

261 阅读4分钟

一、整体架构图(自底向上)

深色版本
[ 网卡硬件 ][ 网卡驱动(NAPI) → Ring Buffer → 中断/轮询 ][ 内核网络子系统(netif_receive_skb) ][ 网桥/虚拟设备(如 br0, veth) → netfilter PREROUTING ][ IP 层(路由决策、转发 or 本机) ][ netfilter INPUT → 传输层(TCP/UDP) ][ Socket 层(接收队列) ][ 应用层(recv()/read()) ]

发包方向相反。


✅ 二、接收流程(Rx)详解

1. 网卡收包 & 硬件中断

  • 网卡收到数据帧(Ethernet Frame)
  • 将数据写入 Ring Buffer(DMA 写入内存)
  • 触发 硬件中断(MSI-X 或传统 IRQ)
  • 或使用 NAPI(New API)轮询模式(避免中断风暴)

⚙️ 查看中断:

bash
深色版本
cat /proc/interrupts | grep eth0

2. 软中断处理(ksoftirqd)

  • 硬件中断触发后,内核调度 软中断(softirq)
  • 软中断类型:NET_RX_SOFTIRQ
  • 执行函数:net_rx_action()
  • 调用驱动注册的 poll() 函数(如 ixgbe_poll

📌 NAPI 机制:

  • 高负载时:切换到轮询模式(减少中断开销)
  • 低负载时:恢复中断模式

3. 进入网络协议栈(netif_receive_skb)

c
深色版本
netif_receive_skb(skb)
    → __netif_receive_skb_core()
        → handle_packet()
            → pt_prev->func()  // 如 eth_type_trans 处理以太网类型
  • skb(socket buffer)是核心数据结构

  • 解析 Ethernet Header

    • 源/目的 MAC
    • EtherType(如 0x0800 → IPv4)

4. 桥接/虚拟设备处理(可选)

如果启用了网桥(bridge)、Docker veth、Open vSwitch:

c
深色版本
br_handle_frame()
    → br_pass_frame_up()  // 本机处理br_forward()        // 转发到其他端口

5. Netfilter PREROUTING 钩子

c
深色版本
nf_hook(NF_INET_PRE_ROUTING)
  • 执行 iptables 的 PREROUTING 链
  • 可做 DNAT、包过滤、连接跟踪(conntrack)

🔍 示例:

bash
深色版本
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.10:80

6. IP 层处理(ip_rcv)

c
深色版本
ip_rcv()
    → ip_rcv_finish()
        → fib_lookup()  // 查路由表

路由决策(FIB:Forwarding Information Base)

目的地址类型处理路径
本机 IP(VIP 或物理 IP)RTN_LOCAL → 送往上层
其他网段 IPRTN_UNICAST → 转发(需开启 IP Forward)
广播/组播特殊处理

📊 查看路由表:

bash
深色版本
ip route show

7. Netfilter INPUT 钩子

c
深色版本
nf_hook(NF_INET_LOCAL_IN)
  • 执行 iptables 的 INPUT 链
  • 常用于防火墙、访问控制

8. 传输层处理

TCP 报文:

c
深色版本
tcp_v4_rcv()
    → tcp_v4_do_rcv()
        → tcp_rcv_state_process()
            → 根据 TCP 状态机处理(SYN、ACK、DATA)
            → 放入 socket 接收队列(sk_receive_queue)

UDP 报文:

c
深色版本
udp_rcv()
    → udp_queue_rcv_skb()
        → __udp_queue_rcv_skb()
            → 放入 socket 接收队列

9. Socket 层(接收队列)

  • 每个 socket 有一个接收队列(sk_receive_queue
  • 数据包被 skb 封装后放入队列
  • 触发 socket 的 data_ready 回调(唤醒等待进程)

🔔 唤醒机制:

  • 如果应用在 recv() 阻塞,会被唤醒
  • 使用 epoll 时,eventpoll 会收到通知

10. 应用层接收

c
深色版本
// 用户态调用
recv(sockfd, buf, len, 0);
// 或
read(sockfd, buf, len);
  • 系统调用进入内核
  • 从 sk_receive_queue 取出 skb
  • 拷贝数据到用户空间 buf
  • 释放 skb

✅ 三、发送流程(Tx)详解(反向)

深色版本
[ 应用层 send()/write() ][ Socket 层(发送队列)][ 传输层(TCP 分段 or UDP 直传)][ IP 层(路由查找、分片)][ Netfilter OUTPUT → POSTROUTING ][ 数据链路层(ARP 解析、封装 Ethernet Header)][ 网卡驱动(DMA 发送到 Ring Buffer)][ 网卡硬件(发送到物理网络)]

关键点:

  • 路由查找ip_route_output_flow()
  • ARP 解析:如果目的 MAC 未知,发 ARP 请求
  • NetfilterOUTPUT 和 POSTROUTING 链
  • GSO/GRO:大段卸载(发送)和大接收卸载(接收)

✅ 四、关键数据结构

结构说明
sk_buff(skb)报文载体,贯穿整个协议栈
socksocket 抽象,包含接收/发送队列
net_device网络设备抽象(如 eth0)
net网络命名空间(支持容器)
flowi路由查找的查询结构

✅ 五、性能优化相关机制

机制说明
NAPI减少中断开销
RPS/RSS多队列网卡负载均衡
GRO/LRO接收端聚合,减少上层处理次数
GSO/TSO发送端分段卸载到网卡
XDPeXpress Data Path,在驱动层处理包
AF_XDP零拷贝用户态网络

✅ 六、查看和调试命令

目的命令
查看软中断cat /proc/softirqs
查看网络设备ethtool -i eth0
查看 socket 状态ss -ant
抓包tcpdump -i eth0
查看路由ip route show
查看 netfilteriptables -L -v -n
跟踪内核函数perf trace -p $(pidof nginx)

✅ 七、面试回答模板

“Linux 内核网络协议栈处理报文的流程如下:

  1. 收包:网卡通过 DMA 将数据写入 Ring Buffer,触发软中断;
  2. 协议栈netif_receive_skb 进入协议栈,经过 PREROUTING、IP 层路由决策;
  3. 本机处理:如果是本机 IP,进入 INPUT 链,再送到 TCP/UDP 层;
  4. Socket:放入 socket 接收队列,唤醒等待进程;
  5. 应用层recv() 系统调用将数据拷贝到用户空间。

发包流程相反,经过 OUTPUT、路由、POSTROUTING,最后由网卡发出。

整个过程由 sk_buff 贯穿,支持 NAPI、GRO、XDP 等高性能机制。”


✅ 八、总结

阶段关键处理
硬件层DMA、中断、NAPI
数据链路层MAC 处理、VLAN、网桥
网络层IP 处理、路由、netfilter
传输层TCP 状态机、UDP 直传
Socket 层接收/发送队列管理
应用层系统调用接口

掌握这个流程,说明你对 Linux 网络的理解已经深入到 内核源码级别