发生肾么事了?
去年9月我就被iPerf3坑过一次。那次经历让我知道了iPerf3收发UDP包时会同时用TCP进行控制通信。
时隔半年多,我们又要用iPerf3做带宽测试了。这次收信端(server)在公网,监听TCP/1234和UDP/1234端口;发信端(client)在内网。收发两端中间有交换机和防火墙。构成大概是下面这样:
发信端(C) -- 防火墙 -- 交换机 -- Internet -> 收信端(S)
为了尽量减少对公网的端口暴露,同时吸取去年的教训,我们为交换机设置了ACL,仅允许内外网双向TCP/1234和UDP/1234端口的通信,屏蔽了其他端口。
于是乎,实验开始:
# 收信端
iperf3 -s -p 1234
# 发信端
## TCP测试
iperf3 -c <公网IP> -p 1234 -b 1000 --time 60
## UDP测试
### 注意:iperf3发UDP默认包长度为8192。
### 有些网卡受不了这个,需要手动指定长度为1400。
### 否则可能出现大量fragement,或者收到的包都是0K(被网卡丢弃了)。
iperf3 -c <公网IP> -u -p 1234 -b 1000 --time 60 --length 1400
吊诡的现象出现了:实验中,用iPerf3从内网向外网发TCP包,一切正常。然而发UDP包的时候,发信端却直接卡住,收信端输出显示一直在收到0K的包。
怎么解决的?
总之先抓包看看
在多次修改iPerf3参数无果后,笔者用WireShark对两台直连设备间的iPerf3通信进行了抓包,了解了iPerf3的UDP模式工作流程如下:
No. C S
01 TCP/xxxx|--------- hostname, etc. -------->|TCP/1234
02 TCP/xxxx|<------------- 0x09 --------------|TCP/1234
03 TCP/xxxx|-------------- 0x67 ------------->|TCP/1234
04 TCP/xxxx|-- {"udp":true,"len":8192,...} -->|TCP/1234
05 TCP/xxxx|<------------- 0x0a --------------|TCP/1234
06 UDP/yyyy|------- 15cd5b07 (Random?) ------>|UDP/1234
07 UDP/yyyy|<------ b168de3a (Random?) -------|UDP/1234
08 TCP/xxxx|<------------- 0x01 --------------|TCP/1234
09 TCP/xxxx|-------------- 0x02 ------------->|TCP/1234
10 UDP/yyyy|---------- udp payload ---------->|UDP/1234
11 UDP/yyyy|---------- udp payload ---------->|UDP/1234
12 UDP/yyyy|---------- udp payload ---------->|UDP/1234
13 UDP/yyyy|---------- udp payload ---------->|UDP/1234
14 TCP/xxxx|-------------- 0x0c ------------->|TCP/1234
15 TCP/xxxx|<------------- ... -------------->|TCP/1234
其中:
- No.10~13 为iPerf3实际用于带宽测试的数据包。其它字符串或意义不明的16进制均为控制信息。
xxxx和yyyy是两个不同的发信端口,两者默认均为随机生成。- No.04 中的JSON信息大致如下:
{
"udp":true,
"omit":0,
"time":60,
"parallel":1,
"len":1400,
"bandwidth":1000,
"client_version":"3.1.3"
}
然后反观实验用的收信端和发信端,用WireShark抓包,发现了如下现象:
- 发信端
- 没有收到No.07中本该由收信端发来的UDP包。
- 没有发送No.10及其以后的数据。
- 收信端
- 没有收到No.10及其以后的数据,它们本该由发信端发送。
恍然大悟!
从上述现象中不难看出,虽然收信端看似收到了0K的UDP数据包,但其实发信端自始至终都没有发送任何UDP包。而其直接原因,就是没有收到No.07的那个4byte的UDP包。它很有可能是启动发包的信号之一。
那为什么没收到呢?因为前文提到:
我们为交换机设置了ACL,仅允许内外网双向
TCP/1234和UDP/1234端口的通信,屏蔽了其他端口。
而位于内网的发信端,发送No.06的UDP包时使用的源端口是随机端口yyyy。作为回复,收信端向发信端发送No.07的UDP包时使用的目的端口就是yyyy。于是这个UDP包就被ACL给华丽地屏蔽了……
No. C S
06 UDP/yyyy|------- 15cd5b07 (Random?) ------>|UDP/1234
07 UDP/yyyy| X<-- b168de3a (Random?) -------|UDP/1234
而TCP通信由于是有状态的,交换机/防火墙在处理TCP时,一般会自动做“连接跟踪”,因此不受该机制的影响。
怎么办?
如果不想修改交换机的ACL设置,便可以拿iPerf做文章。
iPerf3支持指定发信端口。既然ACL只允许UDP/1234,那我们就指定发信端口也为UDP/1234。只需要在启动发包时加上参数--cport即可:
# 发信端
## UDP测试
### 指定发信端口为1234
iperf3 -c <公网IP> -u -p 1234 -b 1000 --time 60 --length 1400 --cport 1234
这样一来实验就能正常进行了。收发双方间的通信变成下面这样:
No. C S
01 TCP/xxxx|--------- hostname, etc. -------->|TCP/1234
02 TCP/xxxx|<------------- 0x09 --------------|TCP/1234
03 TCP/xxxx|-------------- 0x67 ------------->|TCP/1234
04 TCP/xxxx|-- {"udp":true,"len":8192,...} -->|TCP/1234
05 TCP/xxxx|<------------- 0x0a --------------|TCP/1234
06 UDP/1234|------- 15cd5b07 (Random?) ------>|UDP/1234
07 UDP/1234|<------ b168de3a (Random?) -------|UDP/1234
08 TCP/xxxx|<------------- 0x01 --------------|TCP/1234
09 TCP/xxxx|-------------- 0x02 ------------->|TCP/1234
10 UDP/1234|---------- udp payload ---------->|UDP/1234
11 UDP/1234|---------- udp payload ---------->|UDP/1234
12 UDP/1234|---------- udp payload ---------->|UDP/1234
13 UDP/1234|---------- udp payload ---------->|UDP/1234
14 TCP/xxxx|-------------- 0x0c ------------->|TCP/1234
15 TCP/xxxx|<------------- ... -------------->|TCP/1234
