我遇到了与内部制作的VPN相关的相对令人失望的表现。
界面 | 速度Mb/s |
---|---|
enp5s0 | 950 |
tun0 | 550 |
我开始相信罪魁祸首(以及其他一些)是 TCP 分段(参见这篇文章)。
问题是,linux网络不是我的专长,我觉得有点超出了我的深度,我不太确定理解GSO是如何工作的,它需要什么,甚至这确实是我正在寻找的(我希望不要陷入 XY 问题)。
我将使用 Homer Simpson 的 simpletun 作为案例研究。
我愿意接受替代方案,但我主要是询问在 TUN 接口之间启用 GSO 的正确方法,以尽可能晚地卸载前向分段(我认为就性能而言这是有利的)。
问题涉及 Linux 上的网络。我暂时不关心与任何其他操作系统的互操作性。
我一直很难找到有关此功能的正确文档。目前我只能找到
我添加了 ifr 标志
IFF_VNET_HDR
,如一些给出的来源中所解释的
我添加了一个 ioctl 调用,另一个是为了设置我认为正确的向前卸载标志
/**************************************************************************
* tun_alloc: allocates or reconnects to a tun/tap device. The caller *
* must reserve enough space in *dev. *
**************************************************************************/
int tun_alloc(char *dev, int flags) {
struct ifreq ifr;
int fd, err;
char *clonedev = "/dev/net/tun";
if( (fd = open(clonedev , O_RDWR)) < 0 ) {
perror("Opening /dev/net/tun");
return fd;
}
memset(&ifr, 0, sizeof(ifr));
// flags = 0x0001 ≡ IFF_TUN
// flag IFF_VNET_HDR necessary ?
ifr.ifr_flags = flags | IFF_NO_PI | IFF_VNET_HDR;
if (*dev) {
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}
if( (err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0 ) {
perror("ioctl(TUNSETIFF)");
close(fd);
return err;
}
// setting up offload
unsigned long offload_flags = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6;
if ( (err = ioctl(fd, TUNSETOFFLOAD, offload_flags)) < 0 ) {
perror("ioctl(TUNSETOFFLOAD)");
close(fd);
return err;
}
// --------------------------------------------------------------
strcpy(dev, ifr.ifr_name);
return fd;
}
我预计 VPN 会因为卸载而提高性能。
这导致速度稍微慢一些。使用 iperf3 获得的结果
界面 | 速度Mb/s |
---|---|
enp5s0 | 942 |
tun0 | 324 |
这让我感到困惑,我可以理解一个特定的错误,但我不明白如何最终得到较慢的结果。
> iperf3 -c $SERVER_IP
Connecting to host 192.168.50.112, port 5201
[ 5] local 192.168.30.175 port 53126 connected to 192.168.50.112 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 114 MBytes 952 Mbits/sec 0 529 KBytes
[ 5] 1.00-2.00 sec 113 MBytes 949 Mbits/sec 0 557 KBytes
[ 5] 2.00-3.00 sec 112 MBytes 939 Mbits/sec 0 617 KBytes
[ 5] 3.00-4.00 sec 111 MBytes 933 Mbits/sec 0 747 KBytes
[ 5] 4.00-5.00 sec 112 MBytes 944 Mbits/sec 0 783 KBytes
[ 5] 5.00-6.00 sec 112 MBytes 944 Mbits/sec 0 783 KBytes
[ 5] 6.00-7.00 sec 111 MBytes 933 Mbits/sec 0 820 KBytes
[ 5] 7.00-8.00 sec 112 MBytes 944 Mbits/sec 0 909 KBytes
[ 5] 8.00-9.00 sec 112 MBytes 944 Mbits/sec 0 909 KBytes
[ 5] 9.00-10.00 sec 111 MBytes 933 Mbits/sec 0 953 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 1.10 GBytes 942 Mbits/sec 0 sender
[ 5] 0.00-10.00 sec 1.09 GBytes 939 Mbits/sec receiver
iperf Done.
> iperf3 -B 10.10.9.1%tun0 -c $SERVER_IP
Connecting to host 192.168.50.112, port 5201
[ 5] local 10.10.9.1 port 52443 connected to 192.168.50.112 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 107 KBytes 879 Kbits/sec 20 2.83 KBytes
[ 5] 1.00-2.00 sec 45.2 KBytes 371 Kbits/sec 10 4.24 KBytes
[ 5] 2.00-3.00 sec 41.0 KBytes 336 Kbits/sec 10 5.66 KBytes
[ 5] 3.00-4.00 sec 39.6 KBytes 324 Kbits/sec 8 4.24 KBytes
[ 5] 4.00-5.00 sec 39.6 KBytes 324 Kbits/sec 10 5.66 KBytes
[ 5] 5.00-6.00 sec 39.6 KBytes 324 Kbits/sec 10 5.66 KBytes
[ 5] 6.00-7.00 sec 79.2 KBytes 649 Kbits/sec 8 7.07 KBytes
[ 5] 7.00-8.00 sec 39.6 KBytes 324 Kbits/sec 10 5.66 KBytes
[ 5] 8.00-9.00 sec 39.6 KBytes 324 Kbits/sec 11 1.41 KBytes
[ 5] 9.00-10.00 sec 39.6 KBytes 324 Kbits/sec 9 2.83 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 510 KBytes 418 Kbits/sec 106 sender
[ 5] 0.00-10.08 sec 431 KBytes 351 Kbits/sec receiver
iperf Done.
查看 iperf3 输出,通过 tun0 接口时似乎出现数据包丢失问题。请注意 iperf 输出中的“Retr”和“Cwnd”列。第一个是重传。第二个是拥塞窗口。 TCP 连接性能的(许多)限制之一是:
吞吐量 <= EffectiveWindow / RoundTripTime
如果您将 Cwnd 的值插入为“EffectiveWindow”以及连接的 RoundTripTime(例如 ping 显示的内容),我怀疑您会发现在 tun0 情况下您受到拥塞窗口大小的限制。要增加这一点,您需要找到丢弃的来源(也许 tun0 上的传输队列太短?只是猜测)并解决它。
Linux 下默认的拥塞控制启发式“cubic”对连接早期的数据包丢失特别敏感。