OpenVPN 性能-OpenVPN 的第二个瓶颈在ssl加解密

1,869 阅读4分钟

测试命令:ab -k -c 8 -n 500 http://10.0.188.139/5m.html

机器部署:

S0:
eth0:192.168.188.194 mtu 1500 e1000e 1000baseT-FD flow-control
tun0:172.17.0.2      mtu 1500 
route:10.0.188.139   dev tun0
S1:
eth0:192.168.188.193 mtu 1500 e1000e 1000baseT-FD flow-control
eth1:10.0.188.193    mtu 1500 e1000e 1000baseT-FD flow-control
tun0:172.16.0.1      mtu 1500
S2:
eth1:10.0.188.139    mtu 1500 e1000e 1000baseT-FD flow-control
route:172.17.0.0     gw  10.0.188.193

测试数据: OpenVPN配置:--cipher BF-CBC --auth SHA1

Transfer rate:          35480.31 [Kbytes/sec] received

cpu:接近100%

OpenVPN配置:--cipher none --auth SHA1

Transfer rate:          64513.28 [Kbytes/sec] received

cpu:接近100%

OpenVPN配置:--cipher none --auth none

Transfer rate:          94089.42 [Kbytes/sec] received

cpu:接近100%

从数据可以看出,加解密和摘要计算消耗了大量的cpu,由于OpenVPN的本身架构就是一个大的while(1)循环,即使cipher和auth都为none,cpu利用率并没有下来多少,cpu将消耗在传输上,这个信息可以从top中看出: cipher和auth为none时,绑定OpenVPN于cpu0时的top快照:

Cpu0  : 17.4%us,  57.1%sy,  0.0%ni, 1.0%id,  0.0%wa,  0.0%hi,  24.6%si,  0.0%st
cipher为DES-CBC,auth为SHA1时,绑定OpenVPN于cpu0时的top快照:
Cpu0  : 75.4%us,  14.3%sy,  0.0%ni, 6.9%id,  0.0%wa,  0.0%hi,  3.3%si,  0.0%st

可见,不使用加解密和摘要时,cpu大量消耗在内核空间和网卡软中断上,而使用加密以及摘要时,cpu大量消耗在OpenVPN的加解密/摘要计算上。既然加解密是一个很耗时的操作,我们也就有必要使用上篇《OpenVPN性能-OpenVPN的第一个瓶颈在tun驱动》修改后的tun驱动了,鉴于OpenVPN代码不是很简单,在修改OpenVPN从而使之支持修改后的tun驱动之前,首先在simpletun上模拟一下加解密的操作,再来看一下simpletun的执行框架:

while(1) {
    int ret;
    fd_set rd_set;
    FD_ZERO(&rd_set);
    FD_SET(tap_fd, &rd_set); 
    FD_SET(net_fd, &rd_set);
    ret = select(100 + 1, &rd_set, NULL, NULL, NULL);
    if (ret < 0 && errno == EINTR){
              continue;
    }
    if (ret < 0) {
        perror("select()");
        exit(1);
    }
    if(FD_ISSET(tap_fd, &rd_set)) {
        nread = cread(tap_fd, buffer, BUFSIZE);
        //加入加密/mac操作encrypt(buffer)
        plength = htons(nread);
        nwrite = cwrite(net_fd, (char *)&plength, sizeof(plength));
        nwrite = cwrite(net_fd, buffer, nread);
    }
    if(FD_ISSET(net_fd, &rd_set)) {
        nread = read_n(net_fd, (char *)&plength, sizeof(plength));
        if(nread == 0) {
            break;
        }
        nread = read_n(net_fd, buffer, ntohs(plength));
        //加入verify/解密操作decrypt(buffer)
        nwrite = cwrite(tap_fd, buffer, nread);
    }
}

在上述simpletun的执行框架中加入加解密和mac操作,以模拟OpenVPN的情况,因此我们需要给出encrypt和decrypt函数,由于我们只是想制造一点cpu型的延迟(cpu不断计算造成的延迟)而并不是真的想加解密数据,因此没有必要调用libcrypto的EVP系列函数,比如EVP_CipherInit/Update/Final之类的,于是我们给出下面的实现:

void encrypt(char *buf)
{
        char buf2[8192] = {1}; //不要触动参数buf,只是制造一些计算
        int i = 0;
        for (i; i<4192; i++) {
                buf2[i] += i;
        }
}
void decrypt(char *buf)
{
        encryp(NULL);
}

encrypt函数中的8192这个数字是随便猜测的,4192是微调出来的,不断地改变这个数字,然后编译,执行,直到执行ab -k -c 8 -n 500 http://10.0.188.139/5m.html时得到和OpenVPN --config xxx --cipher BF-CBC --auth SHA1测试得到的结果大致相同,也就是35M/s左右,这样就基本模拟了OpenVPN的加解密操作,在S0上,调到了4192正好得到35M/s的速率,因此encrypt和decrypt函数定型为上述。

接下来停掉simpletun的server和client,在S0(运行client的那台)和S1(运行server的那台)上执行rmmod tun,然后insmod上修改后的tun驱动,此时再次执行测试,ab -k -c 8 -n 500 http://10.0.188.139/5m.html的结果是:

Transfer rate:          86526.22 [Kbytes/sec] received

是不是质的飞跃,这就是《OpenVPN性能-OpenVPN的第一个瓶颈在tun驱动》最后说的更猛的效果。

这样针对OpenVPN的修改就比较有意义了。



reference