k8s 环境里 pod 之间 ICMP 通但 TCP 不通,client 收到 synack 却不响应。根因是 server 端 tunl 设备的 tx-checksumming 没生效,发出的包校验和错误,被 client 丢弃。

现象

有个 k8s 的环境,pod 之间 ICMP 是通的,TCP 不通。

client 发送的 SYN 报文,可以收到 server 的 SYNACK 回复,但是 client 并不响应这个 SYNACK 报文。

排查

通过 tcpdump -vvv 抓包,看到 server 发出的包,在 client 端依然 checksum incorrect。

怀疑是 server 端的 tx-checksumming: on 功能对 tunl 设备没有生效。

关闭 server 的该功能后正常:

ethtool -K tunl0 tx off

根因

1. tunl0 是个软件设备

client 和 server 用 tunnel 隧道来通信,这里的 tunl0 是个软件设备。

2. tx-checksumming 的作用

tx-checksumming: on 表示发送侧硬件校验和计算。

如果设置为 on,表示网卡支持该特性,kernel 发包的时候不计算校验和,让网卡发送的时候计算。

ethtool -K eth0 tx-checksum-ip-generic on|off

3. 问题出在哪

server 的 tunl 设备,也支持 tx-checksumming: on 选项。

这一批 server 发出的 SYNACK 报文,从 tunl0 网卡发出来的时候,期待着能在离开网卡的时候,报文的校验和被重新计算为正确的。

也就是 tx-checksumming: on 这个功能生效,但是,没生效,报文校验和依然错误。

4. client 端看到的现象

client 收到的 SYNACK 报文,用 tcpdump -vvv 看到有提示:

  • 如果收到的报文校验和错误,有提示 incorrect
  • 如果正确,有提示 correct

server 端的网卡信息:

root@host:~# ip -d link show dev tunl0
26: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0 promiscuity 0 minmtu 0 maxmtu 0
    ipip any remote any local any ttl inherit nopmtudisc addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

root@host:~# ethtool -k tunl0 | head
Features for tunl0:
rx-checksumming: off [fixed]
tx-checksumming: on
    tx-checksum-ipv4: off [fixed]
    tx-checksum-ip-generic: on
    tx-checksum-ipv6: off [fixed]
    tx-checksum-fcoe-crc: off [fixed]
    tx-checksum-sctp: off [fixed]
scatter-gather: on
    tx-scatter-gather: on

client 端抓包,tcpdump -vvv 加上 -vvv 才能看到 checksum 的结果。

发送的时候,cksum 0x972f (incorrect -> 0x4615) 没关系,因为离开网卡的时候,网卡会计算校验和,符合 tx-checksumming: on 这个功能的预期:

15:27:52.923926 ip: (tos 0x0, ttl 64, id 23082, offset 0, flags [DF], proto TCP (6), length 60)
10.0.0.42.43332 > 10.0.0.4.9400: Flags [S], cksum 0x972f (incorrect -> 0x4615), seq 4089076775, win 64800, options [mss 1440,sackOK,TS val 20860605 ecr 0,nop,wscale 10], length 0

接收的回复,这里 cksum 0xae7a (correct) 是正确的,如果:

15:27:52.924453 ip: (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto TCP (6), length 60)
10.0.0.4.9400 > 10.0.0.42.43332: Flags [S.], cksum 0xae7a (correct), seq 1828286424, ack 4089076776, win 64260, options [mss 1440,sackOK,TS val 3284203794 ecr 20860605,nop,wscale 10], length 0

解决办法

因为这个 tunl 设备是 calico 创建的,想改 tunl 设备还要找好的时机。

可以修改物理机上的真实设备的 ethtool -K eth10 tx off 来解决。

同时要修改 rc.local 将该操作记录到开机启动中。

一点思考

同样的一批 64 台服务器,同样的 tunl 设备通信,有 16 台有问题。

看到这一批网卡跟正常网卡的区别是,异常服务器是定制的网卡,可能跟这个网卡不支持 tunl 设备的校验和计算有关系。

小结

  • pod 之间 ICMP 通但 TCP 不通,优先怀疑校验和问题;
  • tx-checksumming: on 表示让网卡在发送时计算校验和,但软件设备(tunl)上可能不生效;
  • 用 tcpdump -vvv 抓包,看 checksum incorrect / correct 提示;
  • 关闭 ethtool -K tunl0 tx off 可解决;
  • 同型号网卡也可能有差异,异常网卡可能不支持 tunl 设备的校验和计算。