关于Linux内核TCP校验和与skb字段及校验更新逻辑的技术问询
问题背景
我在tc-ingress eBPF程序中修改了一个即将被路由送出主机的数据包,具体是通过bpf_skb_adjust_room/ctx_adjust_hroom添加自定义TCP选项来扩展数据包。
内核中会在上述函数内部调用以下代码:
static inline void __skb_reset_checksum_unnecessary(struct sk_buff *skb) { if (skb->ip_summed == CHECKSUM_UNNECESSARY) { skb->ip_summed = CHECKSUM_NONE; skb->csum_level = 0; } }
我观察到一个现象:即使我手动将TCP头的校验和字段设为0或随机值,Linux内核后续也会自动把它更新为正确的值。补充说明:我使用的是Cilium K8S CNI中的bpf_lxc。
疑问
skb->csum与TCP头的校验和字段之间是什么关系?- Linux内核会在哪个位置修改TCP校验和字段?
ip_summed = CHECKSUM_NONE是否参与此过程?
解答
1. skb->csum与TCP头校验和字段的关系
skb->csum是sk_buff结构体中的部分校验和缓存字段,主要用于存储伪头部+TCP头部/数据的累加和,目的是避免重复计算校验和,提升内核处理性能。- TCP头的校验和字段是最终符合TCP协议规范的校验和值,它本质上是
skb->csum的补码(经过进位处理后)。 - 不同
ip_summed状态下两者的关联不同:- 当
ip_summed为CHECKSUM_COMPLETE时,skb->csum代表整个TCP段(伪头+头部+数据)的校验和,内核只需取其补码就能得到TCP头的校验和字段值; - 当
ip_summed为CHECKSUM_NONE时,内核会忽略skb->csum,重新计算完整的TCP校验和,最终将结果写入TCP头的校验和字段。
- 当
2. 内核修改TCP校验和的位置及CHECKSUM_NONE的作用
- 内核通常在TCP发送路径的
tcp_transmit_skb函数中处理校验和更新(具体位置可能因内核版本略有差异)。当准备发送TCP段时,内核会检查ip_summed标志:- 如果
ip_summed为CHECKSUM_NONE,内核会调用tcp_checksum_complete等函数,重新计算包含伪头部、TCP头部和数据的完整校验和,然后将计算结果覆盖TCP头的校验和字段。
- 如果
ip_summed = CHECKSUM_NONE是触发内核重新计算校验和的关键条件:你提到的__skb_reset_checksum_unnecessary函数将ip_summed从CHECKSUM_UNNECESSARY改为CHECKSUM_NONE,这直接告诉内核当前数据包的校验和不可靠,必须重新计算,这也是你观察到TCP校验和被自动修正的原因。
内容的提问来源于stack exchange,提问作者yeyushengfan
相关产品推荐
相关产品推荐

