惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
T
Tenable Blog
Webroot Blog
Webroot Blog
L
Lohrmann on Cybersecurity
S
Securelist
S
Schneier on Security
NISL@THU
NISL@THU
Know Your Adversary
Know Your Adversary
C
Cybersecurity and Infrastructure Security Agency CISA
T
The Exploit Database - CXSecurity.com
L
LINUX DO - 热门话题
C
CXSECURITY Database RSS Feed - CXSecurity.com
O
OpenAI News
I
Intezer
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
TaoSecurity Blog
TaoSecurity Blog
S
Secure Thoughts
Application and Cybersecurity Blog
Application and Cybersecurity Blog
P
Privacy International News Feed
H
Hacker News: Front Page
N
Netflix TechBlog - Medium
M
MIT News - Artificial intelligence
博客园 - Franky
PCI Perspectives
PCI Perspectives
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Microsoft Azure Blog
Microsoft Azure Blog
MongoDB | Blog
MongoDB | Blog
L
LangChain Blog
P
Proofpoint News Feed
S
Security Affairs
WordPress大学
WordPress大学
The Last Watchdog
The Last Watchdog
S
SegmentFault 最新的问题
小众软件
小众软件
F
Full Disclosure
博客园 - 叶小钗
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
The Blog of Author Tim Ferriss
Simon Willison's Weblog
Simon Willison's Weblog
P
Palo Alto Networks Blog
Security Latest
Security Latest
P
Proofpoint News Feed
月光博客
月光博客
T
Tailwind CSS Blog
Scott Helme
Scott Helme
Hacker News - Newest:
Hacker News - Newest: "LLM"
Google Online Security Blog
Google Online Security Blog
T
Threat Research - Cisco Blogs
Help Net Security
Help Net Security
Project Zero
Project Zero

土法炼钢兴趣小组的算法知识备份

国密算法与国密 TLS 系列索引 【系统架构设计】架构质量属性:不只是"高可用高性能" 【系统架构设计百科】告警策略:如何避免"狼来了" 【系统架构设计】CQRS:读写分离的架构哲学 【系统架构设计】空间架构:极端扩展场景的解法 【系统架构设计】微服务架构深度审视:优势、代价与适用边界 【系统架构设计】扩展性原理:水平、垂直与对角扩展 【系统架构设计】无状态设计:扩展的第一步也是最难的一步 【系统架构设计】缓存架构:从本地到分布式的多级缓存体系 【系统架构设计】管道与过滤器:Unix 哲学的架构表达 【系统架构设计】复杂性管理:架构的核心战场 【系统架构设计】消息队列架构:异步解耦的设计与陷阱 【系统架构设计】CDN 架构:全球加速的设计原理 【系统架构设计】连接池设计:被忽视的性能杀手 【系统架构设计】弹性设计模式:熔断器、舱壁与超时 【系统架构设计】高可用设计模式:冗余、故障转移与仲裁 【系统架构设计】容量规划:从拍脑袋到数据驱动 【系统架构设计】数据库扩展:分库分表的工程实践与替代方案 【系统架构设计】SLO 工程:可靠性的量化管理 【系统架构设计】性能建模:用数学思维分析系统瓶颈 【系统架构设计】混沌工程:主动验证系统的韧性 【系统架构设计】零拷贝与内存映射:数据搬运的极致优化 【系统架构设计】线程模型:从 thread-per-request 到协程 【系统架构设计】容灾架构:多活与灾备设计 【系统架构设计】数据库性能模式:索引、查询与连接管理 【系统架构设计】数据建模:从关系范式到文档模型的真实权衡 【系统架构设计】吞吐量优化:批处理、流水线与并发模型 【系统架构设计】流处理架构:从批处理到实时的范式迁移 【系统架构设计】搜索引擎架构:倒排索引之上的系统设计 【系统架构设计】时序数据架构:监控与 IoT 的存储设计 【系统架构设计】数据迁移与版本化:在线不停机的数据演进 【系统架构设计】数据湖与数据仓库:分析架构的演进路线 【系统架构设计】API 网关设计:入口层的职责边界 【系统架构设计】应用层数据一致性模式:在正确性与性能之间走钢丝 【系统架构设计】多模数据库选型:Polyglot Persistence 的工程实践 【系统架构设计】服务发现与注册:动态拓扑的基础设施 【系统架构设计】配置管理架构:从配置文件到配置中心 【系统架构设计】全链路压测:大规模系统的性能验证 【系统架构设计】幂等性设计:分布式环境下的安全重试 【系统架构设计】契约测试与 Schema 演进:服务间的信任协议 【系统架构设计】长连接与推送架构:WebSocket、SSE 与 MQTT 【系统架构设计】延迟分析:从 P50 到 P999 的全链路追踪 【系统架构设计百科】DDD 战术模式:聚合、实体与值对象 【系统架构设计百科】防腐层与开放主机服务:系统集成的 DDD 方案 【系统架构设计百科】领域事件与事件风暴:从业务到架构的桥梁 【系统架构设计百科】CQRS + Event Sourcing 完整实战:从领域建模到部署 【系统架构设计百科】DDD 与微服务:用领域模型划分服务边界 【系统架构设计】DDD 战略设计:限界上下文与上下文映射 【系统架构设计百科】认证架构:从 Session 到 JWT 到 OIDC 【系统架构设计】API 设计哲学:REST vs GraphQL vs gRPC 的真实权衡 排序算法专题:从 TimSort 到并行排序 【密码学百科】国密算法体系:SM2/SM3/SM4/SM9 全景解读 【密码学百科】承诺方案:Pedersen 承诺、向量承诺与多项式承诺 【密码学百科】不经意传输与隐私信息检索:OT、OT 扩展与 PIR 【密码学百科】门限密码学:门限签名、门限解密与分布式密钥生成 完美哈希:从理论到 gperf 实践 【密码学百科】安全多方计算:从 Yao 的混淆电路到实用 MPC 【密码学百科】同态加密:从 Paillier 到全同态加密(FHE) 【密码学百科】零知识证明系统:zk-SNARKs、zk-STARKs 与 Bulletproofs 【密码学百科】概率论与密码分析:生日攻击、差分分析与线性分析 【密码学百科】计算复杂性与归约:密码安全性证明的基石 【密码学百科】秘密共享:Shamir 方案、VSS 与安全多方计算入口 【密码学百科】椭圆曲线代数:Weierstrass 方程、点群运算与曲线选择 【密码学百科】离散对数与配对密码学:从 DLP 到 BLS 签名 【密码学百科】格密码数学基础:SVP、LWE 与格基约化 【密码学百科】抽象代数:群、环、域的密码学视角 【密码学百科】有限域算术:GF(2^n) 运算与在 AES/ECC 中的应用 【密码学百科】数论进阶:二次剩余、椭圆曲线上的 Weil 配对 【密码学百科】密码学简史:从凯撒密码到量子时代 【密码学百科】威胁模型与安全目标:CIA 三要素之外 【密码学百科】Kerckhoffs 原则与现代密码设计哲学 【密码学百科】随机性:密码学的基石 【密码学百科】信息论入门:熵、完美保密与 Shannon 定理 【密码学百科】分组密码原理:Feistel 网络与 SPN 结构 【密码学百科】AES 逐步拆解:SubBytes 到 MixColumns 的数学 【密码学百科】分组密码工作模式全览:ECB/CBC/CTR/OFB/CFB 【密码学百科】流密码:RC4 的兴衰与 ChaCha20 的崛起 【密码学百科】密码学哈希函数:MD5→SHA-2→SHA-3 的进化之路 【密码学百科】MAC 与 HMAC:消息认证的正确姿势 【密码学百科】认证加密(AEAD):GCM、ChaCha20-Poly1305 与 OCB 【密码学百科】密钥派生函数:HKDF、PBKDF2、Argon2 与密码存储 【密码学百科】公钥密码的数论基础:模运算、群、原根 【密码学百科】RSA 从原理到攻击:教科书 RSA 为什么不安全 【密码学百科】Diffie-Hellman 密钥交换与离散对数问题 【密码学百科】椭圆曲线密码学(ECC):从几何直觉到点群运算 【密码学百科】数字签名:ECDSA、EdDSA 与 Schnorr 签名 【密码学百科】现代密钥交换:X25519、ECDHE 与前向保密 【密码学百科】混合加密与 KEM/DEM 范式:ECIES 与 HPKE 【密码学百科】填充方案:PKCS#1 v1.5、OAEP 与 PSS 【密码学百科】TLS 协议全解析:从握手到 0-RTT 【密码学百科】PKI 与数字证书:信任链的构建与崩塌 【密码学百科】密码认证协议:从 SRP 到 OPAQUE 【密码学百科】零知识证明入门:如何证明你知道而不泄露 【密码学百科】安全信道构造:Noise 协议框架与 Signal 协议 【密码学百科】密钥管理工程:HSM、KMS 与密钥生命周期 【密码学百科】侧信道攻击:从时序攻击到功耗分析 【密码学百科】密码学实现陷阱:三层漏洞分类、审计工具链与系统性预防 密码敏捷性:如何设计可升级的密码系统 【密码学百科】OpenSSL/BoringSSL 架构剖析:ENGINE、Provider 与 FIPS 模块 排序基准测试:用数据说话
【Linux 网络子系统深度拆解】网络丢包定位:从 drop_monitor 到 kfree_skb 追踪
2025-07-24 · via 土法炼钢兴趣小组的算法知识备份

“丢包”是网络故障排查中最常见也最棘手的问题。一个包可能在内核的几十个位置被丢弃——网卡 ring buffer 满、校验和错误、Netfilter 规则拒绝、socket 缓冲区溢出、TCP 窗口外、XDP 程序丢弃、qdisc 队列满……传统的 ifconfig/proc/net/dev 只能看到笼统的 rx_dropped / tx_dropped 计数器,无法回答”在哪个内核函数、因为什么原因丢的”这个关键问题。

Linux 内核从 2.6 时代就引入了 drop_monitor 子系统,而 5.17+ 进一步增强了 kfree_skb tracepoint——现在每次丢包都携带精确的 drop_reason 枚举值。本文从内核源码出发,完整拆解丢包追踪的工具链和实战方法。

Linux 丢包追踪体系全景

1.1 kfree_skb vs consume_skb

内核中释放 sk_buff 有两个路径:

  • kfree_skb_reason(skb, reason):表示包被异常丢弃(如校验和错误、规则拒绝),触发 kfree_skb tracepoint
  • consume_skb(skb):表示包被正常消费(如应用已读取),触发 consume_skb tracepoint

两者的区别是语义上的——kfree_skb 意味着”这个包没有到达它的预期目的地”,是丢包追踪的核心。

1.2 kfree_skb tracepoint 结构

/* include/trace/events/skb.h:24 */
TRACE_EVENT(kfree_skb,
    TP_PROTO(struct sk_buff *skb, void *location,
             enum skb_drop_reason reason),
    TP_ARGS(skb, location, reason),
    TP_STRUCT__entry(
        __field(void *,             skbaddr)
        __field(void *,             location)    /* 丢包的内核函数地址 */
        __field(unsigned short,     protocol)    /* 协议号(ETH_P_IP 等) */
        __field(enum skb_drop_reason, reason)    /* 丢弃原因枚举 */
    ),
    ...
);

三个关键字段:

  • location:调用 kfree_skb_reason() 的内核函数地址,通过 ksym()/proc/kallsyms 解析为函数名
  • protocol:被丢弃包的以太网协议号
  • reasonenum skb_drop_reason 枚举值,精确标识丢弃原因

1.3 consume_skb tracepoint

/* include/trace/events/skb.h:54 */
TRACE_EVENT(consume_skb,
    TP_PROTO(struct sk_buff *skb, void *location),
    TP_ARGS(skb, location),
    TP_STRUCT__entry(
        __field(void *, skbaddr)
        __field(void *, location)
    ),
    ...
);

consume_skb 没有 reason 字段——正常消费不需要理由。在追踪中,可以通过同时监听两个 tracepoint 来区分正常消费和异常丢弃。

二、drop_reason:80+ 种丢包原因全解

2.1 核心丢包原因枚举

enum skb_drop_reason 定义在 include/net/dropreason-core.h,通过 DEFINE_DROP_REASON 宏展开。以下按网络栈层次分类:

L2/驱动层丢包:

reason 含义 常见触发场景
FULL_RING 网卡 ring buffer 满 网卡接收速率超过 CPU 处理速率
CPU_BACKLOG per-CPU backlog 队列满 RPS/RFS 分发后目标 CPU 处理不过来
DEV_HDR 设备头部错误 VLAN 头、PPP 头异常
DEV_READY 设备未就绪 接口 down 或正在初始化
OTHERHOST 包不属于本机 混杂模式下收到他人的包

L3(IP)层丢包:

reason 含义 常见触发场景
IP_CSUM IP 校验和错误 硬件卸载故障或链路噪声
IP_INHDR IP 头部异常 头部长度、版本号错误
IP_RPFILTER 反向路径过滤失败 非对称路由且 rp_filter=1
IP_NOPROTO 不支持的 IP 协议 未加载对应协议模块
IP_INADDRERRORS 目标地址错误 非本机地址且未开启转发
IP_INNOROUTES 无路由 路由表中无匹配项
IP_OUTNOROUTES 出口无路由 发送时找不到出口路由
PKT_TOO_BIG 包太大(超过 MTU) MTU 不匹配且 DF 位置位
PKT_TOO_SMALL 包太小 小于最小合法长度

L4(TCP)层丢包:

reason 含义 常见触发场景
TCP_CSUM TCP 校验和错误 链路损坏或中间设备修改
TCP_FLAGS TCP 标志位异常 无效的标志组合
TCP_ZEROWINDOW 零窗口 接收端窗口为 0
TCP_OLD_DATA 旧数据 已确认的数据重复到达
TCP_OVERWINDOW 超出接收窗口 序列号超出接收窗口范围
TCP_OFOMERGE OFO 队列合并丢弃 乱序队列中的重叠段
TCP_RFC7323_PAWS PAWS 时间戳检查失败 时间戳回退
TCP_OLD_SEQUENCE 旧序列号 重传的旧包
TCP_INVALID_SEQUENCE 无效序列号 不在预期范围内
TCP_RESET RST 包 连接被重置
TCP_INVALID_SYN 无效 SYN SYN 到非 LISTEN socket
TCP_CLOSE 连接已关闭 包到达已关闭的 socket
TCP_FASTOPEN TFO 失败 Fast Open cookie 不匹配
TCP_OLD_ACK 旧 ACK ACK 号低于 snd_una
TCP_TOO_OLD_ACK 过旧 ACK ACK 严重滞后
TCP_ACK_UNSENT_DATA ACK 未发送数据 ACK 号超过 snd_nxt
TCP_OFO_QUEUE_PRUNE OFO 队列修剪 内存压力下清理乱序队列
TCP_OFO_DROP OFO 队列丢弃 乱序队列满
TCP_MINTTL TTL 太小 TTL 低于 min_ttl(BGP 安全)

L4(UDP)层丢包:

reason 含义 常见触发场景
UDP_CSUM UDP 校验和错误 数据损坏
NO_SOCKET 找不到目标 socket 无进程监听该端口

Socket/缓冲区丢包:

reason 含义 常见触发场景
SOCKET_RCVBUFF socket 接收缓冲区满 应用读取太慢,SO_RCVBUF 太小
PROTO_MEM 协议内存限制 net.ipv4.udp_mem 等达到上限
SOCKET_BACKLOG backlog 队列满 net.core.somaxconnlisten() backlog 不足
SOCKET_FILTER socket filter 丢弃 BPF socket filter 返回 0

防火墙/策略丢包:

reason 含义 常见触发场景
NETFILTER_DROP Netfilter 丢弃 iptables/nftables DROP 规则
XDP XDP 程序丢弃 XDP 返回 XDP_DROP
TC_INGRESS TC ingress 丢弃 TC BPF 返回 TC_ACT_SHOT
TC_EGRESS TC egress 丢弃 TC egress filter 丢弃
BPF_CGROUP_EGRESS cgroup BPF 丢弃 cgroup egress 策略拒绝
XFRM_POLICY IPsec 策略失败 XFRM 策略检查不通过

队列/调度丢包:

reason 含义 常见触发场景
QDISC_DROP qdisc 丢弃 队列满(pfifo_fast/fq_codel/HTB)
QUEUE_PURGE 队列清除 qdisc 被删除或重置

2.2 子系统扩展机制

Linux 6.x 引入了 enum skb_drop_reason_subsys 允许各子系统注册自己的丢包原因:

/* include/net/dropreason.h */
enum skb_drop_reason_subsys {
    SKB_DROP_REASON_SUBSYS_CORE,              /* 核心丢包原因 */
    SKB_DROP_REASON_SUBSYS_MAC80211_UNUSABLE, /* Wi-Fi 不可用帧 */
    SKB_DROP_REASON_SUBSYS_MAC80211_MONITOR,  /* Wi-Fi 监控帧 */
    SKB_DROP_REASON_SUBSYS_OPENVSWITCH,       /* Open vSwitch */
    SKB_DROP_REASON_SUBSYS_NUM,
};

void drop_reasons_register_subsys(enum skb_drop_reason_subsys subsys,
                                  const struct drop_reason_list *list);
void drop_reasons_unregister_subsys(enum skb_drop_reason_subsys subsys);

这意味着 Open vSwitch、mac80211 等子系统可以定义自己的丢包原因,通过同一个 kfree_skb tracepoint 报告。

三、drop_monitor 子系统

3.1 工作原理

drop_monitor 是内核的 netlink 子系统,专门用于监控丢包事件。它注册了 kfree_skb tracepoint 的回调,将丢包事件通过 netlink socket 发送到用户态。

相比直接使用 bpftrace 追踪 kfree_skb tracepoint,drop_monitor 的优势在于:

  1. 聚合:在内核态聚合相同位置的丢包事件,减少 netlink 消息量
  2. 硬件丢包:可以通过 devlink trap 接收网卡硬件报告的丢包
  3. 标准接口:通过 netlink 提供标准化的丢包监控 API

3.2 使用 dropwatch 工具

dropwatchdrop_monitor 的用户态前端:

# 安装
apt install dropwatch    # Debian/Ubuntu
yum install dropwatch    # RHEL/CentOS

# 启动交互模式
dropwatch -l kas

# 在交互模式中
> start
# 输出示例:
# Initalizing kallsyms db
# 1 drops at tcp_v4_rcv+0x3c (sobject: netfilter)
# 5 drops at __udp4_lib_rcv+0x2a1 (sobject: NO_SOCKET)
# 2 drops at nf_hook_slow+0x4b (sobject: netfilter)
> stop
> exit

dropwatch 输出每个丢包位置的函数名和聚合计数,快速定位丢包热点。

现代智能网卡可以通过 devlink trap 机制报告硬件级丢包:

# 列出网卡支持的 trap 类型
devlink trap list pci/0000:03:00.0

# 启用特定 trap
devlink trap set pci/0000:03:00.0 trap source_mac_is_multicast action trap

# 查看 trap 统计
devlink trap show pci/0000:03:00.0

四、实战:多工具丢包定位

4.1 bpftrace:按原因聚合丢包

最常用的丢包追踪脚本——按 drop_reasonlocation 聚合:

bpftrace -e '
tracepoint:skb:kfree_skb {
    @by_reason[args->reason] = count();
    @by_location[ksym(args->location)] = count();
}
interval:s:10 {
    printf("\n--- Drop reasons ---\n");
    print(@by_reason);
    printf("\n--- Drop locations ---\n");
    print(@by_location);
    clear(@by_reason);
    clear(@by_location);
}
'

按网络设备过滤:

bpftrace -e '
#include <linux/skbuff.h>
tracepoint:skb:kfree_skb {
    $skb = (struct sk_buff *)args->skbaddr;
    if ($skb->dev != 0) {
        $dev = $skb->dev;
        @by_dev[str($dev->name)] = count();
    }
    @by_reason[args->reason] = count();
}
'

4.2 bpftrace:追踪特定原因的丢包调用栈

当发现某个 drop_reason 异常增长时,追踪完整内核调用栈定位根因:

# 追踪 SOCKET_RCVBUFF 丢包的调用栈
bpftrace -e '
tracepoint:skb:kfree_skb
/args->reason == 14/    /* SKB_DROP_REASON_SOCKET_RCVBUFF */
{
    printf("SOCKET_RCVBUFF drop at %s protocol=0x%x\n",
        ksym(args->location), args->protocol);
    print(kstack);
}
'

追踪 Netfilter 丢包并显示五元组:

bpftrace -e '
#include <linux/skbuff.h>
#include <linux/ip.h>
tracepoint:skb:kfree_skb
/args->reason == 6/    /* SKB_DROP_REASON_NETFILTER_DROP */
{
    $skb = (struct sk_buff *)args->skbaddr;
    $iph = (struct iphdr *)($skb->head + $skb->network_header);
    printf("NF_DROP: %s -> %s proto=%d\n",
        ntop(AF_INET, &$iph->saddr),
        ntop(AF_INET, &$iph->daddr),
        $iph->protocol);
}
'

4.3 perf:记录丢包事件

# 记录 60 秒的丢包事件及调用栈
perf record -a -g --call-graph dwarf \
    -e 'skb:kfree_skb' -- sleep 60

# 查看丢包栈
perf script | head -200

# 按丢包函数统计
perf report --sort comm,dso,symbol

# 按 drop_reason 统计
perf script -F event,trace | grep 'reason:' | \
    awk -F'reason: ' '{print $2}' | sort | uniq -c | sort -rn

4.4 ftrace:最轻量追踪

在无法安装 bpftrace 的环境中(如最小化容器):

# 启用 kfree_skb tracepoint
echo 1 > /sys/kernel/debug/tracing/events/skb/kfree_skb/enable

# 实时查看
cat /sys/kernel/debug/tracing/trace_pipe
# 输出示例:
# <idle>-0  [003] ..s1 12345.678: kfree_skb: skbaddr=0xffff... protocol=2048
#     location=tcp_v4_rcv+0x1a3 reason: TCP_INVALID_SEQUENCE

# 过滤特定原因(通过 filter 文件)
echo 'reason == 2' > /sys/kernel/debug/tracing/events/skb/kfree_skb/filter

# 关闭
echo 0 > /sys/kernel/debug/tracing/events/skb/kfree_skb/enable

4.5 nstat 和 /proc/net/snmp:统计计数器

在深入追踪之前,先通过统计计数器确认丢包方向:

# TCP 相关统计
nstat -az | grep -i 'drop\|error\|fail\|overflow\|prune\|retrans'

# 关键计数器解读
# TcpExtListenDrops     - SYN 队列满丢弃
# TcpExtListenOverflows - Accept 队列满溢出
# TcpExtTCPBacklogDrop  - socket backlog 满丢弃
# TcpExtTCPMinTTLDrop   - TTL 太小丢弃
# TcpExtTCPRcvQDrop     - 接收队列满丢弃
# TcpExtTCPOFODrop      - OFO 队列丢弃
# TcpExtTCPRetransFail  - 重传失败
# TcpExtPFMemallocDrop  - 内存压力丢弃

# UDP 丢包
cat /proc/net/snmp | grep Udp
# Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors
# InErrors = 校验和错误等
# RcvbufErrors = 接收缓冲区满丢弃
# NoPorts = 无监听端口

# 网卡级丢包
cat /proc/net/dev | awk 'NR>2{print $1, "rx_drop="$5, "tx_drop="$13}'

# ethtool 统计
ethtool -S eth0 | grep -i 'drop\|error\|discard\|miss\|overflow'

五、常见丢包场景诊断手册

5.1 网卡 ring buffer 满(FULL_RING)

症状ethtool -S 显示 rx_missed_errorsrx_no_buffer_count 增长。

诊断

bpftrace -e '
tracepoint:skb:kfree_skb /args->reason == 67/ {  /* FULL_RING */
    @by_cpu[cpu] = count();
}
interval:s:5 { print(@by_cpu); clear(@by_cpu); }
'

解决

# 增大 ring buffer
ethtool -G eth0 rx 4096

# 检查当前 ring buffer 大小
ethtool -g eth0

5.2 socket 接收缓冲区满(SOCKET_RCVBUFF)

症状nstat 显示 TcpExtTCPRcvQDropUdpRcvbufErrors 增长。

诊断

bpftrace -e '
tracepoint:skb:kfree_skb /args->reason == 14/ {  /* SOCKET_RCVBUFF */
    printf("RCVBUF full at %s\n", ksym(args->location));
    @stacks = count();
}
'

解决

# 增大默认接收缓冲区
sysctl -w net.core.rmem_default=262144
sysctl -w net.core.rmem_max=16777216

# 或应用层设置 SO_RCVBUF

5.3 Netfilter/iptables 丢弃(NETFILTER_DROP)

症状iptables -L -v -n 显示 DROP 规则匹配计数增长。

诊断

bpftrace -e '
tracepoint:skb:kfree_skb /args->reason == 6/ {  /* NETFILTER_DROP */
    printf("NF drop at %s\n", ksym(args->location));
    print(kstack(5));
}
'

通过调用栈区分是 INPUT、FORWARD 还是 OUTPUT 链的规则。

5.4 TCP 序列号相关丢弃

症状nstat 显示 TcpExtTCPPAWSEstabTcpExtTCPSACKDiscard 增长。

诊断

bpftrace -e '
tracepoint:skb:kfree_skb
/args->reason >= 33 && args->reason <= 44/  /* TCP_OLD_DATA..TCP_OFO_DROP */
{
    @tcp_drop_reason[args->reason] = count();
}
interval:s:10 { print(@tcp_drop_reason); clear(@tcp_drop_reason); }
'

5.5 qdisc 队列满(QDISC_DROP)

症状tc -s qdisc show dev eth0 显示 dropped 计数增长。

诊断

bpftrace -e '
tracepoint:skb:kfree_skb /args->reason == 57/ {  /* QDISC_DROP */
    printf("qdisc drop at %s\n", ksym(args->location));
    @by_cpu[cpu] = count();
}
'

解决

# 增大 txqueuelen
ip link set dev eth0 txqueuelen 10000

# 或更换 qdisc
tc qdisc replace dev eth0 root fq_codel limit 10240

六、丢包追踪流程总结

6.1 系统化排查流程

第一步:nstat + /proc/net/snmp 快速定界
    ↓ 确认丢包方向(RX/TX)和协议(TCP/UDP)
第二步:ethtool -S 检查网卡级丢包
    ↓ 排除硬件/驱动层问题
第三步:bpftrace kfree_skb 按 reason 聚合
    ↓ 确定具体丢包原因
第四步:bpftrace 追踪该 reason 的调用栈
    ↓ 定位到具体内核函数
第五步:根据函数和原因调整配置
    → ring buffer / rcvbuf / iptables / qdisc / sysctl

6.2 drop_reason 快速查询

在生产环境中,可以使用以下脚本快速获取丢包原因的可读名称:

# 一行命令:统计 10 秒内的丢包原因
bpftrace -e '
tracepoint:skb:kfree_skb {
    @[args->reason] = count();
}
interval:s:10 { exit(); }
' 2>/dev/null

结合 dropreason-core.h 中的枚举定义,将数字映射为名称。或者使用 perf 自动解析:

perf stat -a -e 'skb:kfree_skb' -- sleep 10

6.3 性能开销

方法 开销 适合场景
nstat / /proc/net/snmp 始终开启,快速定界
ethtool -S 始终可查,网卡级
kfree_skb tracepoint + bpftrace ~100-200 ns/丢包 实时诊断
perf record -e skb:kfree_skb ~100 ns/丢包 录制分析
ftrace event ~50-100 ns/丢包 最小化环境
dropwatch ~100 ns/丢包 快速交互式诊断

由于丢包通常是低频事件(相对于正常包处理),追踪开销在绝大多数场景下可以忽略。

参考文献

  1. Linux 内核源码 include/trace/events/skb.hkfree_skbconsume_skb tracepoint 定义
  2. Linux 内核源码 include/net/dropreason-core.henum skb_drop_reason 完整定义(80+ 种原因)
  3. Linux 内核源码 include/net/dropreason.henum skb_drop_reason_subsys 子系统扩展机制
  4. Linux 内核文档 Documentation/networking/kfree_skb_reason.rst:drop reason 文档
  5. Linux 内核源码 net/core/drop_monitor.cdrop_monitor netlink 子系统实现

上一篇内核网络追踪工具箱:bpftrace/perf/ftrace 实战

下一篇内核网络调优方法论:从基准测试到生产验证

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2025-07-22 · linux / networking

【Linux 网络子系统深度拆解】eBPF 网络钩子全景:TC/XDP/socket/cgroup

从内核源码全面拆解 eBPF 在网络子系统中的所有挂载点:TC BPF direct-action 模式与 bpf_mprog 多程序链、XDP 驱动级钩子回顾、socket ops 回调与 TCP 生命周期事件、cgroup BPF 策略控制、sk_msg/sk_skb 的 sockmap 重定向引擎、struct_ops 实现自定义拥塞控制,以及 bpftrace 可观测实战。