Linux服务器丢包故障的解决思路及引申的TCP IP协议栈理论

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新软件测试全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注软件测试)
img

正文

Shell

1

2

3

4

5

6

7

8

9

10

11

|

$ sudo tcpdump -i eth0 port 22 and “tcp[tcpflags] & (tcp-syn) != 0”

18:56:37.404603 IP CLIENT.56909 > SERVER.22: Flags [S], seq 1190606850, win 29200, options [mss 1448,sackOK,TS val 198321481 ecr 0,nop,wscale 7], length 0

18:56:38.404582 IP CLIENT.56909 > SERVER.22: Flags [S], seq 1190606850, win 29200, options [mss 1448,sackOK,TS val 198321731 ecr 0,nop,wscale 7], length 0

18:56:40.407289 IP CLIENT.56909 > SERVER.22: Flags [S], seq 1190606850, win 29200, options [mss 1448,sackOK,TS val 198322232 ecr 0,nop,wscale 7], length 0

18:56:44.416108 IP CLIENT.56909 > SERVER.22: Flags [S], seq 1190606850, win 29200, options [mss 1448,sackOK,TS val 198323234 ecr 0,nop,wscale 7], length 0

18:56:45.100033 IP CLIENT.50212 > SERVER.22: Flags [S], seq 4207350463, win 65535, options [mss 1366,nop,wscale 5,nop,nop,TS val 821068631 ecr 0,sackOK,eol], length 0

18:56:45.100110 IP SERVER.22 > CLIENT.50212: Flags [S.], seq 1281140899, ack 4207350464, win 27960, options [mss 1410,sackOK,TS val 1709997543 ecr 821068631,nop,wscale 7], length 0

18:56:52.439086 IP CLIENT.56909 > SERVER.22: Flags [S], seq 1190606850, win 29200, options [mss 1448,sackOK,TS val 198325240 ecr 0,nop,wscale 7], length 0

18:57:08.472825 IP CLIENT.56909 > SERVER.22: Flags [S], seq 1190606850, win 29200, options [mss 1448,sackOK,TS val 198329248 ecr 0,nop,wscale 7], length 0

18:57:40.535621 IP CLIENT.56909 > SERVER.22: Flags [S], seq 1190606850, win 29200, options [mss 1448,sackOK,TS val 198337264 ecr 0,nop,wscale 7], length 0

18:57:40.535698 IP SERVER.22 > CLIENT.56909: Flags [S.], seq 3621462255, ack 1190606851, win 27960, options [mss 1410,sackOK,TS val 1710011402ecr 198337264,nop,wscale 7], length 0

|

排查过程

服务器能正常接收到数据包,问题可以限定在两种可能:部分客户端发出的数据包本身异常;服务器处理部分客户端的数据包时触发了某种机制丢弃了数据包。因为出问题的客户端能够正常访问公网上其他服务,后者的可能性更大。

有哪些情况会导致Linux服务器丢弃数据包?

防火墙拦截

服务器端口无法连接,通常就是查看防火墙配置了,虽然这里已经确认同一个出口IP的客户端有的能够正常访问,但也不排除配置了DROP特定端口范围的可能性。

如何确认

查看iptables filter表,确认是否有相应规则会导致此丢包行为:

Shell

1

|

$ sudo iptables-save -t filter

|

这里容易排除防火墙拦截的可能性。

连接跟踪表溢出

除了防火墙本身配置DROP规则外,与防火墙有关的还有连接跟踪表nf_conntrack,Linux为每个经过内核网络栈的数据包,生成一个新的连接记录项,当服务器处理的连接过多时,连接跟踪表被打满,服务器会丢弃新建连接的数据包。

如何确认
通过dmesg可以确认是否有该情况发生:

Shell

1

|

$ dmesg |grep nf_conntrack

|

如果输出值中有“nf_conntrack: table full, dropping packet”,说明服务器nf_conntrack表已经被打满。

通过/proc文件系统查看nf_conntrack表实时状态:

Shell

1

2

3

4

5

6

|

查看nf_conntrack表最大连接数

$ cat /proc/sys/net/netfilter/nf_conntrack_max

65536

查看nf_conntrack表当前连接数

$ cat /proc/sys/net/netfilter/nf_conntrack_count

7611

|

当前连接数远没有达到跟踪表最大值,排除这个因素。

如何解决

如果确认服务器因连接跟踪表溢出而开始丢包,首先需要查看具体连接判断是否正遭受DOS攻击,如果是正常的业务流量造成,可以考虑调整nf_conntrack的参数:

nf_conntrack_max决定连接跟踪表的大小,默认值是65535,可以根据系统内存大小计算一个合理值:CONNTRACK_MAX = RAMSIZE(in bytes)/16384/(ARCH/32),如32G内存可以设置1048576;

nf_conntrack_buckets决定存储conntrack条目的哈希表大小,默认值是nf_conntrack_max的1/4,延续这种计算方式:BUCKETS = CONNTRACK_MAX/4,如32G内存可以设置262144;

nf_conntrack_tcp_timeout_established决定ESTABLISHED状态连接的超时时间,默认值是5天,可以缩短到1小时,即3600。

Shell

1

2

3

|

$ sysctl -w net.netfilter.nf_conntrack_max=1048576

$ sysctl -w net.netfilter.nf_conntrack_buckets=262144

$ sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600

|

Ring Buffer溢出

排除了防火墙的因素,我们从底向上来看Linux接收数据包的处理过程,首先是网卡驱动层。

如下图所示,物理介质上的数据帧到达后首先由NIC(网络适配器)读取,写入设备内部缓冲区Ring Buffer中,再由中断处理程序触发Softirq从中消费,Ring Buffer的大小因网卡设备而异。当网络数据包到达(生产)的速率快于内核处理(消费)的速率时,Ring Buffer很快会被填满,新来的数据包将被丢弃。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
如何确认
通过ethtool或/proc/net/dev可以查看因Ring Buffer满而丢弃的包统计,在统计项中以fifo标识:

Shell

1

2

3

4

5

6

7

|

$ ethtool -S eth0|grep rx_fifo

rx_fifo_errors: 0

$ cat /proc/net/dev

Inter-|   Receive                                                |  Transmit

face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed

eth0: 17253386680731 42839525880    0    0    0     0          0 244182022 14879545018057 41657801805    0    0    0     0       0         0

|

可以看到服务器的接收方向的fifo丢包数并没有增加,这里自然也排除这个原因。
如何解决
如果发现服务器上某个网卡的fifo数持续增大,可以去确认CPU中断是否分配均匀,也可以尝试增加Ring Buffer的大小,通过ethtool可以查看网卡设备Ring Buffer最大值,修改Ring Buffer当前设置:

Shell

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

|

查看eth0网卡Ring Buffer最大值和当前设置

$ ethtool -g eth0

Ring parameters for eth0:

Pre-set maximums:

RX:     4096

RX Mini:    0

RX Jumbo:   0

TX:     4096

Current hardware settings:

RX:     1024

RX Mini:    0

RX Jumbo:   0

TX:     1024

修改网卡eth0接收与发送硬件缓存区大小

$ ethtool -G eth0 rx 4096 tx 4096

Pre-set maximums:

RX:     4096

RX Mini:    0

RX Jumbo:   0

TX:     4096

Current hardware settings:

RX:     4096

RX Mini:    0

RX Jumbo:   0

TX:     4096

|

netdev_max_backlog溢出

netdev_max_backlog是内核从NIC收到包后,交由协议栈(如IP、TCP)处理之前的缓冲队列。每个CPU核都有一个backlog队列,与Ring Buffer同理,当接收包的速率大于内核协议栈处理的速率时,CPU的backlog队列不断增长,当达到设定的netdev_max_backlog值时,数据包将被丢弃。

如何确认
通过查看/proc/net/softnet_stat可以确定是否发生了netdev backlog队列溢出:

Shell

1

2

3

4

5

|

$ cat /proc/net/softnet_stat

01a7b464 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

01d4d71f 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

0349e798 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

017e0826 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

|

其中:
每一行代表每个CPU核的状态统计,从CPU0依次往下;
每一列代表一个CPU核的各项统计:第一列代表中断处理程序收到的包总数;第二列即代表由于netdev_max_backlog队列溢出而被丢弃的包总数。
从上面的输出可以看出,这台服务器统计中,并没有因为netdev_max_backlog导致的丢包。

如何解决
netdev_max_backlog的默认值是1000,在高速链路上,可能会出现上述第二列统计不为0的情况,可以通过修改内核参数net.core.netdev_max_backlog来解决:

Shell

1

|

$ sysctl -w net.core.netdev_max_backlog=2000

|

反向路由过滤

反向路由过滤机制是Linux通过反向路由查询,检查收到的数据包源IP是否可路由(Loose mode)、是否最佳路由(Strict mode),如果没有通过验证,则丢弃数据包,设计的目的是防范IP地址欺骗攻击。rp_filter提供了三种模式供配置:

  • 0 - 不验证
  • 1 - RFC3704定义的严格模式:对每个收到的数据包,查询反向路由,如果数据包入口和反向路由出口不一致,则不通过
  • 2 - RFC3704定义的松散模式:对每个收到的数据包,查询反向路由,如果任何接口都不可达,则不通过

如何确认
查看当前rp_filter策略配置:

Shell

1

2

|

$ cat /proc/sys/net/ipv4/conf/eth0/rp_filter

0

|

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注软件测试)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
l

1

|

$ sysctl -w net.core.netdev_max_backlog=2000

|

反向路由过滤

反向路由过滤机制是Linux通过反向路由查询,检查收到的数据包源IP是否可路由(Loose mode)、是否最佳路由(Strict mode),如果没有通过验证,则丢弃数据包,设计的目的是防范IP地址欺骗攻击。rp_filter提供了三种模式供配置:

  • 0 - 不验证
  • 1 - RFC3704定义的严格模式:对每个收到的数据包,查询反向路由,如果数据包入口和反向路由出口不一致,则不通过
  • 2 - RFC3704定义的松散模式:对每个收到的数据包,查询反向路由,如果任何接口都不可达,则不通过

如何确认
查看当前rp_filter策略配置:

Shell

1

2

|

$ cat /proc/sys/net/ipv4/conf/eth0/rp_filter

0

|

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注软件测试)
[外链图片转存中…(img-7oBQ0Idh-1713332966981)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值