问题发现
最近使用ebpf的tc classifier对tcp数据进行处理时,发现tcp头部的校验和仅仅包含伪头部的校验和。
于是我使用ethtool的tx off关闭了硬件卸载,却发现tc classifier获得的tcp校验和仍为tcp伪头部的校验和(此时获得的udp校验和符合预期)
问题分析
排除了硬件卸载的问题(因为已经运行了ethtool -K eth0 tx off,且UDP数据包的校验和没有问题),因此看了一下linux对发送校验和的实现;发现目前tcp_v4_send_checksum的实现如下:
void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr)
{
struct tcphdr *th = tcp_hdr(skb);
th->check = ~tcp_v4_check(skb->len, saddr, daddr, 0);
skb->csum_start = skb_transport_header(skb) - skb->head;
skb->csum_offset = offsetof(struct tcphdr, check);
}
static void __tcp_v4_send_check(struct sk_buff *skb,
__be32 saddr, __be32 daddr)
{
struct tcphdr *th = tcp_hdr(skb);
if (skb->ip_summed == CHECKSUM_PARTIAL) { ///HW CSUM
th->check = ~tcp_v4_check(skb->len, saddr, daddr, 0); ///add IPv4 pseudo header checksum
skb->csum_start = skb_transport_header(skb) - skb->head;
skb->csum_offset = offsetof(struct tcphdr, check);
} else {
th->check = tcp_v4_check(skb->len, saddr, daddr,
csum_partial(th,
th->doff << 2,
skb->csum)); ///ip_summed == CHECKSUM_NONE
}
}
所以就想请教一下,是否表明新版的linux内核中在计算tcp校验和时忽略了网卡的属性而直接尝试将tcp的发送校验和放到下层(ip层或硬件)去实现?这么做的理由是是什么呢?真的合理吗?