https://www.sdnlab.com/17530.html
60 second delayed delivery of packet to pod #1268
https://github.com/coreos/flannel/issues/1268
https://github.com/kubernetes/kubernetes/issues/88986
https://github.com/projectcalico/calico/issues/3145
dev_queue_xmit分析
http://doc.okbase.net/wdscq1234/archive/247929.html
https://blog.csdn.net/wdscq1234/article/details/51926808?utm_source=blogxgwz3
[root@worker1 ~]# cat /proc/net/udp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops
60: 00000000:2118 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 99952 2 ffff9599582d8000 1276
udp端口,8472
offload特性,主要是指将原本在协议栈中进行的IP分片、TCP分段、重组、checksum校验等操作,转移到网卡硬件中进行,降低系统CPU的消耗,提高处理性能。
发送模式
**TSO (tcp-segmentation-offload) **
从名字来看很直观,就是把tcp分段的过程转移到网卡中进行。当网卡支持TSO机制时,可以直接把不超过滑动窗口大小的payload下传给协议栈,即使数据长度大于MSS,也不会在TCP层进行分段,同样也不会进行IP分片,而是直接传送给网卡驱动,由网卡驱动进行tcp分段操作,并执行checksum计算和包头、帧头的生成工作。
**UFO(udp-fragmentation-offload) **
是一种专门针对udp协议的特性,主要机制就是将IP分片的过程转移到网卡中进行,用户层可以发送任意大小的udp数据包(udp数据包总长度最大不超过64k),而不需要协议栈进行任何分片操作。目前貌似没找到有支持UFO机制的网卡,主要是应用在虚拟化设备上。
**GSO(generic-segmentation-offload) **
相对于TSO和UFO,GSO机制是针对所有协议设计的,更为通用。同时,与TSO、UFO不同的是,GSO主要依靠软件的方式实现,对于网卡硬件没有过多的要求。其基本思想就是把数据分片的操作尽可能的向底层推迟直到数据发送给网卡驱动之前,并先检查网卡是否支持TSO或UFO机制,如果支持就直接把数据发送给网卡,否则的话再进行分片后发送给网卡,以此来保证最少次数的协议栈处理,提高数据传输和处理的效率。
接收模式
**LRO/GRO(large-receive-offload) **
在网卡驱动层面上将接受到的多个TCP数据包聚合成一个大的数据包,然后上传给协议栈处理。这样可以减少协议栈处理的开销,提高系统接收TCP数据的能力和效率。
**RSS(Receive Side Scaling) **
具备多个RSS队列的网卡,可以将不同的网络流分成不同的队列,再将这些队列分配到多个CPU核心上进行处理,从而将负荷分散,充分利用多核处理器的能力,提交数据接收的能力和效率。
网卡offload模式的设置
**查看状态 **
ethtool –k 设备名
**设置开关状态 **
ethtool –K 设备名 模式名(缩写)on/off
fixed 项是网卡不支持的功能项目。
tx-udp_tnl-segmentation: 本想通过网卡的这项技术支持硬件加解封装VXLAN报文头来提高vxlan性能,可是集群环境中的网卡不支持,就放弃了。
网卡校验和
https://blog.csdn.net/chenzhjlf/article/details/79228323
高级的网卡(e1000e等千M网卡)的接收,发送的校验和的计算方法是CRC32。
Refs:http://www.wireshark.org/docs/wsug_html_chunked/ChAdvChecksums.html
http://www.intel.com/content/dam/doc/manual/pci-pci-x-family-gbe-controllers-software-dev-manual.pdf
IP校验和
校验和只对头部进行,不包括数据部分。
发送IP包, 计算checksum:
(1)把IP数据报的首部校验和字段设置为0。
(2)把首部看成以16位为单位的数字组成,依次进行二进制反码求和。
(3)把得到的结果存入校验和字段中。
接收IP包,验证checksum:
(1)把首部看成以16位为单位的数字组成,依次进行二进制反码求和,包括校验和字段。
(2)检查计算出的校验和的结果是否为全1。
(3)如果全1,校验是和正确。否则,校验和就是错误的,协议栈要抛弃这个数据包。但不生成差错报文,由上层去发现丢失的数据报并进行重传。
Refs:
http://tools.ietf.org/html/rfc791
UDP校验和
校验和即覆盖头部,也覆盖数据部分。UDP校验和是可选的,而TCP校验和是必须的。
发送包, 计算checksum:
算法和IP头部的校验和计算方法类似:二进制反码求和。但有下面两个区别:
1) 总长度如果是奇数,则自动补齐,并自动填充为0. 填充的部分不发送出去。
2)添加12个字节的伪头部。源地址(4个字节),目的地址(4个字节),0(1个字节),udp协议号(1个字节),udp头部中长度字段值(2个字节)。
如果校验和字段是0,表示不需要计算校验和。
接收包, 验证checksum:
验证checksum是根据udp头部中的length字段值所指向的数据长度进行校验,如果length字段值大于实际的数据长度,那么包在校验前会被丢弃。如果length字段值小于实际的数据长度,则需要裁减数据,并校验。
TCP校验和
校验和即覆盖头部,也覆盖数据部分。TCP校验和是必须的。也包含了12个字节的伪头部。
https://www.sdnlab.com/17530.html
https://cloud.tencent.com/developer/article/1602883
同一时刻的访问,无论哪个客户端的数据包先到达,服务端会及时处理部分客户端的SYN请求,对另一部分客户端的SYN包“视而不见”,如tcpdump数据所示,源端口为56909的SYN请求没有得到响应,同一时间源端口为50212的另一客户端SYN请求马上得到响应。
查看iptables filter表,确认是否有相应规则会导致此丢包行为:
$ sudo iptables-save -t filter
连接跟踪表溢出
除了防火墙本身配置DROP规则外,与防火墙有关的还有连接跟踪表nf_conntrack,Linux为每个经过内核网络栈的数据包,生成一个新的连接记录项,当服务器处理的连接过多时,连接跟踪表被打满,服务器会丢弃新建连接的数据包。
如何确认
通过dmesg可以确认是否有该情况发生:
dmesg |grep nf_conntrack
如果输出值中有“nf_conntrack: table full, dropping packet”,说明服务器nf_conntrack表已经被打满。
通过/proc文件系统查看nf_conntrack表实时状态:
Shell
# 查看nf_conntrack表最大连接数
$ cat /proc/sys/net/netfilter/nf_conntrack_max
# 查看nf_conntrack表当前连接数
$ cat /proc/sys/net/netfilter/nf_conntrack_count
7611
当前连接数远没有达到跟踪表最大值,排除这个因素。
#
通过netstat可以得到因PAWS机制timestamp验证被丢弃的数据包统计:
netstat -s |grep -e "passive connections rejected because of time stamp" -e "packets rejects in established connections because of timestamp"
netstat -s | grep -i acks
cat /proc/net/sockstat
至此,tcp的connect的机制已经很清楚了,如果设置了超时,当syn_retries重传syn次数的累计时间大于超时,那么在超时后返回,否则在syn_retries重传累计时间后返回。
如果目标ip不可达,在5次重试后返回
sysctl net.ipv4.tcp_syn_retries
在unix网络编程里边讲到有两点:
1、保证TCP连接关闭的可靠性。如果最终发送的ACK丢失,被动关闭的一端会重传最终的FIN包,如果执行主动关闭的一端没有维护这个连接的状态信息,会发送RST包响应,导致连接不正常关闭。
2、允许老的重复分组在网络中消逝。假设在一个连接关闭后,发起建立连接的一端(客户端)立即重用原来的端口、IP地址和服务端建立新的连接。老的连接上的分组可能在新的连接建立后到达服务端,TCP必须防止来自某个连接的老的重复分组在连接终止后再现,从而被误解为同一个连接的化身。要实现这种功能,TCP不能给处于TIME_WAIT状态的连接启动新的连接。
TIME_WAIT的时长通常定义成2*MSL,MSL表示报文在网络上存在的最长时间,如果超过这个时间,报文将被丢弃。linux下TIME_WAIT被定义在tcp.h中,时间是60s
$ ethtool -S eth0|grep rx_fifo
$ cat /proc/net/dev
$ ethtool -g eth0
$ ethtool -G eth0 rx 4096 tx 4096
通过查看/proc/net/softnet_stat可以确定是否发生了netdev backlog队列溢出:
$ cat /proc/sys/net/ipv4/conf/eth0/rp_filter
[root@master1 ~]# tcpdump -iany host 10.244.1.4 -nne
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
15:24:05.587809 Out d2:8e:f8:03:00:c4 ethertype IPv4 (0x0800), length 76: 10.244.0.0.56982 > 10.244.1.4.8443: Flags [S], seq 925665057, win 29200, options [mss 1460,sackOK,TS val 196430748 ecr 0,nop,wscale 7], length 0
15:24:06.589745 Out d2:8e:f8:03:00:c4 ethertype IPv4 (0x0800), length 76: 10.244.0.0.56982 > 10.244.1.4.8443: Flags [S], seq 925665057, win 29200, options [mss 1460,sackOK,TS val 196431750 ecr 0,nop,wscale 7], length 0
15:24:08.591743 Out d2:8e:f8:03:00:c4 ethertype IPv4 (0x0800), length 76: 10.244.0.0.56982 > 10.244.1.4.8443: Flags [S], seq 925665057, win 29200, options [mss 1460,sackOK,TS val 196433752 ecr 0,nop,wscale 7], length 0
15:24:12.599777 Out d2:8e:f8:03:00:c4 ethertype IPv4 (0x0800), length 76: 10.244.0.0.56982 > 10.244.1.4.8443: Flags [S], seq 925665057, win 29200, options [mss 1460,sackOK,TS val 196437760 ecr 0,nop,wscale 7], length 0
15:24:20.615731 Out d2:8e:f8:03:00:c4 ethertype IPv4 (0x0800), length 76: 10.244.0.0.56982 > 10.244.1.4.8443: Flags [S], seq 925665057, win 29200, options [mss 1460,sackOK,TS val 196445776 ecr 0,nop,wscale 7], length 0
15:24:36.663745 Out d2:8e:f8:03:00:c4 ethertype IPv4 (0x0800), length 76: 10.244.0.0.56982 > 10.244.1.4.8443: Flags [S], seq 925665057, win 29200, options [mss 1460,sackOK,TS val 196461824 ecr 0,nop,wscale 7], length 0
15:25:08.727726 Out d2:8e:f8:03:00:c4 ethertype IPv4 (0x0800), length 76: 10.244.0.0.56982 > 10.244.1.4.8443: Flags [S], seq 925665057, win 29200, options [mss 1460,sackOK,TS val 196493888 ecr 0,nop,wscale 7], length 0
15:25:08.728807 In e6:8f:09:d5:16:6f ethertype IPv4 (0x0800), length 76: 10.244.1.4.8443 > 10.244.0.0.56982: Flags [S.], seq 496037272, ack 925665058, win 27960, options [mss 1410,sackOK,TS val 196484874 ecr 196493888,nop,wscale 7], length 0
[root@worker1 ~]# tcpdump -iany host 10.244.1.4 and host 10.244.0.0 and port 8443 -nne
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
15:25:08.728248 In d2:8e:f8:03:00:c4 ethertype IPv4 (0x0800), length 76: 10.244.0.0.56982 > 10.244.1.4.8443: Flags [S], seq 925665057, win 29200, options [mss 1460,sackOK,TS val 196493888 ecr 0,nop,wscale 7], length 0
15:25:08.728570 P 72:8b:ec:cb:aa:51 ethertype IPv4 (0x0800), length 76: 10.244.1.4.8443 > 10.244.0.0.56982: Flags [S.], seq 496037272, ack 925665058, win 27960, options [mss 1410,sackOK,TS val 196484874 ecr 196493888,nop,wscale 7], length 0
[root@master1 deploy]# ethtool --show-offload flannel.1|grep checksum
rx-checksumming: on
tx-checksumming: off
tx-checksum-ipv4: off [fixed]
tx-checksum-ip-generic: off
tx-checksum-ipv6: off [fixed]
tx-checksum-fcoe-crc: off [fixed]
tx-checksum-sctp: off [fixed]
[root@master1 flannel.1]# ethtool --offload flannel.1 tx on
[root@master1 ~]# ethtool --show-offload flannel.1|grep checksum
rx-checksumming: on
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]
# perf trace --no-syscalls --event 'net:*' wget -q -O /dev/null localhost:8443
这个问题似乎源于试图将源从一个节点路由到另一个节点。
我做了一个跟踪和tcp转储从C -> a(通过localhost:32081对C…见下文)。在两个节点上,TCPDUMP都显示重复的SYN包试图建立连接。
它们在结果中都显示了“bad udp cksum 0xffff -> 0x76dc!”63秒后,SYN数据包被设置为“no cksum”,连接建立。
我不得不禁用TCP卸载…发出此命令后,curl localhost:32081在所有节点上一致工作。
19:56:42.492161 In 52:54:00:af:ce:3e ethertype IPv4 (0x0800), length 126: (tos 0x0, ttl 64, id 48578, offset 0, flags [none], proto UDP (17), length 110)
10.33.46.228.17027 > 10.33.46.229.8472: [bad udp cksum 0xffff -> 0xa824!] OTV, flags [I] (0x08), overlay 0, instance 1
96:80:97:60:52:d4 > e6:8f:09:d5:16:6f, ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 42505, offset 0, flags [DF], proto TCP (6), length 60)
10.244.0.0.51585 > 10.244.1.4.8443: Flags [S], cksum 0x6b59 (correct), seq 2431065664, win 29200, options [mss 1460,sackOK,TS val 29210697 ecr 0,nop,wscale 7], length 0
perf trace 是 perf 子命令,能够跟踪packet路径,默认输出类似于 strace (头信息少很多)。
跟踪ping向 172.17.0.2 容器的包,这里我们只关心 net 事件,忽略系统调用信息:
# perf trace --no-syscalls --event 'net:*' ping 10.33.46.229 -c1 > /dev/null
# perf trace --no-syscalls --event 'net:*' ping 10.244.1.5 -c1 > /dev/null
# perf trace --no-syscalls --event 'net:*' curl 10.104.111.144:443 > /dev/null
ethtool --offload flannel.1 tx on;perf trace --no-syscalls --event 'net:*' curl 10.104.111.144:443 > /dev/null
只保留事件名和 skbaddr ,看起来清晰很多:
首先注意, skbaddr 在中间变了( 0xffff96d481988700 -> 0xffff96d481988b00 )。 变的这里,就是生成了ICMP echo reply包,并作为应答包发送的地方。接下来的时间,这 个包的skbaddr保持不变,说明没有copy。copy非常耗时。
其次,我们可以清楚地看到 packet在内核的传输路径 :首先通过docker0网桥,然后veth pair的 宿主机端( veth79215ff ),最后是veth pair的容器端(容器里的 eth0 );接下来是返回路径。
至此,虽然我们还没有看到网络命名空间,但已经得到了一个不错的全局视图。
[root@master1 ~]# perf trace --no-syscalls --event 'net:*' curl 10.104.111.144:443 > /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
0.000 net:net_dev_queue:dev=flannel.1 skbaddr=0xffff9fe3017036f8 len=74
0.077 net:net_dev_queue:dev=eth0 skbaddr=0xffff9fe3017036f8 len=124
0.096 net:net_dev_xmit:dev=eth0 skbaddr=0xffff9fe3017036f8 len=124 rc=0
0.100 net:net_dev_xmit:dev=flannel.1 skbaddr=0xffff9fe3017036f8 len=74 rc=0
0 0 0 0 0 0 0 0 --:--:-- 0:01:03 --:--:-- 0
63162.563 net:net_dev_queue:dev=flannel.1 skbaddr=0xffff9fe1dd19e6f8 len=148
63162.609 net:net_dev_queue:dev=eth0 skbaddr=0xffff9fe1dd19e6f8 len=198
63162.635 net:net_dev_xmit:dev=eth0 skbaddr=0xffff9fe1dd19e6f8 len=198 rc=0
63162.640 net:net_dev_xmit:dev=flannel.1 skbaddr=0xffff9fe1dd19e6f8 len=148 rc=0
100 48 0 48 0 0 0 0 --:--:-- 0:01:03 --:--:-- 11
63164.196 net:net_dev_queue:dev=flannel.1 skbaddr=0xffff9fe1dd19e6f8 len=66
63164.214 net:net_dev_queue:dev=eth0 skbaddr=0xffff9fe1dd19e6f8 len=116
63164.233 net:net_dev_xmit:dev=eth0 skbaddr=0xffff9fe1dd19e6f8 len=116 rc=0
63164.238 net:net_dev_xmit:dev=flannel.1 skbaddr=0xffff9fe1dd19e6f8 len=66 rc=0
63164.482 net:napi_gro_receive_entry:dev=eth0 napi_id=0 queue_mapping=0 skbaddr=0xffff9fe372998600 vlan_tagged=0 vlan_proto=0x0000 vlan_tci=0x0000 protocol=0x0800 ip_summed=0 hash=0x00000000 l4_hash=0 len=102 data_len=0 truesize=768 mac_header_valid=1 mac_header=-14 nr_frags=0 gso_size=0 gso_type=0
63164.489 net:netif_receive_skb:dev=eth0 skbaddr=0xffff9fe372998600 len=102
63164.506 net:napi_gro_receive_entry:dev=flannel.1 napi_id=0x1401 queue_mapping=0 skbaddr=0xffff9fe372998600 vlan_tagged=0 vlan_proto=0x0000 vlan_tci=0x0000 protocol=0x0800 ip_summed=0 hash=0x00000000 l4_hash=0 len=52 data_len=0 truesize=768 mac_header_valid=1 mac_header=-14 nr_frags=0 gso_size=0 gso_type=0
63164.511 net:netif_receive_skb:dev=flannel.1 skbaddr=0xffff9fe372998600 len=52
[root@master1 ~]# ip route add 10.96.0.0/12 dev cni0
[root@master1 ~]# perf trace --no-syscalls --event 'net:*' curl 10.104.111.144:443 > /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 48 0 48 0 0 37854 0 --:--:-- --:--:-- --:--:-- 48000
0.000 net:net_dev_queue:dev=flannel.1 skbaddr=0xffff9fe3077980f8 len=74
0.036 net:net_dev_queue:dev=eth0 skbaddr=0xffff9fe3077980f8 len=124
0.053 net:net_dev_xmit:dev=eth0 skbaddr=0xffff9fe3077980f8 len=124 rc=0
0.057 net:net_dev_xmit:dev=flannel.1 skbaddr=0xffff9fe3077980f8 len=74 rc=0
0.624 net:net_dev_queue:dev=flannel.1 skbaddr=0xffff9fe3077980f8 len=148
0.644 net:net_dev_queue:dev=eth0 skbaddr=0xffff9fe3077980f8 len=198
0.655 net:net_dev_xmit:dev=eth0 skbaddr=0xffff9fe3077980f8 len=198 rc=0
0.657 net:net_dev_xmit:dev=flannel.1 skbaddr=0xffff9fe3077980f8 len=148 rc=0
1.290 net:net_dev_queue:dev=flannel.1 skbaddr=0xffff9fe2dd25d4f8 len=66
1.302 net:net_dev_queue:dev=eth0 skbaddr=0xffff9fe2dd25d4f8 len=116
1.315 net:net_dev_xmit:dev=eth0 skbaddr=0xffff9fe2dd25d4f8 len=116 rc=0
1.318 net:net_dev_xmit:dev=flannel.1 skbaddr=0xffff9fe2dd25d4f8 len=66 rc=0
1.425 net:napi_gro_receive_entry:dev=eth0 napi_id=0 queue_mapping=0 skbaddr=0xffff9fe1f9c9cc00 vlan_tagged=0 vlan_proto=0x0000 vlan_tci=0x0000 protocol=0x0800 ip_summed=0 hash=0x00000000 l4_hash=0 len=102 data_len=0 truesize=768 mac_header_valid=1 mac_header=-14 nr_frags=0 gso_size=0 gso_type=0
1.454 net:netif_receive_skb:dev=eth0 skbaddr=0xffff9fe1f9c9cc00 len=102
1.472 net:napi_gro_receive_entry:dev=flannel.1 napi_id=0x1401 queue_mapping=0 skbaddr=0xffff9fe1f9c9cc00 vlan_tagged=0 vlan_proto=0x0000 vlan_tci=0x0000 protocol=0x0800 ip_summed=0 hash=0x00000000 l4_hash=0 len=52 data_len=0 truesize=768 mac_header_valid=1 mac_header=-14 nr_frags=0 gso_size=0 gso_type=0
1.477 net:netif_receive_skb:dev=flannel.1 skbaddr=0xffff9fe1f9c9cc00 len=52
perf list 'net:*'
这个命令会列出tracepoint列表,名字类似于 net:netif_rx 。冒号前面是事件类型,后面是事件名称。
sudo perf trace --no-syscalls --event 'net:net_dev_queue' --event 'net:netif_receive_skb_entry' --event 'net:netif_rx' --event 'net:napi_gro_receive_entry' curl 10.104.111.144:443 > /dev/null
使用perf做内核跟踪
会对udp send包 cksum有所影响的只应该有2个层面,一个是内核,也就是系统调用入sendto生产udp报文的时候udp层面的cksum计算,而另外一个就是网卡来计算cksum,二选一。
关掉tx-checksumming,他的理解是开启这个功能相当于用网卡硬件来计算cksum,而关闭则由内核来计算cksum。