Linux 5.10 内核网络子系统分析避开某些挖坑思路
·
一、源码目录分析
1.1 网络子系统核心目录结构
linux-5.10/net/ # 网络子系统根目录 │ ├── socket.c # Socket 层核心实现 ├── sysctl_net.c # 网络 sysctl 接口 │ ├── core/ # 核心网络基础设施 │ ├── skbuff.c # SKB 缓冲区管理 │ ├── sock.c # Socket 通用操作 │ ├── dev.c # 网络设备核心 │ ├── netpoll.c # 网络轮询 │ ├── rtnetlink.c # 路由 netlink 接口 │ ├── neighbour.c # 邻居协议 (ARP/NDP) │ ├── net-sysfs.c # sysfs 接口 │ ├── net-procfs.c # procfs 接口 │ ├── datagram.c # 数据报 socket 操作 │ ├── stream.c # 流 socket 操作 │ ├── filter.c # Socket 过滤器 (BPF) │ ├── request_sock.c # 请求 socket 管理 │ └── dev_ioctl.c # 网络设备 ioctl 接口 │ ├── ipv4/ # IPv4 协议栈 │ ├── af_inet.c # IPv4 socket 族 │ ├── ip_input.c # IPv4 输入处理 │ ├── ip_output.c # IPv4 输出处理 │ ├── ip_forward.c # IPv4 转发 │ ├── tcp.c # TCP 核心 │ ├── tcp_input.c # TCP 输入处理 │ ├── tcp_output.c # TCP 输出处理 │ ├── tcp_timer.c # TCP 定时器 │ ├── tcp_ipv4.c # TCP IPv4 专用 │ ├── tcp_cong.c # TCP 拥塞控制框架 │ ├── tcp_cubic.c # CUBIC 拥塞控制算法(默认) │ ├── tcp_bbr.c # BBR 拥塞控制算法 │ ├── tcp_nv.c # TCP-NV 数据中心拥塞控制 │ ├── tcp_vegas.c # Vegas 拥塞控制算法 │ ├── tcp_dctcp.c # DCTCP 数据中心 TCP │ ├── tcp_westwood.c # Westwood 无线优化 │ ├── tcp_hybla.c # Hybla 卫星链路优化 │ ├── udp.c # UDP 实现 │ ├── icmp.c # ICMP 实现 │ ├── route.c # IPv4 路由 │ ├── fib_trie.c # FIB 路由表 │ ├── inet_fragment.c # IP 分片重组 │ ├── ping.c # ICMP echo │ ├── raw.c # RAW socket │ ├── syncookies.c # SYN Cookie 防攻击 │ └── sysctl_net_ipv4.c # IPv4 sysctl │ ├── ipv6/ # IPv6 协议栈 │ ├── af_inet6.c # IPv6 socket 族 │ ├── ip6_input.c # IPv6 输入 │ ├── ip6_output.c # IPv6 输出 │ ├── tcp_ipv6.c # TCP over IPv6 │ ├── udp.c # UDP over IPv6 │ ├── icmp.c # ICMPv6 │ ├── ndisc.c # 邻居发现 │ ├── route.c # IPv6 路由 │ ├── addrconf.c # 地址配置 │ └── sysctl_net_ipv6.c # IPv6 sysctl │ ├── netfilter/ # Netfilter 防火墙 │ ├── core.c # Netfilter 核心 │ ├── nf_conntrack_core.c # 连接跟踪 │ ├── nf_conntrack_proto_tcp.c # TCP 连接跟踪 │ ├── nf_tables_api.c # nftables 框架 │ └── x_tables.c # iptables 框架 │ ├── sched/ # 流量控制 (QoS) │ ├── sch_generic.c # 通用调度器 │ ├── sch_fifo.c # FIFO 调度器 │ ├── sch_prio.c # 优先级调度器 │ ├── sch_htb.c # HTB 令牌桶 │ ├── sch_cake.c # CAKE 队列管理 │ ├── sch_red.c # RED 主动队列管理 │ ├── sch_sfq.c # SFQ 公平队列 │ ├── sch_tbf.c # TBF 令牌桶过滤器 │ ├── sch_netem.c # 网络模拟器 │ └── cls_api.c # 分类器 API │ ├── netlink/ # Netlink 通信 │ ├── af_netlink.c # Netlink socket │ ├── genetlink.c # 通用 netlink │ └── policy.c # 策略 │ ├── bridge/ # 网桥 │ ├── br.c # 网桥核心 │ ├── br_input.c # 网桥输入 │ ├── br_forward.c # 网桥转发 │ └── br_fdb.c # 转发数据库 │ ├── unix/ # UNIX domain socket │ └── af_unix.c # AF_UNIX 实现 │ ├── packet/ # AF_PACKET │ └── af_packet.c # 原始套接字 │ ├── wireless/ # 无线网络 │ ├── nl80211.c # 无线 netlink │ └── cfg80211.c # 无线配置 │ ├── xfrm/ # IPsec XFRM │ ├── xfrm_input.c # IPsec 输入 │ └── xfrm_output.c # IPsec 输出 │ └── mptcp/ # Multipath TCP ├── mptcp.c # MPTCP 核心 └── subflow.c # 子流管理
1.2 驱动与硬件抽象层
linux-5.10/drivers/net/ethernet/ # 网卡驱动 ├── intel/ │ ├── igb/ # Intel I350 1G 驱动 │ │ ├── igb_main.c # 驱动主逻辑 │ │ ├── igb_lib.c # 队列管理、中断处理 │ │ └── igb_ethtool.c # ethtool 接口 │ └── ixgbe/ # Intel 10G 驱动 │ ├── ixgbe_main.c │ └── ixgbe_lib.c ├── broadcom/ │ └── bnxt/ # Broadcom NetXtreme └── mellanox/ └── mlx5/ # Mellanox ConnectX
1.3 关键头文件
linux-5.10/include/ ├── linux/ │ ├── netdevice.h # 网络设备结构 │ ├── skbuff.h # SKB 结构 │ ├── sock.h # Socket 结构 │ ├── netdev_features.h # 硬件卸载特性标志 │ └── netfilter.h # Netfilter ├── net/ │ ├── sock.h # Socket 内部 │ ├── tcp.h # TCP 内部 │ ├── tcp_states.h # TCP 状态定义 │ ├── ip.h # IP 内部 │ ├── flow.h # 流 │ ├── flow_dissector.h # 流解析器 │ ├── pkt_sched.h # 调度器 │ └── net_namespace.h # 网络命名空间 └── uapi/linux/ # 用户空间 API ├── if.h # 网络接口 ├── in.h # IPv4 ├── in6.h # IPv6 └── tcp.h # TCP
二、数据结构分析
2.1 核心数据结构层次图
┌─────────────────────────────────────────────────────────────────────────────────────┐ │ 网络子系统核心数据结构 │ ├─────────────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌────────────────────────────────────────────────────────────────────────────────┐ │ │ │ struct sk_buff (include/linux/skbuff.h) │ │ │ │ ┌──────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ SKB - 网络数据包核心结构 │ │ │ │ │ ├──────────────────────────────────────────────────────────────────────────┤ │ │ │ │ │ /* 链表指针 */ │ │ │ │ │ │ struct sk_buff *next; // 下一个SKB │ │ │ │ │ │ struct sk_buff *prev; // 上一个SKB │ │ │ │ │ │ │ │ │ │ │ │ /* 数据指针 */ │ │ │ │ │ │ unsigned char *head; // 缓冲区起始 │ │ │ │ │ │ unsigned char *data; // 数据起始 │ │ │ │ │ │ unsigned char *tail; // 数据结束 │ │ │ │ │ │ unsigned char *end; // 缓冲区结束 │ │ │ │ │ │ │ │ │ │ │ │ /* 长度信息 */ │ │ │ │ │ │ unsigned int len; // 总数据长度 │ │ │ │ │ │ unsigned int data_len; // 非线性数据长度 │ │ │ │ │ │ unsigned int truesize; // 实际分配大小 │ │ │ │ │ │ │ │ │ │ │ │ /* 协议信息 */ │ │ │ │ │ │ __be16 protocol; // 三层协议类型 │ │ │ │ │ │ unsigned short transport_header; // 传输头偏移 │ │ │ │ │ │ unsigned short network_header; // 网络头偏移 │ │ │ │ │ │ unsigned short mac_header; // MAC头偏移 │ │ │ │ │ │ │ │ │ │ │ │ /* 控制信息 */ │ │ │ │ │ │ struct dst_entry *dst; // 路由目标 │ │ │ │ │ │ struct sock *sk; // 所属socket │ │ │ │ │ │ unsigned int hash; // 包哈希 │ │ │ │ │ │ unsigned int cb[48]; // 控制块 │ │ │ │ │ │ __u32 priority; // 优先级 │ │ │ │ │ └──────────────────────────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────────────────────────────┐ │ │ │ struct sock (include/net/sock.h) │ │ │ │ ┌──────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ Socket 核心结构 │ │ │ │ │ ├──────────────────────────────────────────────────────────────────────────┤ │ │ │ │ │ /* 接收队列 */ │ │ │ │ │ │ struct sk_buff_head sk_receive_queue; // 接收队列 │ │ │ │ │ │ struct sk_buff_head sk_backlog; // 后备队列 │ │ │ │ │ │ │ │ │ │ │ │ /* 发送队列 */ │ │ │ │ │ │ struct sk_buff_head sk_write_queue; // 发送队列 │ │ │ │ │ │ │ │ │ │ │ │ /* 缓冲区大小 */ │ │ │ │ │ │ int sk_rcvbuf; // 接收缓冲区大小 │ │ │ │ │ │ int sk_sndbuf; // 发送缓冲区大小 │ │ │ │ │ │ │ │ │ │ │ │ /* 状态信息 */ │ │ │ │ │ │ unsigned long sk_state; // socket状态 │ │ │ │ │ │ unsigned long sk_flags; // 标志位 │ │ │ │ │ │ │ │ │ │ │ │ /* 操作函数 */ │ │ │ │ │ │ struct proto *sk_prot; // 协议操作 │ │ │ │ │ │ const struct proto_ops *sk_proto_ops; // socket操作 │ │ │ │ │ │ │ │ │ │ │ │ /* 定时器 */ │ │ │ │ │ │ struct timer_list sk_timer; // 定时器 │ │ │ │ │ │ │ │ │ │ │ │ /* 引用计数 */ │ │ │ │ │ │ atomic_t sk_refcnt; // 引用计数 │ │ │ │ │ └──────────────────────────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────────────────────────────┐ │ │ │ struct tcp_sock (include/linux/tcp.h) │ │ │ │ ┌──────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ TCP 扩展结构 │ │ │ │ │ ├──────────────────────────────────────────────────────────────────────────┤ │ │ │ │ │ u32 rcv_nxt; // 下一个期望接收的序列号 │ │ │ │ │ │ u32 snd_nxt; // 下一个要发送的序列号 │ │ │ │ │ │ u32 snd_una; // 最早未确认的序列号 │ │ │ │ │ │ u32 snd_wnd; // 发送窗口 │ │ │ │ │ │ u32 rcv_wnd; // 接收窗口 │ │ │ │ │ │ u32 snd_cwnd; // 拥塞窗口 │ │ │ │ │ │ u32 snd_ssthresh; // 慢启动阈值 │ │ │ │ │ │ u32 srtt_us; // 平滑 RTT(微秒) │ │ │ │ │ │ u32 mss_cache; // 缓存的 MSS │ │ │ │ │ │ u32 reordering; // 重排序阈值 │ │ │ │ │ │ u8 ecn_flags; // ECN 标志 │ │ │ │ │ │ struct sk_buff_head out_of_order_queue; // 乱序队列 │ │ │ │ │ └──────────────────────────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────────────────────────────┐ │ │ │ struct net_device (include/linux/netdevice.h) │ │ │ │ ┌──────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ 网络设备结构 │ │ │ │ │ ├──────────────────────────────────────────────────────────────────────────┤ │ │ │ │ │ char name[IFNAMSIZ]; // 设备名 (eth0) │ │ │ │ │ │ unsigned long state; // 设备状态 │ │ │ │ │ │ struct netdev_queue *_tx; // 发送队列 │ │ │ │ │ │ unsigned int num_tx_queues; // TX队列数量 │ │ │ │ │ │ unsigned int real_num_tx_queues;// 实际TX队列数 │ │ │ │ │ │ unsigned int mtu; // 最大传输单元 │ │ │ │ │ │ const struct net_device_ops *netdev_ops; // 设备操作 │ │ │ │ │ │ netdev_features_t features; // 设备特性 │ │ │ │ │ │ struct in_device *ip_ptr; // IPv4配置 │ │ │ │ │ │ struct inet6_dev *ip6_ptr; // IPv6配置 │ │ │ │ │ │ struct napi_struct *napi_list; // NAPI列表 │ │ │ │ │ └──────────────────────────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────────────────────────────┐ │ │ │ struct softnet_data (net/core/dev.c) - Per-CPU 结构 │ │ │ │ ┌──────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ 软中断数据处理结构 │ │ │ │ │ ├──────────────────────────────────────────────────────────────────────────┤ │ │ │ │ │ struct list_head poll_list; // NAPI 轮询列表 │ │ │ │ │ │ struct sk_buff_head process_queue; // 待处理 SKB 队列 │ │ │ │ │ │ unsigned int processed; // 已处理包计数 │ │ │ │ │ │ unsigned int time_squeeze; // 预算耗尽次数(丢包标志) │ │ │ │ │ │ struct sk_buff_head input_pkt_queue; // RPS 队列 │ │ │ │ │ └──────────────────────────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────────────────────────────┐ │ │ │ struct tcpnv (net/ipv4/tcp_nv.c) - TCP-NV 拥塞控制参数 │ │ │ │ ┌──────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ u32 nv_min_rtt; // 当前最小 RTT │ │ │ │ │ │ u32 nv_base_rtt; // 拥塞阈值 RTT │ │ │ │ │ │ u32 nv_lower_bound_rtt; // RTT 下界(base_rtt 的 80%) │ │ │ │ │ │ u8 nv_allow_cwnd_growth:1; // 是否允许窗口增长 │ │ │ │ │ │ u8 nv_catchup:1; // 追赶模式标志 │ │ │ │ │ │ u8 nv_eval_call_cnt; // 评估周期计数 │ │ │ │ │ └──────────────────────────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────────┘
2.2 TCP 状态机定义
// include/net/tcp_states.h
enum {
TCP_ESTABLISHED = 1, // 已建立连接
TCP_SYN_SENT, // 已发送 SYN
TCP_SYN_RECV, // 已收到 SYN
TCP_FIN_WAIT1, // 等待 FIN
TCP_FIN_WAIT2, // 等待 FIN
TCP_TIME_WAIT, // 等待 2MSL
TCP_CLOSE, // 关闭
TCP_CLOSE_WAIT, // 等待关闭
TCP_LAST_ACK, // 等待最后 ACK
TCP_LISTEN, // 监听
TCP_CLOSING, // 同时关闭
TCP_NEW_SYN_RECV, // 新 SYN 接收
};
三、初始化与设备注册流程
3.1 内核引导时的网络初始化
start_kernel()
│
└── net_dev_init() [net/core/dev.c]
│
├── for_each_possible_cpu(cpu) {
│ struct softnet_data *sd = &per_cpu(softnet_data, cpu);
│ skb_queue_head_init(&sd->input_pkt_queue);
│ skb_queue_head_init(&sd->process_queue);
│ INIT_LIST_HEAD(&sd->poll_list);
│ }
│
├── open_softirq(NET_TX_SOFTIRQ, net_tx_action);
├── open_softirq(NET_RX_SOFTIRQ, net_rx_action);
│
└── register_pernet_subsys(&netdev_net_ops); // 网络命名空间
3.2 网卡设备驱动初始化(PCI 探测)
驱动模块加载 │ └── module_init(igb_init_module) │ └── pci_register_driver(&igb_driver) │ └── igb_probe() [drivers/net/ethernet/intel/igb/igb_main.c] │ ├── pci_enable_device() ├── pci_request_regions() ├── dma_set_mask() │ ├── netdev = alloc_etherdev_mq(sizeof(struct igb_adapter), │ num_tx_queues, num_rx_queues) │ ├── netdev->netdev_ops = &igb_netdev_ops; │ // ndo_open → igb_open │ // ndo_start_xmit → igb_xmit_frame │ // ndo_set_features → igb_set_features │ └── register_netdev(netdev) // 注册到网络核心
3.3 网卡启动与 NAPI 初始化
igb_open() [驱动层] │ ├── igb_alloc_queues() // 分配 RX/TX 队列 │ ├── igb_configure() // 配置网卡寄存器 │ ├── igb_setup_all_rx_resources() // 设置 RX ring buffer │ ├── igb_request_irq() // 注册中断处理函数 │ │ │ └── request_irq(irq, igb_msix_ring, ...) │ └── napi_enable(&adapter->napi) // 启用 NAPI
3.4 通知链机制
当网卡状态变化时(up/down、地址变化),通过通知链通知其他模块:
// 注册监听 int register_netdevice_notifier(struct notifier_block *nb); // 事件类型 #define NETDEV_UP 0x0001 // 设备启用 #define NETDEV_DOWN 0x0002 // 设备停用 #define NETDEV_CHANGE 0x0004 // 设备状态变化 #define NETDEV_REGISTER 0x0008 // 设备注册 #define NETDEV_UNREGISTER 0x0010 // 设备注销
四、NAPI 与 GRO 完整生命周期
4.1 NAPI 收包完整流程
网卡 DMA 写入 RX Ring Buffer
│
▼
硬中断 IRQ Handler (igb_msix_ring) [驱动层]
│
├── 读取中断状态寄存器
├── 禁用当前 RX 队列的中断
└── napi_schedule() // 将 napi 加入 poll_list
│
└── __raise_softirq_irqoff(NET_RX_SOFTIRQ) // 触发软中断
│
▼
软中断处理 net_rx_action() [net/core/dev.c]
│
├── budget = netdev_budget; // 默认 300
├── time_limit = netdev_budget_usecs; // 默认 2000us
│
└── while (budget > 0 && !time_after(jiffies, time_limit)) {
for_each_napi_in_list() {
work = napi_poll(napi, budget);
budget -= work;
if (work < napi->weight) {
napi_complete_done(napi, work); // 完成轮询,重新启用中断
}
}
}
4.2 GRO 合并与批量提交
驱动 poll() 函数收包 │ ├── napi_gro_receive(skb) // GRO 入口 │ │ │ ├── 计算包的 hash 值(五元组) │ ├── 在 gro_list 中查找相同流的数据包 │ ├── 如果可以合并 → gro_receive() 合并 │ └── 如果无法合并或超时 → netif_receive_skb() │ └── napi_complete_done(napi, work) │ └── gro_normal_list(napi) // 批量提交合并后的包
4.3 关键调优参数
# 控制 NAPI 单次处理的包预算 sysctl net.core.netdev_budget=300 # 控制 NAPI 单次处理的时间上限(微秒) sysctl net.core.netdev_budget_usecs=2000 # 控制 GRO 批量提交的大小 sysctl net.core.gro_normal_batch=8
五、硬件卸载与多队列优化
5.1 多队列与 RSS(Receive Side Scaling)
RSS 通过硬件哈希将流量分散到多个 RX 队列,每个队列绑定不同 CPU,实现并行处理。
# 查看当前队列配置 ethtool -l eth0 # 修改队列数量 ethtool -L eth0 combined 8 # 查看 RSS 哈希分配表 ethtool -x eth0 # 设置权重(队列0权重6,队列1权重2) ethtool -X eth0 weight 6 2 # 修改 RSS 哈希字段(加入端口信息) ethtool -N eth0 rx-flow-hash udp4 sdfn
5.2 硬件卸载类型与配置
| 卸载特性 | 标志 | 启用命令 | 作用 |
|---|---|---|---|
| TSO | tx-tcp-segmentation |
ethtool -K eth0 tso on |
硬件处理 TCP 分片 |
| GRO | rx-gro |
ethtool -K eth0 gro on |
硬件合并小包 |
| RX Checksum | rx-checksumming |
ethtool -K eth0 rx on |
硬件校验和 |
| aRFS | ntuple |
ethtool -K eth0 ntuple on |
硬件流定向 |
# 开启硬件流定向(aRFS) ethtool -K eth0 ntuple on # 添加规则:目标端口 80 的 TCP 包送到 RX 队列 2 ethtool -U eth0 flow-type tcp4 dst-port 80 action 2
5.3 RPS/RFS(软件多队列)
当硬件不支持多队列时,可用 RPS(Receive Packet Steering)在软件层实现负载均衡:
# 启用 RPS:将 CPU 掩码设为 0x0f(CPU 0-3) echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus # 启用 RFS:设置流表大小 sysctl net.core.rps_sock_flow_entries=32768 echo 4096 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
六、数据包接收完整路径
═══════════════════════════════════════════════════════════════════════════════════════
数据包接收完整流程
═══════════════════════════════════════════════════════════════════════════════════════
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ 硬件层 │
│ 1. 网卡接收电信号,进行 CRC 校验 │
│ 2. DMA 将数据写入 RX Ring Buffer(Descriptor 指向的内存) │
│ 3. 更新 RX 描述符状态,触发 MSI-X 中断 │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ 驱动层 - 硬中断处理 │
│ igb_msix_ring() │
│ 1. 读取中断状态寄存器,确认是 RX 完成中断 │
│ 2. 禁用该 RX 队列的中断(避免嵌套中断) │
│ 3. napi_schedule(&adapter->napi[queue_id]) │
│ 4. 触发 NET_RX_SOFTIRQ 软中断 │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ 核心层 - 软中断处理(NAPI) │
│ net_rx_action() [net/core/dev.c] │
│ budget = netdev_budget (默认 300) │
│ for each napi in poll_list: │
│ work = napi->poll(napi, budget) │
│ budget -= work │
│ if (work < napi->weight) { │
│ napi_complete_done(napi, work) // 重新启用中断 │
│ } │
│ │
│ 驱动 poll() 函数 (igb_poll): │
│ 1. 从 RX Ring Buffer 取出 skb │
│ 2. 设置 skb->protocol = eth_type_trans(skb, netdev) │
│ 3. napi_gro_receive(skb) // GRO 合并 │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ GRO 层 │
│ napi_gro_receive() → netif_receive_skb() 或 gro_normal_list() │
│ 1. 计算数据包哈希值(五元组) │
│ 2. 在 gro_list 中查找相同流的数据包 │
│ 3. 如果可合并 → gro_receive() 将小包合并到大包 │
│ 4. 如果不可合并或超时 → netif_receive_skb() │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ 协议栈入口(L2 处理) │
│ __netif_receive_skb_core() [net/core/dev.c] │
│ 1. 处理 VLAN 标签 (vlan_do_receive) │
│ 2. 处理网桥 (br_handle_frame) │
│ 3. ptype_all 协议嗅探器(tcpdump 抓包点在这里!) │
│ 4. 根据 protocol 查找 ptype_base 中的处理函数 │
│ 5. 调用 ip_rcv() 或 arp_rcv() │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ Netfilter 钩子点 │
│ ip_rcv() 中执行 NF_INET_PRE_ROUTING 钩子 │
│ ip_local_deliver() 中执行 NF_INET_LOCAL_IN 钩子 │
│ ip_forward() 中执行 NF_INET_FORWARD 钩子 │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ IPv4 层 │
│ ip_rcv() [net/ipv4/ip_input.c] │
│ 1. 检查 IP 头版本和长度 │
│ 2. 计算 IP 头校验和 │
│ 3. ip_rcv_finish() │
│ ├── ip_route_input_noref() // 查找路由 │
│ └── 根据目的地址决定: │
│ - 本地接收 → ip_local_deliver() │
│ - 转发 → ip_forward() │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ 传输层(TCP) │
│ tcp_v4_rcv() [net/ipv4/tcp_ipv4.c] │
│ 1. __inet_lookup_skb() 查找 socket │
│ 2. 根据 socket 状态分发: │
│ - TCP_ESTABLISHED → tcp_rcv_established() │
│ - TCP_LISTEN → tcp_v4_do_rcv() (三次握手) │
│ - 其他 → tcp_rcv_state_process() │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ Socket 层 │
│ tcp_rcv_established() [net/ipv4/tcp_input.c] │
│ 1. tcp_ack() 更新发送窗口和 RTT │
│ 2. 处理乱序包:放入 out_of_order_queue │
│ 3. 将数据加入 sk_receive_queue │
│ 4. sk->sk_data_ready(sk) 唤醒等待进程 │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ 用户空间 │
│ recv() / read() 系统调用返回数据 │
└─────────────────────────────────────────────────────────────────────────────────────┘
七、TCP 三次握手与四次挥手
7.1 三次握手完整流程
客户端 (主动连接) 服务端 (被动连接) ───────────────── ───────────────── CLOSED CLOSED │ │ │ socket() │ socket() │ bind()(可选) │ bind() │ │ listen() │ │ LISTEN │ │ │ connect() │ │ SYN_SENT │ │ │ │ │ └───────── SYN (seq=x) ────────────►│ │ │ SYN_RCVD │ │ │ │ ◄──────── SYN+ACK (seq=y, ack=x+1) ─┘ │ │ │ │ │ ESTABLISHED │ │ │ │ │ │ │ └───────── ACK (ack=y+1) ──────────►│ │ │ │ ESTABLISHED │ │ │ ◄───────── 数据传输 ────────────────► │
7.2 三次握手的意义
| 握手次数 | 方向 | SYN | ACK | 验证的能力 |
|---|---|---|---|---|
| 第一次 | C→S | 1 | 0 | 客户端发送能力、服务端接收能力 |
| 第二次 | S→C | 1 | 1 | 服务端发送能力、客户端接收能力 |
| 第三次 | C→S | 0 | 1 | 确认双方接收正常 |
为什么需要三次握手?
-
防止历史连接请求:网络中延迟的旧 SYN 包可能被服务端误认为是新连接请求
-
同步初始序列号:需要三次交互才能让双方都确认对方的序列号
-
避免资源浪费:二次握手会导致服务端在不确定客户端是否收到 SYN+ACK 时就分配资源
7.3 四次挥手完整流程
主动关闭方 被动关闭方 ──────────────── ──────────────── ESTABLISHED ESTABLISHED │ │ │ close() │ │ FIN_WAIT_1 │ │ │ │ │ └───────── FIN (seq=u) ────────────►│ │ │ CLOSE_WAIT │ ◄───────── ACK (ack=u+1) ──────────┘ │ │ FIN_WAIT_2 │ │ │ │ │ (可能继续发送数据) │ │ │ │ │ close() │ │ LAST_ACK │ ◄───────── FIN (seq=w) ─────────────┘ │ │ TIME_WAIT │ │ │ (等待 2MSL) │ │ │ │ │ │ │ └───────── ACK (ack=w+1) ──────────►│ │ │ │ CLOSED │ CLOSED │
7.4 四次挥手的意义
| 挥手次数 | 方向 | FIN | ACK | 状态转换 |
|---|---|---|---|---|
| 第一次 | A→B | 1 | 0 | ESTABLISHED → FIN_WAIT_1 |
| 第二次 | B→A | 0 | 1 | ESTABLISHED → CLOSE_WAIT |
| 第三次 | B→A | 1 | 1 | CLOSE_WAIT → LAST_ACK |
| 第四次 | A→B | 0 | 1 | TIME_WAIT → CLOSED |
为什么需要四次挥手?
-
TCP 全双工特性:需要分别关闭两个方向的数据流
-
ACK 和 FIN 触发时机不同:ACK 由内核立即回复,FIN 由应用程序调用 close() 触发
-
被动方可能还有数据要发送:收到 FIN 后只是表示主动方不再发送数据
7.5 关键状态详解
| 状态 | 含义 | 常见问题 |
|---|---|---|
| LISTEN | 服务端等待连接 | 正常 |
| SYN_RECV | 收到 SYN,等待 ACK | 半连接队列满(SYN Flood) |
| ESTABLISHED | 连接已建立 | 正常 |
| FIN_WAIT_2 | 主动方等待被动方 FIN | 可调 tcp_fin_timeout |
| TIME_WAIT | 等待 2MSL(60秒) | 过多需启用 tcp_tw_reuse |
| CLOSE_WAIT | 被动方等待 close() | ⚠️ 应用未调用 close() |
| LAST_ACK | 等待最后 ACK | 正常 |
7.6 四次挥手异常场景分析
乱序导致超时重传
同时关闭的场景,server 和 client 几乎同时发送 fin 包。 问题现象: 1. client 先收到 server 的 fin 包,并回传 ack 包 2. server 处发生乱序,先收到 client 的 ack 包,后收到 fin 包 3. server 未能正确处理 client 的 fin 包,未能返回正确的 ack 包 4. client 没收到 ack,等待超时后重传 fin 包,之后才正常关闭 根本原因:内核在 FIN_WAIT_1 状态收到乱序包时,对 fin 包的处理有特定逻辑, 导致 fin 包未被正确接收,需要超时重传。
7.7 SYN Cookie 防攻击机制
当半连接队列满时,内核启用 SYN Cookie 机制:
// net/ipv4/syncookies.c // 收到 SYN 时,不立即分配资源 // 根据源/目的 IP、端口、时间等计算 cookie 值作为 seq // 第三次握手时验证 cookie 有效性,确认后才分配资源
八、TCP 拥塞控制算法体系
8.1 拥塞控制算法架构
Linux 5.10 实现了可插拔的拥塞控制框架:
net/ipv4/tcp_cong.c # 拥塞控制框架核心 net/ipv4/tcp_cubic.c # CUBIC(默认算法) net/ipv4/tcp_bbr.c # BBR(Google) net/ipv4/tcp_nv.c # NV(数据中心优化) net/ipv4/tcp_vegas.c # Vegas(延迟感知) net/ipv4/tcp_dctcp.c # DCTCP(数据中心 ECN) net/ipv4/tcp_westwood.c # Westwood(无线优化) net/ipv4/tcp_hybla.c # Hybla(卫星链路)
8.2 拥塞控制算法对比
| 算法 | 拥塞检测方式 | 适用场景 | 特点 |
|---|---|---|---|
| CUBIC | 丢包 | 通用(默认) | 三次函数窗口增长,高带宽友好 |
| BBR | 延迟/带宽 | 高延迟高带宽 | 基于带宽估计,不依赖丢包 |
| Reno | 丢包 | 传统网络 | AIMD,基础算法 |
| Vegas | RTT变化 | 低延迟要求 | 主动避免拥塞,提前降速 |
| NV | 队列积压 | 数据中心 | Vegas后继,预测 cwnd |
| DCTCP | ECN标记 | 数据中心 | 配合 ECN,低延迟 |
| Westwood | 带宽估计 | 无线网络 | 利用 ACK 速率估计带宽 |
| Hybla | 丢包 | 卫星/长胖网络 | 补偿高延迟环境 |
8.3 TCP-NV 算法详解
TCP-NV 是专门为数据中心设计的拥塞控制算法:
// net/ipv4/tcp_nv.c static int nv_pad __read_mostly = 10; // 允许的最大网络排队包数 static int nv_cong_dec_mult __read_mostly = 30 * 128 / 100; // 拥塞降窗 30% static int nv_loss_dec_factor __read_mostly = 819; // 丢包时窗口乘数(≈80%) static int nv_rtt_factor __read_mostly = 128; // RTT 平均因子(1/2+1/2)
NV 核心机制:
-
检测队列积压而非丢包(类似 Vegas)
-
当 RTT 超过 base_rtt 时判定为拥塞
-
按比例减少 cwnd
-
适用于全 NV 流的数据中心环境
九、CAKE 队列管理(Common Applications Kept Enhanced)
CAKE 是一个先进的队列管理算法,结合了流量整形、AQM 和公平队列:
// net/sched/sch_cake.c
#define CAKE_SET_WAYS (8)
#define CAKE_MAX_TINS (8)
#define CAKE_QUEUES (1024)
struct cobalt_params {
u64 interval; // codel 初始丢包率
u64 target; // 最大持续停留时间
u64 mtu_time; // 最大包串行化延迟
u32 p_inc; // blue 丢包概率增量
u32 p_dec; // blue 丢包概率减量
};
CAKE 核心特性:
-
带宽整形器:赤字模式,无需突发参数
-
Diffserv 感知优先级队列:按优先级分类
-
Flow Queue 隔离:防止单流增加延迟
-
Cobalt AQM:Codel + Blue 组合算法
十、数据包发送路径
用户空间 send()/write() │ ▼ sock_sendmsg() [net/socket.c] │ ▼ inet_sendmsg() [net/ipv4/af_inet.c] │ ▼ tcp_sendmsg() [net/ipv4/tcp.c] │ ├── alloc_skb() [net/core/skbuff.c] // 分配 SKB ├── skb_copy_from_user() // 复制用户数据 └── tcp_push() [net/ipv4/tcp.c] │ ▼ tcp_write_xmit() [net/ipv4/tcp_output.c] │ ├── 拥塞控制检查(cwind > snd_cwnd) ├── tcp_transmit_skb() // 构建 TCP 头 │ ▼ ip_queue_xmit() [net/ipv4/ip_output.c] │ ├── ip_route_output_flow() // 查找路由 ├── ip_finish_output() │ ▼ neigh_output() [net/core/neighbour.c] │ ├── 查找邻居缓存 ├── 如果 MAC 未知 → 触发 ARP 请求 │ ▼ dev_queue_xmit() [net/core/dev.c] │ ├── 选择发送队列 (netdev_pick_tx) ├── 应用 QoS 规则 (QDisc) │ ▼ __dev_xmit_skb() [net/sched/sch_generic.c] │ ├── qdisc_enqueue_root() // 入队 └── __qdisc_run() // 出队 │ └── qdisc_restart() │ ▼ dev_hard_start_xmit() [net/core/dev.c] │ ▼ ndo_start_xmit() [驱动层] │ ├── 将 skb 映射到 DMA 地址 ├── 将发送描述符加入 TX Ring Buffer └── 通知网卡发送
十一、关键调优参数汇总
11.1 驱动/网卡层
| 参数 | 查看/修改命令 | 默认值 | 作用 |
|---|---|---|---|
| RX 队列数量 | ethtool -l eth0 |
硬件决定 | 多队列并行收包 |
| RX 队列大小 | ethtool -g eth0 |
512 | Ring buffer 大小 |
| 中断合并 | ethtool -c eth0 |
动态 | 降低中断频率 |
| 硬件卸载 | ethtool -k eth0 |
开启 | TSO/GRO/RX 校验和 |
11.2 软中断/NAPI 层
| 参数 | 路径 | 默认值 | 作用 |
|---|---|---|---|
netdev_budget |
/proc/sys/net/core/netdev_budget |
300 | 单次软中断处理包上限 |
netdev_budget_usecs |
/proc/sys/net/core/netdev_budget_usecs |
2000 | 单次软中断时间上限(us) |
gro_normal_batch |
/proc/sys/net/core/gro_normal_batch |
8 | GRO 批量提交大小 |
11.3 TCP 协议栈层
| 参数 | 路径 | 默认值 | 作用 |
|---|---|---|---|
tcp_rmem |
/proc/sys/net/ipv4/tcp_rmem |
4096 87380 6291456 | TCP 接收缓冲区 |
tcp_wmem |
/proc/sys/net/ipv4/tcp_wmem |
4096 16384 4194304 | TCP 发送缓冲区 |
tcp_congestion_control |
/proc/sys/net/ipv4/tcp_congestion_control |
cubic | 拥塞控制算法 |
tcp_max_syn_backlog |
/proc/sys/net/ipv4/tcp_max_syn_backlog |
1024 | 半连接队列大小 |
somaxconn |
/proc/sys/net/core/somaxconn |
128 | 全连接队列大小 |
tcp_fin_timeout |
/proc/sys/net/ipv4/tcp_fin_timeout |
60 | FIN_WAIT_2 超时(秒) |
tcp_syn_retries |
/proc/sys/net/ipv4/tcp_syn_retries |
6 | SYN 重试次数 |
11.4 RPS/RFS 层
| 参数 | 路径 | 默认值 | 作用 |
|---|---|---|---|
rps_cpus |
/sys/class/net/eth0/queues/rx-0/rps_cpus |
0 | RPS CPU 掩码 |
rps_flow_cnt |
/sys/class/net/eth0/queues/rx-0/rps_flow_cnt |
0 | RFS 流表大小 |
rps_sock_flow_entries |
/proc/sys/net/core/rps_sock_flow_entries |
0 | 全局 RFS 流表 |
十二、监控与调试工具链
12.1 关键监控指标
| 监控层级 | 指标 | 数据源 | 异常阈值 |
|---|---|---|---|
| 硬件层 | CRC 错误、FCS 错误 | ethtool -S eth0 |
> 0 表示链路质量问题 |
| 驱动层 | RX dropped、ring full | /sys/class/net/eth0/statistics/rx_dropped |
持续增长需增大 ring buffer |
| 软中断层 | time_squeeze | /proc/net/softnet_stat 第2列 |
持续增长需调整 budget |
| 协议栈 | ListenOverflows/ListenDrops | netstat -s |
增长需增大 backlog |
| TCP层 | 重传段、丢包重传 | netstat -s |
增长需检查链路或调优 |
12.2 /proc/net/softnet_stat 详解
$ cat /proc/net/softnet_stat 0002b45a 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 0002c4e3 00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000
| 列 | 含义 | 解读 |
|---|---|---|
| 1 | 处理的总包数 | 正常增长 |
| 2 | time_squeeze | net_rx_action 预算耗尽次数 ⚠️ 高表示 CPU 过载 |
| 3 | CPU collision | CPU 冲突次数 |
| 4 | 接收包数 | 实际接收包数 |
12.3 监控命令速查
# 硬件/驱动层
ethtool -S eth0 | grep -E "drop|error"
cat /sys/class/net/eth0/statistics/rx_dropped
# 软中断层
cat /proc/net/softnet_stat | awk '{print $2}' # time_squeeze
cat /proc/softirqs | grep NET_RX
# 协议栈
netstat -s | grep -E "overflow|dropped|rejected|pruned"
netstat -s | grep -E "retrans|loss"
# Socket 层
ss -tunlp
ss -ti | grep -E "cwnd|rtt"
# 中断分布
cat /proc/interrupts | grep -E "eth|igb|ixgbe"
12.4 性能调优决策树
观测到丢包 │ ├─► `ethtool -S eth0` 有 CRC/FIFO 错误 │ └─► 硬件问题:检查网卡、线缆、交换机 │ ├─► `/sys/class/net/eth0/statistics/rx_dropped` 增长 │ └─► 驱动层丢包:增大 ring buffer (ethtool -G) │ ├─► `/proc/net/softnet_stat` time_squeeze 增长 │ └─► 软中断过载:增大 netdev_budget,或开启 RPS/RFS │ ├─► `netstat -s` ListenDrops 增长 │ └─► 应用层过载:增大 net.core.somaxconn │ └─► `netstat -s` TCP 重传增长 └─► 网络拥塞:检查带宽,考虑更换拥塞控制算法
12.5 动态追踪(ftrace/kprobe)
# 追踪特定函数调用 echo tcp_rcv_established > /sys/kernel/debug/tracing/set_ftrace_filter echo function > /sys/kernel/debug/tracing/current_tracer cat /sys/kernel/debug/tracing/trace # 使用 kprobe 追踪函数参数 echo 'p:my_tcp_ack tcp_ack ack=%r2' > /sys/kernel/debug/tracing/kprobe_events echo 1 > /sys/kernel/debug/tracing/events/kprobes/my_tcp_ack/enable # perf 追踪网络 tracepoint perf record -e net:netif_receive_skb -a -g -- sleep 10 perf report # 追踪所有网络事件 echo 1 > /sys/kernel/debug/tracing/events/enable cat /sys/kernel/debug/tracing/trace
十三、总结
| 分析维度 | 核心内容 |
|---|---|
| 源码目录 | net/ 下有 core/, ipv4/, ipv6/, netfilter/, sched/ 等 |
| 核心数据结构 | sk_buff, sock, tcp_sock, net_device, softnet_data |
| 初始化流程 | net_dev_init() Per-CPU 初始化、PCI 驱动探测、通知链 |
| NAPI 生命周期 | 硬中断 → 软中断 → poll 轮询 → budget 控制 |
| GRO 批量处理 | gro_normal_batch 控制批量提交 |
| 硬件卸载 | TSO/GRO/RSS/RFS/aRFS,多队列并行处理 |
| TCP 三次握手 | SYN → SYN+ACK → ACK,状态转换,SYN Cookie 防攻击 |
| TCP 四次挥手 | FIN → ACK → FIN → ACK,TIME_WAIT 意义,异常场景 |
| 拥塞控制算法 | CUBIC/BBR/NV/Vegas/DCTCP,可插拔框架 |
| CAKE 队列 | 带宽整形 + Diffserv + Flow Queue + Cobalt AQM |
| 关键调优参数 | netdev_budget, tcp_rmem, tcp_congestion_control 等 |
| 监控指标 | softnet_stat, ethtool -S, netstat -s |
| 调试工具 | ftrace、kprobe、perf、tcpdump |
更多推荐


所有评论(0)