OpenVPN-秘籍第二版-三-

135 阅读1小时+

OpenVPN 秘籍第二版(三)

原文:annas-archive.org/md5/fd72a09eaec4467049fbfe9a8c996015

译者:飞龙

协议:CC BY-NC-SA 4.0

第六章:故障排除 OpenVPN - 配置

在本章中,我们将讨论以下内容:

  • 加密算法不匹配

  • TUN 与 TAP 不匹配

  • 压缩不匹配

  • 密钥不匹配

  • 故障排除 MTU 和 tun-mtu 问题

  • 故障排除网络连接

  • 故障排除 client-config-dir 问题

  • 故障排除多个 remote 问题

  • 故障排除桥接问题

  • 如何读取 OpenVPN 日志文件

介绍

本章和下一章的主题是故障排除 OpenVPN。本章将专注于故障排除 OpenVPN 配置错误,而下一章将专注于在设置 VPN 时常见的路由问题。

因此,本章中的方法首先将致力于破坏配置。然后,我们将提供查找和解决配置错误的工具。本章中使用的一些配置指令之前没有演示过,所以即使你不打算破坏配置,本章仍然很有启发性。

加密算法不匹配

在本示例中,我们将更改 OpenVPN 使用的加密算法。最初,我们只会在客户端侧更改加密算法,这将导致 VPN 连接初始化失败。本示例的主要目的是展示出现的错误信息,而不是探索 OpenVPN 支持的不同类型的加密算法。

准备工作

使用 第二章 客户端-服务器仅 IP 网络 中的第一个方法设置客户端和服务器证书。在本示例中,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端运行 Windows 7 64 位和 OpenVPN 2.3.10。保留来自 第二章 客户端-服务器仅 IP 网络 中的 服务器端路由 配置文件 basic-udp-server.conf,以及客户端配置文件 basic-udp-client.conf

如何操作...

  1. 使用配置文件 basic-udp-server.conf 启动服务器:

            [root@server]# openvpn --config basic-udp-server.conf
    
    
  2. 接下来,通过向 basic-udp-client.conf 文件中添加一行来创建客户端配置文件:

            cipher CAST5-CBC 
    
    

    将其保存为 example6-1-client.conf

  3. 启动客户端后,客户端日志中将出现以下信息:

            [root@client]# openvpn --config example6-1-client.conf 
            ... WARNING: 'cipher' is used inconsistently, local='cipher 
            CAST5-CBC'', remote='cipher BF-CBC'' 
            ... [openvpnserver] Peer Connection Initiated with server-
            ip:1194 
            ... TUN/TAP device tun0 opened 
            ... /sbin/ip link set dev tun0 up mtu 1500 
            ... /sbin/ip addr add dev tun0 10.200.0.2/24 broadcast 
            10.200.0.255 
            ... Initialization Sequence Completed 
            ... Authenticate/Decrypt packet error: cipher final failed 
    
    

    同样,在服务器端:

            ... client-ip:52461 WARNING: 'cipher' is used inconsistently,         local='cipher BF-CBC'', remote='cipher CAST5-CBC'' 
            ... client-ip:52461 [client1] Peer Connection Initiated with         client1:52461 
            ... client1/client-ip:52461 Authenticate/Decrypt packet error: 
            cipher final failed 
            ... client1/client-ip:52461 Authenticate/Decrypt packet error: 
            cipher final failed 
    
    

    连接不会成功建立,但也不会立即断开连接。

它是如何工作的...

在连接阶段,客户端和服务器协商多个安全连接所需的参数。此阶段最重要的参数之一是加密算法,它用于加密和解密所有消息。如果客户端和服务器使用不同的加密算法,它们根本无法互相通信。

通过向服务器配置文件中添加以下配置指令,客户端和服务器可以重新通信:

cipher CAST5-CBC 

还有更多...

OpenVPN 支持许多加密算法,尽管有些加密算法的支持仍处于实验阶段。要查看受支持的加密算法列表,请输入:

$ openvpn --show-ciphers

这将列出所有加密算法,其中包括变量长度和固定长度的加密算法。OpenVPN 对变量长度的加密算法有很好的支持,其他加密算法有时可能会导致不可预测的结果。

可推送的加密算法

从版本 2.4 开始,OpenVPN 客户端支持处理从服务器推送到客户端的加密算法选项。因此,如果所有客户端都运行 OpenVPN 2.4 或更高版本,那么在现有部署中更改加密算法将变得更加容易。

TUN 与 TAP 的不匹配

在基于 OpenVPN 设置 VPN 时,一个常见的错误是所使用的适配器类型。如果服务器配置为使用 TUN 风格的网络,而客户端配置为使用 TAP 风格的接口,那么 VPN 连接将失败。在这个配方中,我们将展示当出现这个常见配置错误时,通常会看到的情况。

准备工作

使用第二章中第一个配方中的内容来设置客户端和服务器证书,客户端-服务器仅 IP 网络。在这个配方中,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11。客户端运行的是 Fedora 22 Linux 和 OpenVPN 2.3.11。保留来自第二章中服务器端路由配方的配置文件,basic-udp-server.conf客户端-服务器仅 IP 网络

如何操作...

  1. 使用配置文件basic-udp-server.conf启动服务器:

            [root@server]# openvpn --config basic-udp-server.conf
    
    
  2. 接下来,创建客户端配置:

            client 
            proto udp 
            remote openvpnserver.example.com 
            port 1194 
    
            dev tap 
            nobind 
    
            remote-cert-tls server 
            tls-auth /etc/openvpn/cookbook/ta.key 1 
            ca       /etc/openvpn/cookbook/ca.crt 
            cert     /etc/openvpn/cookbook/client1.crt 
            key      /etc/openvpn/cookbook/client1.key  
    
    

    将其保存为example6-2-client.conf

  3. 启动客户端:

            [root@client]# openvpn --config example6-2-client.conf
    
    

    客户端日志将显示如下内容:

            ... WARNING: 'dev-type' is used inconsistently, local='dev-type 
            tap'', remote='dev-type tun'' 
            ... WARNING: 'link-mtu' is used inconsistently, local='link-mtu 
            1573'', remote='link-mtu 1541'' 
            ... WARNING: 'tun-mtu' is used inconsistently, local='tun-mtu 
            1532'', remote='tun-mtu 1500'' 
            ... [openvpnserver] Peer Connection Initiated with server-
            ip:1194 
            ... TUN/TAP device tap0 opened 
            ... /sbin/ip link set dev tap0 up mtu 1500 
            ... /sbin/ip addr add dev tap0 10.200.0.2/24 broadcast 
            10.200.0.255 
            ... Initialization Sequence Completed 
    
    

    此时,您可以尝试 ping 服务器,但它将返回错误:

            [client]$ ping 10.200.0.1 
            PING 10.200.0.1 (10.200.0.1) 56(84) bytes of data. 
            From 10.200.0.2 icmp_seq=2 Destination Host Unreachable 
            From 10.200.0.2 icmp_seq=3 Destination Host Unreachable 
            From 10.200.0.2 icmp_seq=4 Destination Host Unreachable 
    
    

工作原理...

TUN 风格的接口提供点对点连接,只有 TCP/IP 流量可以通过该接口进行隧道化。TAP 风格的接口提供等同于以太网接口的功能,并包含额外的头信息。这允许用户通过该接口隧道其他类型的流量。当客户端和服务器配置错误时,预期的数据包大小会有所不同:

... WARNING: 'tun-mtu' is used inconsistently, local='tun-mtu 1532'', remote='tun-mtu 1500'' 

这表明通过 TAP 风格接口发送的每个数据包比通过 TUN 风格接口发送的包大 32 字节。

通过纠正客户端配置,解决了这个问题。

压缩不匹配

OpenVPN 支持对通过 VPN 隧道发送的流量进行即时压缩。这可以提高慢速网络线路上的性能,但也会增加一些开销。在传输无法压缩的数据(如 ZIP 文件)时,性能实际上会略微下降。

如果服务器启用了压缩,但客户端没有启用,那么 VPN 连接将失败。

准备工作

使用第二章中的 设置公钥和私钥 配方来设置客户端和服务器证书。对于此配方,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端运行的是 Fedora 22 Linux 和 OpenVPN 2.3.11。保留第二章中 服务器端路由 配方中的配置文件 basic-udp-server.conf,以及客户端配置文件 basic-udp-client.conf

如何操作……

  1. 向服务器配置文件 basic-udp-server.conf 添加一行:

            comp-lzo 
    
    

    将其保存为 example6-3-server.conf

  2. 启动服务器:

    [root@server]# openvpn --config example6-3-server.conf
    
    
  3. 接下来,启动客户端:

     [root@client]# openvpn --config basic-udp-client.conf
    
    

    连接将会启动,但是当数据通过 VPN 连接发送时,以下消息将会出现:

            Initialization Sequence Completed 
            ... write to TUN/TAP : Invalid argument (code=22) 
            ... write to TUN/TAP : Invalid argument (code=22) 
    
    

它是如何工作的……

在连接阶段,不使用压缩在客户端和服务器之间传输信息。商定的参数之一是是否为实际的 VPN 负载使用压缩。如果客户端和服务器之间的配置不匹配,那么双方将对对方发送的流量感到困惑。

这个错误可以通过添加一行来轻松修复所有客户端:

push "comp-lzo"

密钥不匹配

OpenVPN 为其 TLS 控制通道提供额外保护,使用的是 HMAC 密钥。这些密钥与在第一章中用于点对点式网络的静态“秘密”密钥完全相同。对于多客户端式网络,可以通过使用 tls-auth 指令启用这种额外的保护。如果客户端和服务器之间与 tls-auth 密钥相关的配置不匹配,则 VPN 连接将无法初始化。

准备就绪

使用第二章中的第一个配方来设置客户端和服务器证书。对于此配方,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端运行的是 Fedora 22 Linux 和 OpenVPN 2.3.11。保留第二章中 服务器端路由 配方中的配置文件 basic-udp-server.conf

如何操作……

  1. 使用配置文件 basic-udp-server.conf 启动服务器:

            [root@server]# openvpn --config basic-udp-server.conf
    
    
  2. 接下来,创建客户端配置:

            client 
            proto udp 
            remote openvpnserver.example.com 
            port 1194 
    
            dev tun 
            nobind 
    
            remote-cert-tls server 
            tls-auth /etc/openvpn/cookbook/ta.key 
            ca       /etc/openvpn/cookbook/ca.crt 
            cert     /etc/openvpn/cookbook/client1.crt 
            key      /etc/openvpn/cookbook/client1.key 
    
    

    注意到缺少 tls-auth 的第二个参数。将其保存为 example6-4-client.conf 文件。

  3. 启动客户端:

    [root@client]# openvpn --config example6-4-client.conf
    
    

    客户端日志将不会显示错误,但连接也不会建立。在服务器日志中,我们会看到以下内容:

            ... Initialization Sequence Completed
            ... Authenticate/Decrypt packet error: packet HMAC 
            authentication failed
            ... TLS Error: incoming packet authentication failed from 
            client-ip:54454
    
    

    这表明客户端client1使用了错误的tls-auth参数,连接被拒绝。

工作原理...

在连接初始化的第一阶段,客户端和服务器会相互验证对方的 HMAC 密钥。如果 HMAC 密钥没有正确配置,则初始化会被中止,连接无法建立。由于 OpenVPN 服务器无法确定客户端是简单配置错误,还是恶意客户端试图过载服务器,因此连接会被直接中断。这导致客户端一直监听服务器的流量,直到最终超时。

在这个食谱中,配置错误是配置行末缺少了参数1

tls-auth /etc/openvpn/cookbook/ta.key 

tls-auth指令的第二个参数是密钥的方向。通常,使用以下约定:

  • 0:从服务器到客户端

  • 1:从客户端到服务器

这个参数使 OpenVPN 从ta.key文件的不同部分派生 HMAC 密钥。如果客户端和服务器在 HMAC 密钥派生的部分上存在分歧,则连接无法建立。同样,如果客户端和服务器从不同的ta.key文件中派生 HMAC 密钥,也无法建立连接。

参见

  • 第一章中的多个密钥食谱,详细解释了 OpenVPN 密钥的格式和使用方法。

故障排除 MTU 和 tun-mtu 问题

OpenVPN 的一个高级特性是能够调整 TUN(或 TAP)适配器以及加密链路本身的网络参数。这是配置错误的一个常见原因,可能导致性能低下,甚至无法成功地通过 VPN 隧道传输数据。这个食谱将展示如果客户端和服务器之间存在 MTU(最大传输单元)不匹配时会发生什么,以及如何在某些情况下这种不匹配会导致 VPN 隧道失败。

准备工作

使用第二章中的第一个食谱,客户端-服务器 IP 仅网络,来设置客户端和服务器证书。对于此食谱,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.3.11,而客户端运行 Fedora 22 Linux 和 OpenVPN 2.3.11。请将客户端配置文件basic-udp-client.conf与来自服务器端路由食谱的配置文件basic-udp-server.conf一起保管好,这个食谱也在第二章中,此外还需要保管好客户端配置文件basic-udp-client.conf

如何操作...

  1. 使用配置文件basic-udp-server.conf启动服务器:

            [root@server]# openvpn --config basic-udp-server.conf
    
    
  2. 接下来,通过向 basic-udp-client.conf 文件添加一行来创建客户端配置文件:

            tun-mtu 1400 
    
    

    将其保存为 example6-5-client.conf

  3. 启动客户端并查看客户端日志:

            [root@client]# openvpn --config example6-5-client.conf 
            ... WARNING: 'link-mtu' is used inconsistently, local='link-mtu 
            1441'', remote='link-mtu 1541'' 
            ... WARNING: 'tun-mtu' is used inconsistently, local='tun-mtu 
            1400'', remote='tun-mtu 1500'' 
            ... [openvpnserver] Peer Connection Initiated with server-
            ip:1194 
            ... TUN/TAP device tun0 opened 
            ... /sbin/ip link set dev tun0 up mtu 1400 
            ... /sbin/ip addr add dev tun0 10.200.0.2/24 broadcast 
            10.200.0.255 
            ... Initialization Sequence Completed 
    
    

    当隧道建立时会出现一些警告,但连接已初始化。

  4. 可以通过链路发送流量,我们可以使用 ping 命令来验证:

            [client]$ ping -c 2 10.200.0.1 
            PING 10.200.0.1 (10.200.0.1) 56(84) bytes of data. 
            64 bytes from 10.200.0.1: icmp_seq=1 ttl=64 time=30.6 ms 
            64 bytes from 10.200.0.1: icmp_seq=2 ttl=64 time=30.7 ms 
    
    
  5. 但是,考虑到发送更大包时,例如:

            [client]$ ping -s 1450 10.200.0.1
    
    

    在这种情况下,客户端日志文件中会出现以下信息:

            ... Authenticate/Decrypt packet error: packet HMAC 
            authentication failed
            ... Authenticate/Decrypt packet error: packet HMAC 
            authentication failed
    
    

如果客户端尝试下载大文件,也会发生同样的事情。

它是如何工作的...

MTU 决定了可以通过隧道发送的最大数据包大小,且不需要将数据包拆分(分片)成多个部分。如果客户端和服务器对 MTU 大小意见不一致,则服务器会向客户端发送过大的数据包。这会导致 HMAC 失败(如果使用 tls-auth,如本配方所示),或者超大的部分数据包会被丢弃。

还有更多...

在 Windows 平台上,修改 OpenVPN 使用的 Tap-Win32 适配器的 MTU 设置并不容易。可以指定 tun-mtu 指令,但 Windows 版本的 OpenVPN 无法更改实际的 MTU 设置,因为 Windows 直到 Vista 版本才支持这一功能。然而,OpenVPN 目前尚不具备在 Windows 上更改 MTU 大小的能力。

另请参见

  • 第九章,性能调优,提供了一些关于如何优化 tun-mtu 指令的提示和示例

故障排除网络连接问题

本配方将重点介绍在 OpenVPN 配置正确时,但网络连接有问题时,通常会看到的日志消息。在大多数情况下,这是由于防火墙阻止了对服务器或客户端的访问。在本配方中,我们显式地阻止对服务器的访问,然后尝试连接它。

准备工作

使用第二章中的第一个配方设置客户端和服务器证书,客户端-服务器仅 IP 网络。对于本配方,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端运行的是 Fedora 22 Linux 和 OpenVPN 2.3.11。保留客户端配置文件 basic-udp-client.conf,以及来自第二章的 服务器端路由 配方中的配置文件 basic-udp-server.conf,同时保留客户端配置文件 basic-udp-client.conf

如何操作...

  1. 使用配置文件 basic-udp-server.conf 启动服务器:

            [root@server]# openvpn --config basic-udp-server.conf
    
    
  2. 在服务器上,使用 iptables 显式阻止对 OpenVPN 的访问:

            [root@server]# iptables -I INPUT -p udp --dport 1194 -j DROP
    
    
  3. 接下来,使用配置文件 basic-udp-client.conf 启动客户端:

            [root@client]# openvpn --config basic-udp-client.conf
    
    

    客户端将尝试使用 UDP 协议连接服务器。过了一段时间,由于没有流量通过,超时发生,客户端将重新启动:

            ... TLS Error: TLS key negotiation failed to occur within 60 
            seconds (check your network connectivity) 
            ... TLS Error: TLS handshake failed 
            ... SIGUSR1[soft,tls-error] received, process restarting 
    
    

    中止客户端并停止服务器。

工作原理...

当 OpenVPN 被配置为使用默认的 UDP 协议时,客户端将等待来自服务器的答复 60 秒。如果没有收到答复,连接将重新启动。由于我们明确地阻止了 UDP 流量,超时会发生,客户端始终无法连接。

客户端等待连接开始的时间由以下指令控制:

hand-window N 

这里,N是等待初始握手完成的秒数。默认值为 60 秒。

当然,可以通过删除防火墙规则来修复连接。

还有更多...

UDP 协议和 TCP 协议之间的主要区别之一是连接的建立方式:每个 TCP 连接都需要客户端和服务器通过 TCP 握手来启动。如果握手失败,则连接不会建立。无需等待来自服务器的流量返回,因为连接本身会被断开:

... Attempting to establish TCP connection with openvpnserver:1194 [nonblock]
... TCP: connect to openvpnserver:1194 failed, will try again in 5 seconds: Connection refused

排查客户端配置目录问题

在这个配方中,我们将演示如何排查与使用client-config-dir指令相关的问题。这个指令可以用来指定一个目录用于存放所谓的 CCD 文件。CCD 文件可以包含 OpenVPN 指令,根据客户端的证书为客户端分配一个特定的 IP 地址。经验表明,这个指令容易被配置错误。在这个配方中,我们将故意进行一种常见的配置错误,然后展示如何排查该问题。

准备就绪

使用第二章中的第一个配方,客户端-服务器 IP-only 网络,设置客户端和服务器证书。对于这个配方,服务器运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11,而客户端运行的是 Fedora 22 Linux 和 OpenVPN 2.3.11。保持客户端配置文件basic-udp-client.conf和来自服务器端路由配方中的basic-udp-server.conf配置文件一同备用,这个配方也来自第二章,客户端-服务器 IP-only 网络,同时还需要客户端配置文件basic-udp-client.conf

操作方法...

  1. 将以下行附加到配置文件basic-udp-server.conf

            client-config-dir /etc/openvpn/cookbook/clients 
            ccd-exclusive 
    
    

    将其保存为example6-7-server.conf

  2. 确保/etc/openvpn/cookbook/clients目录只有 root 用户可访问:

            [root@server]# chown root /etc/openvpn/cookbook/clients
            [root@server]# chmod 700  /etc/openvpn/cookbook/clients
    
    
  3. 启动服务器:

            [root@server]# openvpn --config example6-7-server.conf
    
    
  4. 接下来,使用配置文件basic-udp-client.conf启动客户端:

            [root@client]# openvpn --config basic-udp-client.conf
    
    

然后,客户端将以以下信息失败连接:

... [openvpnserver] Peer Connection Initiated with server-ip:1194 
... AUTH: Received AUTH_FAILED control message 

服务器日志文件有点混乱:首先提到读取 CCD 文件client1时出现问题,但随后又说明客户端已连接:

... client-ip:45432 TLS Auth Error: --client-config-dir authentication failed for common name 'client1' file=''/etc/openvpn/cookbook/clients/client1'' 
... client-ip:45432 [client1] Peer Connection Initiated with client-ip:45432 

然而,VPN 连接没有正确初始化。

它是如何工作的……

以下指令被 OpenVPN 服务器用来在/etc/openvpn/cookbook/clients目录中查找客户端证书名称(CN)的 CCD 文件:

client-config-dir /etc/openvpn/cookbook/clients 
ccd-exclusive 

第二个指令ccd-exclusive的目的是仅允许那些存在 CCD 文件的客户端。如果某个客户端没有对应的 CCD 文件,则该客户端将被拒绝访问。客户端证书的名称会在服务器日志中列出:

... client-ip:45432 TLS Auth Error: --client-config-dir authentication failed for common name 'client1' 

然而,也可以通过以下方式获取:

openssl x509 -subject -noout -in client1.crt 

查找以/CN=开头的第一部分,并将所有空格转换为下划线。

OpenVPN 服务器进程是以nobody用户身份运行的。由于我们对/etc/openvpn/cookbook/clients目录设置了非常严格的权限,因此该用户无法读取该目录中的任何文件。当带有client1证书的客户端连接时,OpenVPN 服务器无法读取 CCD 文件(即使该文件可能存在)。由于ccd-exclusive指令的存在,客户端因此被拒绝访问。

还有更多……

本节将解释如何增加日志的详细程度以及一些最常见的client-config-dir错误。

更详细的日志记录

增加日志的详细程度通常在排查client-config-dir问题时非常有帮助。使用verb 5并具备正确权限时,你将在 OpenVPN 服务器日志中看到以下日志条目:

client1/client-ip:39814 OPTIONS IMPORT: reading client specific options from: /etc/openvpn/cookbook/clients/client1 

如果在服务器日志中没有看到此消息,那么可以安全地假设 CCD 文件没有被读取。

其他常见的client-config-dir错误

有一些常见的client-config-dir错误:

  • 使用非绝对路径来指定client-config-dir指令,例如:

            client-config-dir clients
    
    

    这在某些情况下可能有效,但在启动服务器时,或者与--chroot--cd等指令结合使用时,必须非常小心。尤其是在使用--chroot指令时,所有路径,包括绝对路径,将相对于chroot路径。

  • CCD 文件本身必须正确命名,且没有任何扩展名。这通常会让 Windows 用户感到困惑。请查看服务器日志,查看 OpenVPN 服务器认为客户端证书的/CN= name是什么。此外,请注意,OpenVPN 会重写某些字符,比如空格。有关将要重映射的字符的完整列表,请参考手册中的字符串类型和重映射部分。

  • CCD 文件及其完整路径必须对 OpenVPN 服务器进程运行的用户可读(通常是nobody)。

另见

  • 第二章中的使用 client-config-dir 文件配方,客户端-服务器仅 IP 网络,解释了客户端配置文件的基本用法。

排查多个远程问题

在本示例中,我们将演示如何排查使用多个remote指令时出现的问题。能够使用多个remote指令是 OpenVPN 2.2 版本以来的一项较少为人知的功能。它允许用户指定多个连接配置文件,以连接到不同的主机、不同的端口和不同的协议(例如,TCP 与 UDP)。

使用此指令时,如果在配置文件的其他地方或在命令行中指定额外的指令时,存在一个需要注意的陷阱。在这个示例中,我们将展示这个陷阱是什么。

正在准备中

使用第二章中的第一个配方设置客户端和服务器证书,客户端-服务器 IP-only 网络。在这个配方中,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端计算机运行的是 Fedora 22 Linux 和 OpenVPN 2.3.11。请将客户端配置文件basic-udp-client.confbasic-udp-server.conf(来自服务器端路由配方,第二章)一起保留,并使用客户端配置文件basic-udp-client.conf

如何操作...

  1. 使用配置文件basic-udp-server.conf启动服务器:

    [root@server]# openvpn --config basic-udp-server.conf
    
    
  2. 接下来,创建客户端配置:

            client 
            remote openvpnserver.example.com 1195 udp 
            remote openvpnserver.example.com 1196 tcp 
            port 1194 
    
            dev tun 
            nobind 
    
            remote-cert-tls server 
            tls-auth /etc/openvpn/cookbook/ta.key 1 
            ca       /etc/openvpn/cookbook/ca.crt 
            cert     /etc/openvpn/cookbook/client1.crt 
            key      /etc/openvpn/cookbook/\client1.key  
    
    

    请注意,我们指定了两个连接配置,一个使用 UDP 协议连接到服务器,端口 1195,另一个使用 TCP 协议,端口 1196。然而,我们期望使用port 1194这一行覆盖端口号。将此文件保存为example6-8-client.conf

  3. 启动客户端:

            [root@client]# openvpn --config example6-8-client.conf
    
    

    然后,客户端会因以下错误信息而无法连接:

            ... UDPv4 link local: [undef] 
            ... UDPv4 link remote: [AF_INET]server-ip:1195 
    
    

    所以,即使我们明确声明了port 1194,客户端仍然使用 UDP 协议,端口 1195进行连接。

它是如何工作的...

当你使用以下方式指定远程连接条目时:

 remote openvpnserver.example.com 1195 udp

OpenVPN 会将其内部转换为连接配置文件。通常,连接配置文件会继承全局配置中的设置。连接配置文件中的任何内容都会覆盖全局配置中指定的内容,即使它在配置文件中稍后指定,或在命令行中指定。因此,port 1194这一行没有任何效果,客户端尝试使用第一个(默认)remote连接配置文件、UDP 协议和端口 1195进行连接。

要解决这个问题,需要在配置文件中的remote行修改端口号。

还有更多...

指定remote openvpnserver.example.com 1195 udp的另一种方法是使用连接块:

<connection> 
    remote openvpnserver.example.com 
    port 1195  
    proto udp 
</connection> 

然而,在连接块内部,您可以指定更多的指令,正如我们将在使用连接块这一配方中看到的那样,位于第十章,高级配置

另请参见

  • 在第十章的使用连接块一节中,详细讲解了连接块的使用方法。

故障排除桥接问题

在本节中,我们将演示如何排除与桥接相关的常见问题。OpenVPN 桥接配置可能很棘手,因为警告和错误信息可能令人困惑。在本节中,我们将故意制造一个常见的配置错误,并展示如何排除故障。

准备工作

使用第二章中的第一个食谱设置客户端和服务器证书,客户端-服务器仅 IP 网络。对于这个食谱,服务器运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端运行的是 Fedora 22 Linux 和 OpenVPN 2.3.11。请保留来自桥接 - Linux食谱的脚本example3-3-bridge-startexample3-3-bridge-stop,以及来自启用客户端到客户端流量食谱中的客户端配置文件example-3-2-client2.ovpn,这些都来自第三章,客户端-服务器以太网风格网络

操作方法...

  1. 创建服务器配置文件:

            proto udp 
            port 1194 
            dev tap 
            server-bridge 192.168.4.65 255.255.255.0 192.168.4.128 
            192.168.4.200 
            push "route 192.168.4.0 255.255.255.0" 
    
            tls-auth /etc/openvpn/cookbook/ta.key 0 
            ca       /etc/openvpn/cookbook/ca.crt 
            cert     /etc/openvpn/cookbook/server.crt 
            key      /etc/openvpn/cookbook/server.key 
            dh       /etc/openvpn/cookbook/dh2048.pem 
    
            persist-key 
            persist-tun 
            keepalive 10 60 
    
            user  nobody 
            group nobody  # use "group nogroup" on some distros 
    
            daemon 
            log-append /var/log/openvpn.log 
    
    

    请注意,我们并未明确指定适配器名称(tap0)。将其保存为example-6-9-server.conf

  2. 创建网络桥接并验证其工作状态:

     [root@server]# bash example3-3-bridge-start 
              TUN/TAP device tap0 opened 
              Persist state set to: ON 
     [root@server]# brctl show 
              bridge name bridge id         STP enabled interfaces 
              br0         8000.00219bd2d422 no          eth0 
                           tap0 
    
    
  3. 启动 OpenVPN 服务器:

     [root@server]# openvpn --config example6-9-server.conf
    
    
  4. 启动客户端:操作方法...

  5. 现在,尝试连接到服务器:

    [WinClient]C:> ping 192.168.4.65
    
    

    即使连接已经建立,客户端仍然无法连接到服务器。

    请记住,在停止 OpenVPN 服务器进程后,关闭以太网桥接。

它是如何工作的……

本例中的连接失败是因为 OpenVPN 服务器在启动时打开了一个新的 tap 适配器,而不是连接到桥接。服务器日志文件中给出了提示:

... TUN/TAP device tap1 opened 

检查服务器上的 tap 接口时,我们看到现在有两个 tap 接口:

[root@server]# ip addr show
...
39: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue 
    state UNKNOWN 
 link/ether 00:25:90:c0:3e:d0 brd ff:ff:ff:ff:ff:ff
 inet 192.168.4.65/24 brd 192.168.4.255 scope global br0
 inet6 fe80::225:90ff:fec0:3ed0/64 scope link 
 valid_lft forever preferred_lft forever
40: tap1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 
    100
 link/ether ae:9f:3e:ae:93:ba brd ff:ff:ff:ff:ff:ff

第二个 tap 接口,tap1,是 OpenVPN 正在使用的接口,并且没有分配 IP 地址!

为了解决这个问题,需要在服务器配置文件中指定正确的 tap 适配器。

另见

  • 第三章中的Linux - 桥接一节,详细解释了如何在 Linux 上设置桥接。

如何读取 OpenVPN 日志文件

排除 OpenVPN 配置问题通常需要正确地阅读和解释 OpenVPN 日志文件。在这个指南中,将不会介绍 OpenVPN 的新特性,而是会详细讲解如何分析 OpenVPN 日志文件。将会使用本章前面 排除 MTU 和 tun-mtu 问题 这一节中的配置作为起始点。

准备就绪

使用与本章前面 排除 MTU 和 tun-mtu 问题 章节中的相同配置进行操作。本节中,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11,而客户端运行的是 Fedora 22 Linux 和 OpenVPN 2.3.11。保留从 第二章 服务器端路由 这一节中获得的 basic-udp-server.conf 配置文件。对于客户端,保留从 排除 MTU 和 tun-mtu 问题 章节中获得的 example6-5-client.conf 配置文件。

如何操作...

  1. 使用配置文件 basic-udp-server.conf 启动服务器:

    [root@server]# openvpn --config basic-udp-server.conf
    
    
  2. 接下来,使用增加的详细日志设置启动客户端,并且日志文件中不包含时间戳:

    [root@client]# openvpn --config example6-5-client.conf \
     --verb 7 --suppress-timestamps
    
    

    连接会启动,但无法发送大数据包。

  3. 通过输入以下内容来触发错误:

    [client]$ ping -c 1 10.200.0.1
    [client]$ ping -c 1 -s 1450 10.200.0.1
    
    
  4. 中止客户端。日志文件会很快变得很大。

  5. 使用文本编辑器打开日志文件并浏览它。日志文件的一般结构将在下一部分中解释。

它是如何工作的...

日志文件的第一部分包含了配置文件和命令行参数中指定的配置信息。这一部分从以下一行开始:

Current Parameter Settings: 
  config = 'example6-5-client.conf' 

它以以下这一行结束:

OpenVPN 2.3.11 x86_64-redhat-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [MH] [IPv6] built on May 10 2016 

这一部分大约有 275 行,具体取决于配置,包含了 OpenVPN 所认为的配置内容。请仔细检查此部分,确保你与它的配置一致。

接下来的有趣部分如下:

Control Channel Authentication: using '/etc/openvpn/cookbook/ta.key' as a OpenVPN static key file 
Outgoing Control Channel Authentication: Using 160 bit message hash 'SHA1' for HMAC authentication 
Outgoing Control Channel Authentication: HMAC KEY: 51cc24c0 ... 
Outgoing Control Channel Authentication: HMAC size=20 ... Incoming Control Channel Authentication: Using 160 bit ... 
Incoming Control Channel Authentication: HMAC KEY: 1c748f91 ...  
Incoming Control Channel Authentication: HMAC size=20 ...  

这一部分显示了 tls-auth 密钥已被读取并使用,且两个单独的 HMAC 密钥已被派生。密钥实际上会打印在日志文件中,因此你可以通过服务器日志文件中的输出与之进行对照。服务器接收的密钥应与客户端发送的密钥相同,反之亦然。本章前面 密钥不匹配 这一节中的配置错误会出现在这里。

紧接在这一部分之后是警告,它是本章前面 排除 MTU 和 tun-mtu 问题 章节中配置错误的根本原因:

WARNING: normally if you use --mssfix and/or --fragment, you should also set --tun-mtu 1500 (currently it is 1400) 

WARNING 开头的日志信息应始终特别关注。在某些情况下,这些警告可以忽略,但在本例中,它是导致 VPN 连接无法正常工作的根本原因。

在这个警告之后,会出现一系列如下形式的消息:

UDPv4 link remote: [AF_INET]server-ip:1194 
UDPv4 WRITE [42] to [AF_INET]server-ip:1194: P_CONTROL_HARD_RESET_CLIENT_V2 kid=0 pid=[ #1 ] [ ] pid=0 DATA len=0 
UDPv4 READ [54] from [AF_INET]server-ip:1194: P_CONTROL_HARD_RESET_SERVER_V2 kid=0 pid=[ #1 ] [ 0 ] pid=0 DATA len=0 
TLS: Initial packet from [AF_INET]server-ip:1194, sid=c483bcc9 a60cc834 
PID_TEST [0] [TLS_AUTH-0] [] 0:0 1469290891:1 t=1469290891[0] r=[0,64,15,0,1] sl=[0,0,64,528] 
UDPv4 WRITE [50] to [AF_INET]server-ip:1194: P_ACK_V1 kid=0 pid=[ #2 ] [ 0 ] 
UDPv4 WRITE [249] to [AF_INET]server-ip:1194: P_CONTROL_V1 kid=0 pid=[ #3 ] [ ] pid=1 DATA len=207 

这些消息都是客户端与服务器之间初始握手的一部分,用于交换配置信息、加密密钥及其他建立 VPN 连接所需的信息。紧接着是另一个关于配置错误的提示:

WARNING: 'link-mtu' is used inconsistently, local='link-mtu 1441', remote='link-mtu 1541' 
WARNING: 'tun-mtu' is used inconsistently, local='tun-mtu 1400', remote='tun-mtu 1500'  

我们跳过了许多 TLS_prf 消息,继续到达连接握手的结束部分:

Control Channel: TLSv1.2, cipher TLSv1/SSLv3 DHE-RSA-AES256-GCM-SHA384, 2048 bit RSA 
[openvpnserver] Peer Connection Initiated with [AF_INET]server-ip:1194 

此时,OpenVPN 客户端已与服务器建立了初始连接,若有配置指令推送,客户端现在已准备好处理:

PUSH: Received control message: 'PUSH_REPLY,route-gateway 10.200.0.1,topology subnet,ping 10,ping-restart 60,ifconfig 10.200.0.2 255.255.255.0' 

这是一个重要的检查项,它显示了服务器实际上推送给客户端的内容。确认这是否与你认为服务器应该推送的内容一致。

之后,启动并初始化本地 TUN 适配器,第一批数据包开始流动。

第一个 ping 命令运行正常,从以下部分可以看到:

TUN READ [84] 
... 
UDPv4 WRITE [125] to server-ip:1194: P_DATA_V1 kid=0 DATA len=124 
UDPv4 READ [125] from server-ip:1194: P_DATA_V1 kid=0 DATA len=124 
TLS: tls_pre_decrypt, key_id=0, IP=server-ip:1194 
TUN WRITE [84] 

TUN READ 是从 TUN 接口读取的 ping 命令,随后通过加密通道写入远程服务器。注意包大小的差异:通过加密隧道发送的包为 125 字节,比从 TUN 接口读取的原始包大 41 字节。这正好匹配了前面日志文件中所显示的 link-mtutun-mtu 选项之间的差异。

接下来是 ping -s 1450 命令出现问题的部分。如果接口的 MTU 设置为 1400,1450 字节的 ping 无法一次性读取,因此需要进行两次 TUN READ 才能捕获所有数据:

TUN READ [1396] 
... 
UDPv4 WRITE [1437] to server-ip:1194: P_DATA_V1 kid=0 DATA len=1436 
TUN READ [102] 
... 
UDPv4 WRITE [141] to server-ip:1194: P_DATA_V1 kid=0 DATA len=140 

注意,数据实际上是作为两个独立的数据包发送到服务器的。这是完全正常的行为,因为数据包需要进行分段。包的大小与 MTU 大小之间的计算在这种情况下失效,因为第二个数据包并不是一个完整的 IP 包。

服务器接收到大的 ping 命令并发送了同样大的回复。由于服务器的 MTU 设置为 1500,因此无需对数据进行分段,数据作为一个完整的数据包到达客户端:

UDPv4 READ [1441] from server-ip:1194: P_DATA_V1 kid=0 DATA len=1440 
TLS: tls_pre_decrypt, key_id=0, IP=server-ip:1194 
Authenticate/Decrypt packet error: packet HMAC authentication failed 

然而,客户端期望接收到一个最大为 1400 字节的数据包。它无法正确解码较大的数据包,并输出 packet HMAC authentication failed 消息。

最后,当我们中止客户端时,会看到 interrupted system call 消息(在此情况下,使用了 Ctrl + C 中止客户端,并且在客户端实际停止之前,还会出现一系列清理消息):

event_wait : Interrupted system call (code=4) 
PID packet_id_free 
... 
TCP/UDP: Closing socket 
Closing TUN/TAP interface 
/sbin/ip addr del dev tun0 10.200.0.2/24 
PID packet_id_free 
SIGINT[hard,] received, process exiting 

考虑到客户端配置中包含了以下内容:

user nobody 

然后,我们也应该会看到类似这样的消息:

SIOCSIFADDR: Permission denied 
SIOCSIFFLAGS: Permission denied 
Linux ip addr del failed: external program exited with error status: 255 

在这种情况下,这些是无害的。

还有更多内容...

在基于 UNIX 的操作系统上,还可以通过 syslog 发送 OpenVPN 日志输出。这样可以让系统管理员通过单一的系统日志接口有效地管理大量计算机。要通过 syslog 发送日志消息,需将指令 log-append 替换为以下内容:

syslog [name] 

在这里,name是一个可选参数,用于指定 syslog 日志文件中 OpenVPN 实例的名称。如果在单个主机上运行多个 OpenVPN 实例,并且它们都使用syslog来记录输出和错误信息,那么这个参数特别有用。

第七章. 故障排除 OpenVPN - 路由

本章将涉及以下故障排除主题:

  • 缺失的返回路由

  • 使用 iroute 时缺失的返回路由

  • 所有客户端都能正常工作,除了 OpenVPN 端点

  • 源路由

  • Windows 上的路由和权限

  • 无法更改 Windows 网络位置

  • 故障排除客户端到客户端流量路由

  • 理解 MULTI: bad source 警告

  • 重定向默认网关时的故障

介绍

本章和前一章的主题是排除 OpenVPN 故障。本章重点讨论在设置 VPN 时常见的路由问题。由于在 openvpn-users 邮件列表上超过一半的问题都可以归结为路由问题,本章旨在为一些常见的路由配置错误提供解答。

因此,本章中的配方将首先处理故障排除,然后提供如何查找和解决配置错误的工具。

缺失的返回路由

在首次成功设置 OpenVPN 后,网络路由配置错误是非常常见的。在本配方中,我们将首先设置一个基本的 TUN 风格 VPN,如第二章,客户端-服务器 IP-only 网络 中所做的那样。起初,路由将无法工作,直到正确的路由被添加。本配方的目的是描述如何排除此类路由错误。

准备就绪

我们使用以下网络布局:

准备就绪

使用 第二章,客户端-服务器 IP-only 网络 中的 设置公钥和私钥 配方设置客户端和服务器证书。对于这个配方,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端运行 Fedora 22 Linux 和 OpenVPN 2.3.11。保持使用第二章,客户端-服务器 IP-only 网络 中的 服务器端路由 配方的配置文件 basic-udp-server.conf,以及客户端配置文件 basic-udp-client.conf

如何操作...

  1. 使用配置文件 basic-udp-server.conf 启动服务器:

    [root@server]# openvpn --config basic-udp-server.conf
    
    
  2. 接下来,启动客户端:

    [root@client]# openvpn --config basic-udp-client.conf
    ...
    ... Initialization Sequence Completed
    
    
  3. 此时,可以 ping 远程 VPN IP 和所有位于 VPN 服务器上的接口:

            [client]$ ping -c 2 10.200.0.1
            PING 10.200.0.1 (10.200.0.1) 56(84) bytes of data.
            64 bytes from 10.200.0.1: icmp_seq=1 ttl=64 time=25.2 ms
            64 bytes from 10.200.0.1: icmp_seq=2 ttl=64 time=25.1 ms
            [client]$ ping -c 2 10.198.0.10
            PING 10.198.0.10 (10.198.0.10) 56(84) bytes of data.
            64 bytes from 10.198.0.10: icmp_seq=1 ttl=64 time=24.7 ms
            64 bytes from 10.198.0.10: icmp_seq=2 ttl=64 time=25.0 ms
    
    

    如果这两个 ping 命令中的任何一个失败,那么 VPN 连接尚未成功建立,且无需继续。

  4. 如果没有在服务器端网关上添加路由,那么远程 10.198.0.0/16 网络上的所有其他主机将无法访问:

            [client]$ ping 10.198.0.1
            PING 10.198.0.1 (10.198.0.1) 56(84) bytes of data.
            ^C
            --- 10.198.0.1 ping statistics ---
            1 packets transmitted, 0 received, 100% packet loss, time 764ms
    
    
  5. 如果我们在远程网络的局域网网关上添加路由,明确转发所有 VPN 流量到 VPN 服务器,那么我们就可以访问远程局域网上的所有机器(就像在第二章的服务器端路由食谱中做的那样,客户端-服务器 IP-only 网络):

            [gateway]> ip route add 10.200.0.0/24 via 10.198.0.10
    
    

    这里,10.198.1.1是 VPN 服务器的局域网 IP 地址。在这种情况下,远程局域网网关运行的是 Linux 系统。添加静态路由到网关的确切语法将根据网关的型号和操作系统有所不同。

  6. 现在,所有的机器都可以访问:

            [client]$ ping 10.198.0.1
            PING 10.198.0.1 (10.198.0.1) 56(84) bytes of data.
            64 bytes from 10.198.0.1: icmp_seq=1 ttl=63 time=27.1 ms
            64 bytes from 10.198.0.1: icmp_seq=2 ttl=63 time=25.0 ms
    
    

它是如何工作的……

当 VPN 客户端尝试连接到服务器端局域网上的主机时,数据包会带有源 IP 地址和目标 IP 地址:

  • 源 IP = 10.200.0.2:这个地址是 VPN 隧道的 IP 地址

  • 目标 IP = IP:这是我们试图联系的主机的 IP 地址

远程主机希望回复一个数据包,并交换源 IP 地址和目标 IP 地址。当远程主机想要发送数据包时,它不知道该发送到哪里,因为地址10.200.0.2是我们的私有 VPN 地址。于是,它将数据包转发到局域网网关。然而,局域网网关也不知道应该将数据包返回到哪里,它会将数据包转发到其默认网关。当数据包到达直接连接到互联网的路由器时,该路由器通常会选择丢弃(丢掉)数据包,导致主机无法访问。

通过在远程局域网网关上添加路由——告诉它所有针对网络10.200.0.0/24的流量都应转发到 VPN 服务器——数据包就会被发送回正确的机器。VPN 服务器会将数据包转发回 VPN 客户端,连接得以建立。

首先 ping 远程 VPN 端点,然后 ping 服务器局域网 IP(10.198.0.10)的步骤,乍一看似乎是多余的,但这些步骤在排查路由问题时至关重要。如果这些步骤已经失败,那么就不需要再查看缺失的路由了。

还有更多……

本节将重点介绍针对本食谱中描述的问题的不同解决方案。

伪装

解决上述问题的一个快速且粗糙的方案概述见于第二章的服务器端路由食谱。在该食谱的*更多内容…*部分,使用伪装(即 NAT 的一种形式)来让所有流量看起来像是来自 OpenVPN 服务器本身。如果你无法控制远程局域网网关,这是一个完美的解决方案,但它并不是一个很干净的路由解决方案。当数据经过 NAT 时,某些应用程序可能表现得不太好。此外,从安全日志记录的角度来看,有时最好避免 NAT,因为你将多个 IP 地址映射到一个地址上,从而丧失了一些信息。

在局域网主机上添加路由

除了向远程局域网网关添加路由外,还可以在每个 VPN 客户端需要访问的远程局域网主机上添加路由。如果 VPN 客户端只需要访问有限的服务器端主机集,则此解决方案非常适合,但它的扩展性较差。

另请参见

  • 来自第二章的服务器端路由食谱,其中包含从服务器端局域网路由流量的基本设置

使用 iroute 时缺少返回路由

本食谱是前一个食谱的延续。在确保单个 VPN 客户端可以访问服务器端局域网后,下一步是确保 VPN 客户端背后的其他主机能够访问服务器端局域网中的主机。

在本食谱中,我们将首先设置一个 VPN,就像在第二章 路由:两侧的子网 食谱中一样。如果没有设置返回路由,那么客户端局域网中的主机将无法访问服务器端局域网中的主机,反之亦然。通过添加适当的路由,问题得以解决。

准备就绪

我们使用以下网络布局:

准备就绪

使用第二章的第一个食谱设置客户端和服务器证书,客户端-服务器 IP 仅网络。对于本食谱,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端运行 Fedora 22 Linux 和 OpenVPN 2.3.11。保留配置文件example2-5-server.conf,来自第二章的路由:两侧的子网食谱,以及客户端配置basic-udp-client.conf,来自第二章的服务器端路由食谱。

如何实现...

  1. 启动服务器:

            [root@server]# openvpn --config example2-5-server.conf
    
    
  2. 接下来,启动客户端:

            [root@client]# openvpn --config basic-udp-client.conf
            ...
            ... Initialization Sequence Completed
    
    
  3. 此时,可以 ping 远程 VPN IP 以及 VPN 服务器本身的所有接口,反之亦然:

            [client]$ ping -c 2 10.200.0.1
            PING 10.200.0.1 (10.200.0.1) 56(84) bytes of data.
            64 bytes from 10.200.0.1: icmp_seq=1 ttl=64 time=25.2 ms
            64 bytes from 10.200.0.1: icmp_seq=2 ttl=64 time=25.1 ms
            [client]$ ping -c 2 10.198.0.10
            PING 10.198.0.1 (10.198.0.10) 56(84) bytes of data.
            64 bytes from 10.198.0.10: icmp_seq=1 ttl=64 time=24.7 ms
            64 bytes from 10.198.0.10: icmp_seq=2 ttl=64 time=25.0 ms
            [server]$ ping -c 2 10.200.0.2
            PING 10.200.0.2 (10.200.0.2) 56(84) bytes of data.
            64 bytes from 10.200.0.2: icmp_seq=1 ttl=64 time=25.0 ms
            64 bytes from 10.200.0.2: icmp_seq=2 ttl=64 time=24.6 ms
            [server]$ ping -c 2 192.168.4.64
            PING 192.168.4.64 (192.168.4.64) 56(84) bytes of data.
            64 bytes from 192.168.4.64: icmp_seq=1 ttl=64 time=25.2 ms
            64 bytes from 192.168.4.64: icmp_seq=2 ttl=64 time=24.3 ms
    
    
  4. 服务器上的路由表显示远程网络已正确路由:

            [server]$ netstat -rn | grep tun0
            192.168.4.0   10.200.0.1 255.255.255.0 UG 0 0 0 tun0
            10.200.0.0 0.0.0.0       255.255.255.0 U  0 0 0 tun0
    
    
  5. 当我们尝试 ping 服务器端局域网的远程主机时,失败了,就像在前一个食谱中一样。反之,当我们尝试从服务器端局域网的主机 ping 客户端局域网的主机时,我们看到:

            [siteB-host]$ ping -c 2 192.168.4.66
            PING 192.168.4.66 (192.168.4.66) 56(84) bytes of data.
            --- 192.168.4.66 ping statistics ---
            2 packets transmitted, 0 received, 100% packet loss, time 999ms
    
    
  6. 通过在两侧的网关上添加适当的路由,路由恢复。首先是服务器端局域网的网关:

            [gateway1]> ip route add 192.168.4.0/24 via 10.198.0.10
    
    

    这里,10.198.0.10 是 VPN 服务器的局域网 IP 地址。

    接下来是客户端局域网的网关/路由器:

            [gateway2]> ip route add 10.198.0.0/16 via 192.168.4.64
    
    

    这里,192.168.4.64 是 VPN 客户端的局域网 IP 地址。

之后,局域网中的主机可以互相访问。

它是如何工作的...

与前一操作类似,当站点 A 局域网中的主机尝试连接站点 B 局域网中的主机时,数据包将以源和目的 IP 地址的方式发送:

  • 源 IP = 192.168.4.64:站点 A 的局域网地址

  • 目的 IP = 10.198.1.12:站点 B 的局域网地址

远程主机会希望用一个源和目的 IP 地址交换的包进行回复。当远程主机要发送数据包时,它将数据包转发到局域网网关。然而,局域网网关也不知道将数据包返回到哪里,它会将数据包转发到其默认网关。当数据包到达与互联网直接连接的路由器时,路由器通常会决定丢弃(丢掉)这些数据包,导致主机变得不可访问。

在前面的操作中出现了类似的问题,但现在数据包的 IP 地址是实际的站点 A 和站点 B 的局域网 IP 地址。

通过在两边添加适当的路由,问题得以缓解。

在排查这类路由问题时,首先从最内层的网络(此案例中为实际的 VPN)开始排查,然后向外层扩展:

  1. 首先,确保 VPN 端点可以互相访问。

  2. 确保 VPN 客户端可以访问服务器的局域网 IP,反之亦然。

  3. 确保 VPN 客户端能够访问服务器端局域网中的主机。

  4. 确保服务器端局域网中的主机能够看到 VPN 客户端。

  5. 确保客户端局域网中的主机能够看到 VPN 服务器。

  6. 最后,确保客户端局域网中的主机能够看到服务器端局域网中的主机,反之亦然。

还有更多内容...

同样,前面提到的 服务器端路由 操作中概述了对上述问题的一个简单快速的解决方案,该操作来自 第二章,客户端-服务器仅 IP 网络。在该操作中,使用了伪装技术,使得所有流量看起来都来自 OpenVPN 服务器本身。特别是在通过 VPN 连接子网时,这种做法并不可取,因为伪装使得无法确定是哪个客户端连接到哪个服务器,反之亦然。因此,在这种情况下,推荐使用完全路由的设置。

另请参见

  • 来自 第二章,客户端-服务器仅 IP 网络路由:两边的子网 操作详细解释了如何在客户端和服务器端设置路由。

除了 OpenVPN 端点,所有客户端都能正常工作

本操作是前一操作的延续。前一操作解释了如何在连接客户端局域网(或子网)到服务器端局域网时排查路由问题。然而,在前一操作中,故意做了一些路由配置上的遗漏。在本操作中,我们将重点关注排查这个相当常见的遗漏。

准备工作

我们使用以下网络布局:

准备工作

使用第二章中的第一个配方设置客户端和服务器证书,客户端-服务器 IP 仅网络。对于此配方,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11。

客户端运行的是 Fedora 22 Linux 和 OpenVPN 2.3.11。保留第二章中的 路由:两端子网 配方中的配置文件example2-5-server.conf,以及来自第二章中 服务器端路由 配方的客户端配置文件basic-udp-client.conf

如何操作...

  1. 启动服务器:

            [root@server]# openvpn --config example2-5-server.conf
    
    
  2. 接下来,启动客户端:

            [root@client]# openvpn --config basic-udp-client.conf
            ...
            ... Initialization Sequence Completed
    
    
  3. 在两端的网关上添加适当的路由:

            [gateway1]> ip route add 192.168.4.0/24 via 10.198.0.10
            [gateway2]> ip route add 10.198.0.0/16 via 192.168.4.64
    
    

    之后,所有局域网内的主机都可以相互访问。

  4. 我们通过 ping 两端局域网上的各种机器来验证这一点:

            [client]$ ping -c 2 10.198.0.10
            [server]$ ping -c 2 192.168.4.64
            [siteA-host]$ ping -c 2 10.198.0.1
            [siteB-host]$ ping -c 2 192.168.4.66
    
    

    它们都能正常工作。然而,当 VPN 服务器尝试 ping 客户端局域网中的一台主机时,它失败了:

            [server]$ ping -c 2 192.168.4.66
            PING 192.168.4.66 (192.168.4.66) 56(84) bytes of data.
            --- 192.168.4.66 ping statistics ---
            2 packets transmitted, 0 received, 100% packet loss, time 
            1009ms
    
    

    同样,客户端只能访问服务器的局域网 IP 地址,无法访问其他主机。

  5. 在 Linux 和 UNIX 主机上,可以明确指定源 IP 地址:

            [server]$ ping -I 10.198.0.10 -c 2 192.168.4.66
            PING 192.168.4.66 (192.168.4.66) 56(84) bytes of data.
            64 bytes from 192.168.4.66: icmp_seq=1 ttl=63 time=25.5 ms
            64 bytes from 192.168.4.66: icmp_seq=2 ttl=63 time=24.3 ms
    
    

    成功了!所以,问题出在数据包的源地址上。

  6. 通过在两端的网关上为 VPN 子网本身添加额外的路由,可以解决此问题:

            [gateway1]> ip route add 10.200.0.0/24 via 10.198.0.10
            [gateway2]> ip route add 10.200.0.0/24 via 192.168.4.64
    
    
  7. 现在,VPN 服务器可以访问客户端子网中的所有主机,反之亦然:

            [server]$ ping -c 2 192.168.4.66
            PING 192.168.4.66 (192.168.4.66) 56(84) bytes of data.
            64 bytes from 192.168.4.66: icmp_seq=1 ttl=63 time=25.3 ms
            64 bytes from 192.168.4.66: icmp_seq=2 ttl=63 time=24.9 ms
    
    

它是如何工作的...

要排查此类问题,将所有涉及局域网的源地址和目标地址列出来非常有用。在此案例中,问题出现在 VPN 服务器想要连接客户端局域网中的主机时。在 VPN 服务器上,发送到客户端主机的数据包是直接通过 VPN 接口发送的。因此,该数据包的源地址被设置为 VPN 接口本身的 IP 地址。于是,数据包具有以下 IP 地址:

  • 源 IP = 10.200.0.1:VPN 服务器的 IP 地址

  • 目标 IP = 192.168.4.66:站点 A 的局域网地址

远程主机将希望通过交换源和目标 IP 地址的方式响应数据包。当远程主机想要发送数据包时,它会将数据包转发到局域网网关。然而,局域网网关同样不知道将数据包返回到哪里,它会将数据包转发到默认网关。当数据包到达一个直接连接到互联网的路由器时,该路由器通常会决定丢弃(抛弃)数据包,导致主机无法访问。

这个问题仅出现在 VPN 服务器和 VPN 客户端上。在客户端和服务器端的其他主机上,使用的是局域网 IP 地址,且路由按照之前的配方正常工作。

通过在两端添加适当的路由,问题得以解决。

还有更多...

本配方中使用 NAT(网络地址转换)的一个好方法是从路由表中移除与 VPN IP 范围相关的任何条目。这可以通过仅伪装 VPN 端点地址来完成。如果这样做,网关上的额外路由将不再需要。例如,在服务器上添加一个 NAT 规则,在客户端上添加一个类似的规则,网关上的额外路由就不再需要。

[root@server]# iptables -t nat -I POSTROUTING -i tun0 -o eth0 \
 -s 10.200.0.0/24 -j MASQUERADE
[root@client]# iptables -t nat -I POSTROUTING -i tun0 -o eth0 \
 -s 10.200.0.0/24 -j MASQUERADE

请注意,在基于 Linux 和 UNIX 的操作系统上,源路由设置非常简单,但在 Windows 上则需要更多的操作。

另请参见

  • 第二章中的路由:两侧子网配方,客户端-服务器仅 IP 网络,详细说明了如何在客户端和服务器端设置路由。

源路由

随着网络配置变得更加复杂,对更多高级功能的需求也在增加,源路由功能就是其中之一。当服务器通过两个网络接口连接到网络(或互联网)时,通常会使用源路由(请参见下图)。在这种情况下,确保通过某一接口启动的连接保持在该接口上非常重要。如果 VPN 连接的传入流量是通过第一个接口进入,但返回流量是通过第二个接口发送回去,那么 VPN 连接等将会失败,正如我们在这个配方中将要看到的那样。源路由是大多数现代操作系统的高级功能。在本配方中,我们将展示如何使用 Linux 的iproute2工具设置源路由,但在其他操作系统上使用类似工具也可以实现相同的功能。

准备工作

我们使用以下网络布局:

准备工作

使用第二章中的第一个配方设置客户端和服务器证书,客户端-服务器仅 IP 网络。对于这个配方,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11,并连接到一个具有两个 IP 地址的路由器:192.168.4.65192.168.2.13;该系统的默认网关是192.168.2.1,意味着流量默认会通过 IP 地址为192.168.2.13的接口离开。第二网关的 IP 地址是192.168.4.1。客户端运行的是 Windows 7 64 位和 OpenVPN 2.3.11。客户端的 IP 地址是192.168.2.10,默认路由为192.168.2.1。保留第二章中服务器端路由配方中的配置文件basic-udp-server.conf,以及第二章中使用 ifconfig-pool 块配方中的客户端配置文件basic-udp-client.ovpn

如何操作…

  1. 使用配置文件 basic-udp-server.conf 启动服务器:

            [root@server]# openvpn --config basic-udp-server.conf
    
    
  2. 接下来,启动客户端:如何操作...

  3. 在此配置中,远程服务器地址 openvpnserver.example.com 解析为 192.168.4.65

    连接将无法启动,客户端的 OpenVPN 日志文件将显示以下消息,并重复几次:

            Wed Aug 25 16:24:28 2010 TCP/UDP: Incoming packet rejected from           192.168.2.13:1194[2], expected peer address: 192.168.4.65:1194 
            (allow this incoming source address/port by removing --remote 
            or adding --float)
    
    
  4. 通过添加一个源路由规则,将所有通过某接口(192.168.4.65)从另一个接口(子网 192.168.2.0/24)的主机传入的流量,重定向到出接口(192.168.2.0/24)的路由器(192.168.4.1),连接得以恢复:

            [root@server]# ip route add to default table 100 dev eth0 \
                              via 192.168.4.1
            [root@server]# ip rule add from 192.168.2.10 priority 50 \
                              table 100
            [root@server]# ip rule add to 192.168.2.10 priority 50 \
                               table 100
    
    

    现在,客户端可以成功连接到 VPN 服务器。

它是如何工作的...

当客户端 192.168.2.10 连接到 VPN 服务器 192.168.4.65 时,返回路由选择了最短路径,在这里的设置中是 192.168.2.1。服务器操作系统将数据包的返回 IP 地址设置为 192.168.2.13,因为这是与该网络关联的接口的 IP 地址。这使得 OpenVPN 客户端感到困惑,因为它连接到主机 192.168.4.65,但却接收到来自 192.168.2.13 的返回流量。通过明确强制流量从另一个接口(192.168.4.65)出去,这个不对称路由问题得以解决。

源路由规则的确切语法高度依赖于具体的网络配置,但在如何操作一节中概述的三条命令的基本思想是:

  • 创建一个 ID 为100的路由表,并将此表的默认网关设备设置为eth0,其 IP 地址为192.168.4.65

  • 创建一条路由规则,将任何来自客户端 192.168.2.10 的流量重定向到路由表

  • 创建一条路由规则,将任何希望离开客户端192.168.2.10的流量重定向到路由表

路由规则需要根据实际情况进行调整,因为这些规则会阻止某些其他类型的网络流量,但原理是正确的。

还有更多...

更高级的路由控制可以使用LARTCLinux 高级路由与流量控制)来完成。更好的方法是标记从接口传入的数据包,并仅将标记的数据包重定向到正确的外发接口。

Windows 上的路由和权限

在本教程中,我们将重点讨论用户在 VPN 客户端机器运行 Windows 且没有完全或提升权限时常遇到的常见错误。在某些情况下,OpenVPN 客户端能够成功连接,但远程服务器推送的路由未正确设置。本教程将重点讨论如何排查和修正这个错误。

准备中

使用第二章中的第一个配方设置客户端和服务器证书,客户端-服务器 IP 专用网络。对于这个配方,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11,而客户端运行的是 Windows 7 64 位和 OpenVPN 2.3.11。请保留来自第二章中 服务器端路由 配方的配置文件 basic-udp-server.conf,以及来自第二章中 使用 ifconfig-pool 块 配方的客户端配置文件 basic-udp-client.ovpn

如何操作...

  1. 以非特权用户身份登录 Windows,即没有 Power UserAdministrator 权限的用户。此外,确保暂时从 OpenVPN 中移除以管理员身份运行标志,以便不以提升的权限启动 OpenVPN。可以通过在 OpenVPN GUI 属性中取消勾选以管理员身份运行此程序标志来实现:如何操作...

  2. 使用配置文件 basic-udp-server.conf 启动服务器:

    [root@server]# openvpn --config basic-udp-server.conf
    
    
  3. 最后,启动客户端。如何操作...

连接将开始,OpenVPN GUI 灯会变为绿色。然而,客户端 OpenVPN 日志文件将显示以下消息:

... C:\WINDOWS\system32\route.exe ADD 10.198.0.0 MASK 255.255.0.0 10.200.0.1
... ROUTE: route addition failed using CreateIpForwardEntry: Network access is denied.   [status=65 if_index=2]
... Route addition via IPAPI failed [adaptive]
Thu Aug 26 16:47:53 2010 us=187000 Route addition fallback to route.exe

如果尝试访问服务器端 LAN 上的主机,将会失败:

[WinClient]C:\>ping 10.198.0.1
Pinging 10.198.0.1 with 32 bytes of data:
Request timed out.
Ping statistics for 10.198.0.1:
Packets: Sent = 1, Received = 0, Lost = 1 (100% loss)

解决此问题的方法是为用户提供适当的网络权限,或者恢复 OpenVPN 的提升权限。

它是如何工作的...

OpenVPN 客户端尝试打开 TAP-Win32 适配器,在默认安装中这是被允许的。然而,当服务器通过以下方式推送路由到客户端时:

push "route 10.198.0.0 255.255.0.0"

然后,由于缺少管理员权限,OpenVPN 客户端将无法实际将该路由添加到系统路由表中。然而,VPN 连接已经成功建立,并且 GUI 客户端显示连接成功。

请注意,即使没有 push "route" 语句,Windows OpenVPN 图形界面仍然显示绿色图标,表明连接已启动。从技术角度讲,连接确实已经建立,但这仍然应视为一个 bug。

还有更多内容...

Windows XP 及更高版本包含一个以管理员身份运行服务,允许用户暂时以更高的权限级别运行程序。这个机制在 Windows Vista/7 中得到了扩展,并在启动应用程序时成为默认设置。实际上,这就是在这些平台上运行 OpenVPN 时,openvpn-users 邮件列表上出现的许多问题的根本原因。

无法更改 Windows 网络位置

这篇食谱的标题看起来似乎与路由问题无关,但 Windows 的网络位置依赖于路由才能正常工作。从 Windows Vista 开始,微软引入了网络位置的概念。默认情况下,有多个网络位置:家庭工作公共(适用于 Windows 7),以及私人公共(适用于 Windows 8 及以上)。这些网络位置适用于所有网络适配器,包括 OpenVPN 的虚拟 TAP-Win 网络适配器。

家庭网络位置是为家庭网络设计的。同样,工作网络位置也为工作提供较高的信任级别,允许计算机共享文件、连接打印机等。在 Windows 8 及以上版本中,家庭工作网络位置合并成了可信的私人网络位置。公共网络位置不被信任,Windows 会限制对网络资源的访问,即使 Windows 防火墙被禁用。

OpenVPN 设置的路由属性决定了 TAP-Win 适配器是否被信任,从而决定是否允许文件共享。在这个食谱中,我们将展示如何更改 OpenVPN 设置,以便可以更改网络位置。

准备工作

使用第二章中的第一个食谱设置客户端和服务器证书,客户端-服务器 IP 仅网络。在这个食谱中,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端运行的是 Windows 7 64 位和 OpenVPN 2.3.11。请随时准备好第二章中重定向默认网关食谱中的配置文件example2-7-server.conf,以及第二章中的使用 ifconfig-pool 块食谱中的客户端配置文件basic-udp-client.ovpn客户端-服务器 IP 仅网络

如何操作...

  1. 使用配置文件example-2-7-server.conf启动服务器:

            [root@server]# openvpn --config example2-7-server.conf
    
    
  2. 接下来,启动客户端。如何操作...

  3. 进入网络和共享中心,并观察到 TAP 适配器位于公共网络部分,并且无法更改此设置。此外,尝试通过 VPN 隧道访问文件共享,这应该是无法实现的。

  4. 通过删除push redirect-gateway def1行中的def1来更改服务器配置:

            push "redirect-gateway" 
    
    
  5. 在两端重新启动 VPN 连接。

  6. 当 VPN 连接建立时,Windows 会询问你新网络的位置:网络如何操作...

  7. 选择工作网络位置,然后将新网络命名为VPN

  8. 现在,再次进入网络和共享中心,并观察到 TAP 适配器(命名为vpn0)位于工作网络位置VPN如何操作...

它是如何工作的...

尽管所有网络流量都通过 VPN 路由(使用 redirect-gateway def1),Windows 仍然不信任 VPN 适配器,因此会拒绝通过 VPN 隧道的完全访问。Windows 只有在网络适配器广告默认网关(0.0.0.0/0)时,才会信任该网络适配器,或者该网络适配器必须属于 Windows 域。可以通过将服务器配置更改为使用来解决此问题:

push "redirect-gateway" 

还有更多内容...

也可以使用 Windows 注册表编辑器更改网络位置,但不推荐这样做,因为这将把所有网络适配器标记为受信任。

排查客户端之间流量路由问题

在本例中,我们将排查一个 VPN 设置问题,该设置的目标是启用客户端之间的流量,但服务器配置文件中缺少 "client-to-client" 指令。在 TUN 类型的网络中,即使没有此指令,也可以允许客户端之间的流量,并且它还允许服务器管理员对客户端间的流量应用防火墙规则。在 TAP 类型的网络中,通常不可能做到这一点,正如在 还有更多内容... 部分将会解释的那样。

准备就绪

我们使用以下网络布局:

准备就绪

使用 第二章 的第一个例子,设置客户端和服务器证书,客户端-服务器 IP 网络。在这个例子中,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.3.11。第一个客户端运行 Fedora 22 Linux 和 OpenVPN 2.3.11。第二个客户端运行 Windows 7 64 位和 OpenVPN 2.3.11。保留配置文件 basic-udp-server.conf,该文件来自 第二章 中的 服务器端路由 例子,以及客户端配置文件 basic-udp-client.ovpn,该文件来自 第二章 中的 使用 ifconfig-pool 块 例子,客户端-服务器 IP 网络

如何操作...

  1. 使用配置文件 basic-udp-server.conf 启动服务器:

            [root@server]# openvpn --config basic-udp-server.conf
    
    
  2. 接下来,启动 Linux 客户端。

            [root@client]# openvpn --config basic-udp-client.conf
    
    
  3. 最后,启动 Windows 客户端:如何操作...

  4. 接下来,尝试从 Linux 客户端 ping Windows 客户端(确保没有防火墙阻止流量):

            [client]$ ping -c 2 10.200.0.3
            PING 10.200.0.3 (10.200.0.3) 56(84) bytes of data.
            --- 10.200.0.3 ping statistics ---
            2 packets transmitted, 0 received, 100% packet loss, time 
            10999ms
    
    

    可能主机已经可达,但在这种情况下,服务器上的防火墙非常宽松。

  5. 此时,可以在 VPN 服务器上设置 iptables 日志记录来进行排查:

            [root@server]# iptables -I FORWARD -i tun+ -j LOG
    
    

    然后再次尝试 ping。这将在 /var/log/messages 中产生以下消息:

            ... openvpnserver kernel: IN=tun0 OUT=tun0 SRC=10.200.0.2     
            DST=10.200.0.3 LEN=84 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF 
            PROTO=ICMP TYPE=8 CODE=0 ID=40808 SEQ=1 
            ... openvpnserver kernel: IN=tun0 OUT=tun0 SRC=10.200.0.2 
            DST=10.200.0.3 LEN=84 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF 
            PROTO=ICMP TYPE=8 CODE=0 ID=40808 SEQ=2
    
    

第一个客户端 10.200.0.2 正试图连接第二个客户端 10.200.0.3。此问题可以通过向服务器配置文件中添加 client-to-client 配置指令并重启 OpenVPN 服务器来解决,或者通过允许隧道流量转发来解决:

        [server]# iptables -I FORWARD -i tun+ -o tun+ -j ACCEPT
        [server]# echo 1 > /proc/sys/net/ipv4/ip_forward

它是如何工作的...

当第一个 OpenVPN 客户端尝试连接到第二个客户端时,数据包会被发送到服务器本身。OpenVPN 服务器不知道如何处理这些数据包,因此将它们交给内核。内核根据是否启用了路由以及防火墙规则(iptables)是否允许来转发数据包。如果不允许,数据包将被丢弃,第二个客户端将无法到达。

通过添加以下指令,OpenVPN 服务器进程可以内部处理客户端到客户端的流量,绕过内核转发和防火墙规则:

client-to-client 

另一种解决方案是正确设置 Linux 内核中的路由,这种方式稍微更安全,但可扩展性较差。

还有更多...

在 TAP 风格的网络中,上述 iptables 规则不起作用。在 TAP 风格的网络中,所有客户端都属于同一个广播域。当省略 client-to-client 指令时,如果客户端尝试连接到另一台客户端,它首先会发送 arp 谁有 消息,以查找另一台客户端的 MAC 地址。OpenVPN 服务器将忽略这些请求,也不会将它们转发给其他客户端,无论是否设置了 iptables 规则。因此,客户端在没有 client-to-client 指令的情况下,不能轻松地相互访问,除非使用像代理 ARP 这样的技巧。

另请参阅

  • 启用客户端到客户端流量 食谱,来自 第三章,客户端-服务器以太网风格网络,该部分解释了如何在 TAP 风格的环境中设置客户端到客户端的流量。

理解 MULTI: bad source 警告

在这个食谱中,我们再次关注 VPN 配置,其中我们尝试将客户端局域网(LAN)连接到服务器端局域网。通常,这通过向 OpenVPN 服务器配置添加 client-config-dir 指令,然后添加相应的 CCD 文件来完成。然而,如果找不到 CCD 文件或该文件不可读,VPN 连接将正常工作,但客户端局域网上的主机将无法访问服务器端局域网上的主机,反之亦然。在这种情况下,如果日志的详细级别足够高,OpenVPN 服务器日志文件将显示类似 MULTI: bad source 的消息。在本食谱中,我们将首先像在 路由: 两端子网 食谱中那样设置 VPN,参见 第二章,客户端-服务器 IP-only 网络,但客户端缺少 CCD 文件。接下来,我们将展示如何触发 MULTI: bad source 警告以及如何解决这个问题。

准备工作

我们使用以下网络布局:

准备工作

使用第二章的第一个配方来设置客户端和服务器证书,客户端-服务器 IP 专用网络。在这个配方中,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端运行的是 Fedora 22 Linux 和 OpenVPN 2.3.11。保留使用 client-config-dir 文件配方中的配置文件example2-5-server.conf,来自第二章,客户端-服务器 IP 专用网络。对于客户端,保留服务器端路由配方中的配置文件basic-udp-client.conf,该配方同样来自第二章,客户端-服务器 IP 专用网络

如何做到...

  1. 首先,确保客户端 CCD 文件不可访问:

            [root@server]# chmod 700 /etc/openvpn/cookbook/clients
    
    
  2. 使用配置文件example2-5-server.conf启动服务器,并增加输出详细程度:

            [root@server]# openvpn --config example2-5-server.conf --verb 5
    
    
  3. 接下来,启动客户端以成功连接:

            [root@client]# openvpn --config basic-udp-client.conf
            ...
            ... Initialization Sequence Completed
    
    

    然而,当客户端侧的某个主机尝试访问服务器侧的计算机时,OpenVPN 服务器日志文件中会出现以下信息:

            ... openvpnclient1/client-ip:58370 MULTI: bad source address 
            from client [192.168.4.66], packet dropped
    
    

在这个配方中,问题的根本原因可以像在第六章,排查 OpenVPN 配置问题排查 client-config-dir 问题配方中那样解决,修复目录/etc/openvpn/cookbook/clients的权限,然后重新连接 OpenVPN 客户端。

它是如何工作的...

为了将远程局域网连接到 OpenVPN 服务器,需要两个服务器配置指令:

route remote-lan remote-mask 
client-config-dir /etc/openvpn/cookbook/clients 

还需要一个 CCD 文件,包含客户端证书的名称。CCD 文件包含:

iroute remote-lan remote-mask 

如果没有这个,OpenVPN 服务器无法知道远程网络连接到哪个 VPN 客户端。如果从一个 OpenVPN 服务器未识别的客户端接收到数据包,那么该数据包将被丢弃,并且在"verb 5"或更高等级时,会打印警告MULTI: bad source

还有更多...

除了上述解释的警告之外,还有另一个主要原因会导致MULTI: bad source信息出现。

其他出现的 MULTI: bad source 信息

有时,即使没有客户端局域网连接到 VPN 客户端,OpenVPN 服务器日志中也会打印MULTI: bad source信息。这种情况通常发生在运行 Windows 的 VPN 客户端中。当通过 VPN 连接访问文件共享时,Windows 有时会发送与 VPN 接口源 IP 地址不同的源 IP 地址的数据包。这些数据包无法被 OpenVPN 服务器识别,从而打印出警告。解决此问题的方法尚不清楚。

另见

  • 来自第二章的路由:两侧的子网配方,客户端-服务器 IP 专用网络,解释了如何设置client-config-dir配置的基础知识。

  • 来自第六章,故障排除 OpenVPN 配置中的故障排除 client-config-dir 问题食谱更深入地探讨了使用client-config-dir指令时常见的一些错误。

重定向默认网关失败

在本食谱中,我们将排查一个不常见但非常持久的问题,通常出现在设置 VPN 连接时。当使用redirect-gateway指令来重定向 OpenVPN 客户端的默认网关时,有时会导致客户端失去所有的 Internet 连接。这种情况尤其在 OpenVPN 运行的客户端计算机通过 PPP 连接(如 PPPoE 或 PPPoA)连接到网络或互联网时发生,尤其是使用手机进行 GPRS/UMTS 连接时。

当这种情况发生时,OpenVPN 有时无法在重定向之前确定默认网关。默认网关被重定向到 OpenVPN 隧道后,整个隧道会崩溃,因为所有流量(包括加密的隧道流量)都被重定向到隧道中,导致 VPN 锁死。

本食谱将展示如何检测这种情况以及可以采取的措施。在此食谱中,我们不会使用 GPRS/UMTS 连接,而是使用 PPP-over-SSH 连接,其行为类似,并且更容易获取。

准备工作

我们使用以下网络布局:

准备工作

使用第二章中的第一个食谱,客户端-服务器仅 IP 网络,来设置客户端和服务器证书。对于这个食谱,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端计算机运行的是 Fedora 22 Linux 和 OpenVPN 2.3.11。保持使用服务器端路由食谱中的配置文件basic-udp-server.conf,该食谱来自第二章,客户端-服务器仅 IP 网络

确保客户端通过 PPP 连接到网络,否则本食谱标题中描述的问题将不会发生。对于此食谱,使用 PPP-over-SSH 连接,并且默认路由被更改为指向ppp0设备。

如何操作...

  1. 启动服务器并添加一个额外的参数来指引默认网关:

            [root@server]# openvpn --config basic-udp-server.conf \
              --push "redirect-gateway"
    
    
  2. 创建客户端配置文件:

            client 
            proto udp 
            # next is the IP address of the VPN server via the 
            # PPP-over-SSH link 
            remote 192.168.222.1 
            port 1194 
    
            dev tun 
            nobind 
    
            ca       /etc/openvpn/cookbook/ca.crt 
            cert     /etc/openvpn/cookbook/client1.crt 
            key      /etc/openvpn/cookbook/client1.key 
            tls-auth /etc/openvpn/cookbook/ta.key 1 
    
            user nobody 
            verb 5 
    
    

    将其保存为example7-9-client.conf

  3. 启动客户端之前检查系统路由:

            [root@client]# netstat -rn
            172.30.0.10    172.30.0.1     255.255.255.255 UGH 0 0 0 eth0
            192.168.222.1  0.0.0.0        255.255.255.255 UH  0 0 0 ppp0
            0.0.0.0        192.168.222.1  0.0.0.0         UG  0 0 0 ppp0
    
    
  4. 现在,启动客户端:

    [root@client]# openvpn --config example7-9-client.conf
    
    

    连接会启动,但几秒钟后会停止,日志文件中会出现一条警告信息:

    ... OpenVPN ROUTE: omitted no-op route:   
            192.168.222.1/255.255.255.255 -> 192.168.222.1
    
    
  5. 再次检查系统路由:

            [client]$ netstat -rn
            172.30.0.19    172.30.0.1     255.255.255.255 UGH 0 0 0 eth0
            192.168.222.1  0.0.0.0        255.255.255.255 UH  0 0 0 ppp0
            192.16.186.192 0.0.0.0        255.255.255.192 U   0 0 0 eth0
            10.200.0.0  0.0.0.0        255.255.248.0   U   0 0 0 tun0
            10.198.0.0     10.200.0.1  255.255.0.0     UG  0 0 0 tun0
            0.0.0.0        10.200.0.1  0.0.0.0         UG  0 0 0 tun0
    
    

默认网关现在是 VPN 隧道,但通往网关的原始路由已不复存在。

客户端的所有连接已停止。更糟的是,当 OpenVPN 客户端被中止(通过在终端窗口按Ctrl + C)时,默认路由没有恢复,因为 OpenVPN 进程没有足够的权限来恢复它:

TCP/UDP: Closing socket 
/sbin/ip route del 10.198.0.0/16 
RTNETLINK answers: Operation not permitted 
ERROR: Linux route delete command failed: external program exited with error status: 2 
/sbin/ip route del 192.168.222.1/32 
RTNETLINK answers: Operation not permitted 
ERROR: Linux route delete command failed: external program exited with error status: 2 
/sbin/ip route del 0.0.0.0/0 
RTNETLINK answers: Operation not permitted 
ERROR: Linux route delete command failed: external program exited with error status: 2 
/sbin/ip route add 0.0.0.0/0 via 192.168.222.1 
RTNETLINK answers: Operation not permitted 
ERROR: Linux route add command failed: external program exited with error status: 2 
Closing TUN/TAP interface 

结果是客户端机器上的默认网关消失了。唯一的解决方法是重新加载网络适配器,以便恢复所有系统默认设置。

上述问题的解决方案是使用以下方法,就像在第二章的重定向默认网关一节中所做的那样:

push "redirect-gateway def1" 

它是如何工作的……

当 OpenVPN 客户端初始化时,它总是尝试通过现有的系统网关与 OpenVPN 服务器建立直接连接。在某些情况下,这种尝试会失败,主要是由于异常的网络配置。通常在默认网关是拨号连接或 PPPoE 连接时会看到这种问题,尤其是在某些 ADSL/VDSL 配置以及使用 GPRS/UMTS 连接时。

当 OpenVPN 客户端被指示将所有流量通过 VPN 隧道重定向时,它通常会直接通过链接将加密的 VPN 流量发送到 OpenVPN 服务器。你可以将加密的 VPN 流量视为隧道外部流量。然而,当这个直接路由缺失时,隧道外的流量也会被发送到隧道内,造成一个 VPN 永远无法恢复的隧道循环。

在本食谱中使用的示例中,使用客户端配置指令使情况变得更糟:

user nobody

这告诉 OpenVPN 进程在启动后丢弃所有权限。当客户端因为隧道未正常工作而被中止时,客户端无法恢复原始网关,系统将处于无法使用的状态:

[client]$ netstat -rn
194.171.96.27  192.16.186.254 255.255.255.255 UGH 0 0 0 eth0
192.168.222.1  0.0.0.0        255.255.255.255 UH  0 0 0 ppp0
192.16.186.192 0.0.0.0        255.255.255.192 U   0 0 0 eth0

只有添加一个新的默认网关,网络才能恢复。

适当的解决方法是使用:

push "redirect-gateway def1"

这不会覆盖现有的默认网关,但会添加两个额外的路由:

0.0.0.0   10.200.0.1 128.0.0.0  UGH 0 0 0 tun0 
128.0.0.0 10.200.0.1 128.0.0.0  UGH 0 0 0 tun0 

这两个路由各自覆盖了可用网络空间的一半。它们有效地替代了现有的默认路由,而不会覆盖它。

还有更多……

这个“自咬尾巴”问题在旧版本的 OpenVPN 中更为常见。在当前版本的 OpenVPN 中,默认网关的检测已得到大幅改进,现在这个问题很少发生。然而,了解当问题发生时发生了什么,仍然是有用的。

另见

  • 第二章的重定向默认网关一节,解释了如何通过 VPN 隧道正确重定向所有流量。

第八章:性能调优

在本章中,我们将讨论以下故障排除主题:

  • 使用ping优化性能

  • 使用iperf优化性能

  • 比较 IPv4 和 IPv6 速度

  • OpenSSL 密码算法速度

  • OpenVPN 在千兆网络中的应用

  • 压缩测试

  • 流量整形

  • 调整基于 UDP 的连接

  • 调整基于 TCP 的连接

  • 使用tcpdump分析性能

介绍

本章重点介绍如何从 OpenVPN 设置中获取最佳性能。有几个参数可以在服务器端和客户端进行调整,以获得最高的吞吐量和最低的延迟。然而,这些参数的最佳设置在很大程度上取决于网络布局。因此,本章中的配方将提供如何调整这些参数以及如何衡量性能增减的指南。这些指南随后可以应用于其他网络布局,以找到最佳性能。

使用 ping 优化性能

在本节中,我们将使用低级的ping命令来确定 OpenVPN 设置的最佳最大传输单元(MTU)大小。找到合适的 MTU 大小对性能有巨大影响,尤其是在使用卫星连接,或某些有线/ADSL 提供商时。尤其是,使用PPPoE(以太网点对点协议)协议的宽带连接通常具有非标准的 MTU 大小。在常规的局域网设置中,几乎不需要优化 MTU 大小,因为 OpenVPN 的默认设置接近最佳。

准备工作

确保客户端和服务器计算机通过网络连接。在本节中,服务器计算机运行的是 CentOS 6 Linux,客户端运行的是 Fedora 22 Linux,但也提供了 Windows 客户端的相关说明。

如何操作...

  1. 我们首先验证客户端是否能连接到服务器:

    [client]$ ping -c 2 <openvpn-server-ip>
    
    

    这将向服务器发送两个 ICMP ping 数据包,应该返回两个回复。如果没有返回,那么防火墙或iptables规则可能在阻止 ICMP 流量。在继续之前,确保服务器可以使用ping命令访问。

  2. 接下来,尝试从客户端向服务器发送一个大的 ping 数据包,并设置Don't Fragment(DF)位。奇怪的是,在 Linux 上,这是通过参数-M do来完成的。

    [client]$ ping -c 2 -M do -s 1600 <openvpn-server-ip>
    
    

    通常,这个命令不会成功:

    From 172.30.0.128 icmp_seq=1 Frag needed and DF set (mtu = 
            1500)
    
    

    从这个接口发送的数据包的最大大小为 1500 字节。从中需要减去以太网头部(通常是 28 字节),这意味着 ICMP 数据包的最大大小为 1472 字节:

    [client]$ ping -c 2 -M do -s 1472 <openvpn-server-ip>
    PING 172.30.0.128 (172.30.0.128) 1472(1500) bytes of data.
    1480 bytes from 172.30.0.128: icmp_seq=1 ttl=128 time=0.630 ms
    1480 bytes from 172.30.0.128: icmp_seq=2 ttl=128 time=0.398 ms
    
    
  3. 对于 Windows 客户端,ping命令的语法略有不同:

    [winclient]C:> ping -f -l 1600 <openvpn-server-ip>
    Packet needs to be fragmented but DF set.
    
    

    以及:

    [winclient]C:> ping -f -l 1472 <openvpn-server-ip>
    Pinging 172.30.0.1 with 1472 bytes of data:
    Reply from 172.30.0.1: bytes=1472 time<1ms TTL=64
    
    

    1472 字节的有效载荷大小实际上是基于以太网网络的常规大小,尽管本节是在有线连接上进行的。

对于 OpenVPN 的tun-mtu设置,一个好的初始值是最大有效载荷大小加上之前减去的 28 字节。然而,这并不意味着这是最优值,正如我们在后面的配方中所看到的那样。

它是如何工作的...

ping 命令使用的 ICMP 协议有一个选项,用于设置标志 Don't Fragment(DF)。启用该标志后,ICMP 数据包在到达目的地之前不能被拆分成多个片段。如果路由器需要将数据包拆分才能传输,它将被丢弃,并返回 ICMP 错误代码。这提供了一种非常简单的方法来确定可以传输到服务器的最大数据包大小,反之亦然。特别是在高延迟网络中,例如使用卫星链接时,限制数据包数量并最大化每个数据包的大小非常重要。

通过巧妙地使用 ping 命令,可以确定最大数据包大小。然后,可以利用这个大小进一步优化 OpenVPN 的性能。

还有更多内容...

在某些网络配置中,ICMP 流量会被过滤,从而使此方法失效。如果能够访问 OpenVPN 服务器,那么也可以通过隧道来找出最大负载大小。

启动 OpenVPN 服务器并添加额外的标志:

cipher none 
auth none 

对 OpenVPN 客户端执行相同操作。确保禁用压缩(或简单地不指定)并且没有使用 fragment 选项。这将启动一个明文隧道,我们可以通过该隧道发送各种大小的 ICMP 数据包。

Ping 远程端的 VPN IP 地址,例如:

[client]$ ping -c 2 -M do -s 1472 10.200.0.1

当 ICMP 数据包变得太大时,流量将被一个间歇性的路由器丢弃。降低 ICMP 数据包的大小,直到 ping 命令成功返回。从这个值,可以推导出 MTU 大小。

另见

  • 调整基于 UDP 的连接 配方,详细解释了如何调整基于 UDP 的配置性能

  • 调整基于 TCP 的连接 配方,深入讲解了 TCP 基于连接的调优细节,并解释了网络适配器的 MTU 设置的一些复杂性

使用 iperf 优化性能

本方法并不真正关于 OpenVPN,而是更多地讲解如何在 OpenVPN 配置中使用网络性能测量工具 iperfiperf 工具可以从 sourceforge.net/projects/iperf/ 下载,支持 Linux、Windows 和 macOS。

在本方法中,我们将在 OpenVPN 外部以及通过 VPN 隧道运行 iperf,然后解释性能差异。

准备就绪

我们使用以下网络布局:

准备就绪

使用第二章中的设置公共和私有密钥食谱,配置客户端和服务器证书。对于这个食谱,服务器使用的是 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端使用的是 Fedora 22 Linux 和 OpenVPN 2.3.11。保留来自服务器端路由食谱的配置文件basic-udp-server.conf,以及客户端配置文件basic-udp-client.conf,这些都来自第二章,客户端-服务器 IP 仅网络

如何操作...

  1. 启动服务器:

    [root@server]# openvpn --config basic-udp-server.conf
    
    
  2. 接下来,启动客户端:

            [root@client]# openvpn --config basic-udp-client.conf
            ...
            ... Initialization Sequence Completed
    
    
  3. 接下来,我们在服务器上启动 iperf

            [server]$ iperf -s
    
    
  4. 首先,我们测量隧道外的性能:

            [client]$ iperf -l 1M -n 8M -c <openvpn-server-ip>
            [  3]  0.0-15.2 sec  8 MBytes  4.1 Mbits/sec
    
    

    这实际上是测量发送到服务器的数据性能。本食谱中使用的有线网络的理论上传速限制为每秒 4 兆位(Mbps),我们在这次测试中达到了该速率。

  5. 接下来,我们测量隧道内的性能:

            [client]$ iperf -l 1M -n 8M -c 10.200.0.1
            [  3]  0.0-17.0 sec  8 MBytes  3.95 Mbits/sec
    
    

    在这种网络设置下,隧道外的流量和通过隧道发送的流量之间存在小幅性能差异。

  6. 在 802.11n 无线网络上进行第二次测试:

            [client]$ iperf -c <openvpn-server-ip>
            [  4]  0.0-10.8 sec  7.88 MBytes  6.10 Mbits/sec 
    
    

    对比:

            [client]$ iperf -c 10.200.0.1
            [  5]  0.0-11.3 sec  5.25 MBytes  3.91 Mbits/sec
    
    

    在这里,可以明显看到性能下降,表明 OpenVPN 配置并不理想。这个无线网络上噪声较多,导致优化变得困难。

它是如何工作的...

iperf 工具非常直观:它建立一个 TCP 连接(或者 UDP,如果需要)并测量它在该连接上发送或接收数据的速度。通常,流量只测试一个方向,虽然也可以使用 -r 标志触发双向测试。

还有更多...

网络性能的调优很大程度上依赖于网络延迟和可用带宽,详情请参考这里的内容。

客户端与服务器 iperf 结果

客户端和服务器的 iperf 进程报告在 iperf -c 会话结束后网络吞吐量的结果。实践表明,本食谱中使用的服务器报告的数字比客户端报告的数字更为准确。在编写此食谱时使用的有线网络,最大上传速度约为 4 Mbps。客户端有时会报告超过 4.4 Mbps 的速度,而服务器则报告更准确的 4.1 Mbps。

网络延迟

有线网络和无线网络在性能下降方面的主要差异之一,源于网络延迟。在有线网络中,延迟非常稳定,约为 11 毫秒。而在无线网络中,延迟在 2 毫秒到 90 毫秒之间波动。尤其是,延迟的这种波动会影响 iperf 性能测试结果,使得优化 OpenVPN 参数变得非常困难。

千兆网络

千兆网络上的性能测试显示,VPN 本身正在成为瓶颈。正常的 TCP 连接将显示 900 Mbps 的传输速率,而通过未调优的 OpenVPN 隧道的 TCP 连接最多也不会超过 320 Mbps。我们将在本章后面回来详细讨论这个问题。

另请参见

  • 《在千兆网络中使用 OpenVPN》,将详细解释如何调优 OpenVPN 以在高速网络上提升性能的方法。

比较 IPv4 和 IPv6 的速度

这个方案是前一个方案的延续,但我们将集中讨论 IPv4 流量和 IPv6 流量之间的性能差异。在这个方案中,我们将在 VPN 隧道中使用 IPv4 地址和 IPv6 地址运行iperf,之后将解释性能差异。

准备就绪

我们使用以下网络布局:

准备就绪

使用《设置公共和私有密钥》一章的设置客户端和服务器证书方案来设置客户端和服务器证书,客户端-服务器仅 IP 网络。对于本方案,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11。客户端运行的是 Fedora 22 Linux 和 OpenVPN 2.3.11。保留来自《添加 IPv6 支持》一章的配置文件example-2-4-server.conf客户端-服务器仅 IP 网络,以及客户端配置文件basic-udp-client.conf

如何操作...

  1. 启动服务器:

            [root@server]# openvpn --config example-2-4-server.conf
    
    
  2. 接下来,启动客户端:

            [root@client]# openvpn --config basic-udp-client.conf
            ...
            ... Initialization Sequence Completed
    
    
  3. 接下来,我们在服务器上启动iperf

            [server]$ iperf -s
    
    
  4. 首先,我们测量通过 IPv4 流量进行隧道时的性能:

            [client]$ iperf -l 1M -n 8M -c 10.200.0.1
            [  3]  0.0-17.0 sec  8 MBytes  3.95 Mbits/sec
    
    
  5. 接下来,我们测量使用 IPv6 数据包时的性能:

            [client]$ iperf -l 1M -n 8M -c  2001:db8:100::1
            [  3]  0.0-17.7 sec  8 MBytes  3.78 Mbits/sec
    
    

这显示了大约 5%的性能差异。这种差异在所有类型的网络上都能稳定测量到。

工作原理...

IPv6 地址比 IPv4 地址更长。所有数据包的源和目的地地址都存储在经过 OpenVPN 隧道的加密数据包中。因此,使用较大的寻址方案时,实际的“有效负载”所剩下的字节数就会减少。IPv6 数据包实际上可以比 IPv4 数据包携带少 20 字节的“有效负载”。这 20 字节占了 5%的性能差异。对此几乎无能为力。

还有更多...

调整网络性能取决于网络特性,以及所使用的调整工具,这里将详细概述。

客户端与服务器 iperf 结果

客户端和服务器的iperf进程在iperf -c会话结束后报告网络吞吐量。实践表明,本食谱中使用的服务器报告的数字比客户端报告的数字更准确。此外,通过使用固定数据大小而不是默认的 10 秒固定时间间隔来运行iperf,可以获得更准确的结果。我们通过iperf -l 1M -n 8M -c <IP 地址>指定了固定的块大小(1MB)和固定的总大小(8MB)。

这增加了准确性并改善了客户端和服务器端报告数字的一致性。

OpenSSL 加密算法速度

OpenVPN 使用 OpenSSL 执行所有的加密操作。这意味着,OpenVPN 客户端或服务器的性能取决于接收流量的解密速度以及发送流量的加密速度。对于一个与 OpenVPN 服务器连接的单个客户端,这通常不是问题,但当 OpenVPN 服务器同时连接数百个客户端时,加密性能变得非常重要。此外,在高速度网络链路(千兆位或更高)上运行 OpenVPN 时,加密性能也发挥着重要作用。

在本食谱中,我们将展示如何测量 OpenSSL 加密例程的性能,以及如何使用这些测量结果来提高 OpenVPN 服务器的性能。

准备工作

本食谱在多种计算机上进行:

  • 一台老旧的笔记本电脑,配备 2.5 GHz 的 Intel Core2 Duo T9300 处理器,运行 Fedora Linux 22 64 位

  • 一台较旧的服务器,配备 2.8 GHz 的 Intel Xeon X5660 处理器,并支持 AESNI 指令,运行 CentOS 6 64 位

  • 一台高端服务器,配备 2.6 GHz 的 Intel Xeon E5-2697A v4 处理器,并支持 AESNI 指令,运行 CentOS 6 64 位

本食谱也可以轻松在 MacOS 上执行。每台计算机都安装了 OpenVPN 2.3 及其附带的 OpenSSL 库。

如何操作...

在每个系统上,运行以下 OpenSSL 命令:

$ openssl speed -evp bf-cbc
$ openssl speed -evp aes-128-cbc
$ openssl speed -evp aes-256-cbc

第一个命令测试 OpenVPN 默认的 BlowFish 加密算法的速度。第二个和第三个命令测试 128 位和 256 位 AES 算法的性能,这些算法通常用于保护网站安全。所有命令在新高端服务器上运行了两次:一次启用了 AES-NI 指令集,另一次则关闭了 AES-NI 支持,使用命令$ OPENSSL_ia32=0 openssl speed -evp <cipher>

结果显示在下表中。表中的所有数字都是在加密一个数据块时每秒处理的字节数。数据块的大小列在表格的列中。

对于BlowFish加密算法,记录了以下结果:

类型256 字节1024 字节8192 字节
笔记本电脑95851.54k95426.22k95862.84k
旧服务器111466.67k111849.47k112162.13k
新服务器151329.96k152054.10k152428.54k
新服务器,无 AES-NI151128.49k151951.02k152048.98k

对于 AES128 加密算法,记录了以下结果:

类型256 字节1024 字节8192 字节
笔记本电脑85588.05k179870.91k183104.85k
旧服务器758884.44k762378.58k755960.49k
新服务器802229.85k806787.75k807682.05k
新服务器,无 AES-NI160414.98k361608.53k368836.61k

对于 AES256

类型256 字节1024 字节8192 字节
笔记本电脑60698.20k130553.15k132085.73k
旧服务器560398.93k562632.92k564687.49k
新服务器577053.35k578981.21k579532.12k
新服务器,无 AES-NI114444.29k266473.47k270030.17k

它是如何工作的...

openssl speed 命令的输出显示,加密和解密性能取决于加密密钥和使用的硬件。大多数 OpenVPN 数据包约为 1500 字节,因此 1024 字节这一列是最值得关注的列。

BlowFish 加密算法的结果非常有趣,如果考虑到处理器的速度:如果将 BlowFish 的性能除以处理器时钟速度,结果非常相似。这意味着 BlowFish 的性能完全受限于处理器的时钟速度。运行在更高时钟速度的旧处理器可能实际上会超过时钟速度略低的新版处理器。

对于 AES128AES256 加密算法,这个情况不再成立。现代的 i5/i7 和 Xeon 架构比旧的 Pentium 4 和 Athlon 架构要快得多。启用 AES-NI 扩展后,性能提升了 4 倍。如果设置一个必须支持大量客户端的 OpenVPN 服务器,那么这个加密算法是一个非常好的选择,前提是服务器的 CPU 支持这些扩展。

这个配方还提供了一个简单的测试,检查 AES-NI 指令是否可用,并且底层的 OpenSSL 库是否实际调用了这些指令。如果 opensslOPENSSL_ia32cap=0 openssl 之间的速度结果没有差异,则说明 AES-NI 指令没有用于加密或解密。

还有更多...

加密算法的选择对 OpenVPN 性能的影响在单个客户端的情况下是最小的。根据本食谱的测量,当在现代系统上通过 VPN 隧道以最高速度下载文件时,客户端 CPU 的负载低于 8%。然而,在较旧的桌面电脑上,选择加密算法变得很重要:当 BlowFish 算法切换为 AES256 算法时,上传速度从 760 kbps 降至 720 kbps。特别是在使用较旧的硬件或某些家庭路由器设备时,这可能很快成为瓶颈。例如,大多数能够运行 OpenVPN 的家庭无线路由器(如支持 DD-WRT 或 OpenWRT 的无线路由器)都拥有约 250 MHz 的处理器速度。如果此路由器还用作 OpenVPN 服务器,尤其是在多个客户端同时连接时,处理器速度很快就会成为瓶颈。

另见

  • 第六章中的 加密不匹配 配方,详细说明了如何排查客户端和服务器配置文件中的加密算法不匹配问题。

千兆网络中的 OpenVPN

随着高速网络的到来,对高速 VPN 的需求也增加了。OpenVPN 并非专门为高速设计,但通过现代硬件和正确的加密算法,使用 OpenVPN 2.4 可以达到接近千兆的速度。本食谱将展示如何实现这些速度。

准备就绪

我们使用以下网络布局:

准备就绪

本食谱中使用的客户端是一台搭载 Core i7-4810 处理器的笔记本,最高 TurboBoost 速度为 3.8 GHz。服务器是一台搭载 Xeon E5-2697A v4 处理器的服务器,最高 TurboBoost 速度为 3.6 GHz。将客户端和服务器都连接到千兆以太网交换机。使用第二章中的 设置公钥和私钥 配方设置客户端和服务器证书,客户端-服务器仅 IP 网络。在本食谱中,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.4.0,客户端运行 Fedora 22 Linux 和 OpenVPN 2.4.0。保留第二章中 服务器端路由 配方中的配置文件 basic-udp-server.conf,以及客户端配置文件 basic-udp-client.conf

如何操作...

  1. 启动服务器:

    [root@server]# openvpn --config basic-udp-server.conf
    
    
  2. 接下来,启动客户端:

            [root@client]# openvpn --config basic-udp-client.conf
            ...
            ... Initialization Sequence Completed
    
    
  3. 接下来,我们在服务器上启动iperf

            [server]$ iperf -s
    
    
  4. 首先,我们测量隧道外的性能:

            [client]$ iperf -c <openvpn-server-ip>
            [  3]  0.0-10.0 sec  11 GBytes  900 Mbits/sec
    
    

    对于千兆以太网网络,这接近理论极限。

  5. 接下来,我们在隧道内测量性能:

            [client]$ iperf -l 1M -n 8M -c 10.200.0.1
            [  4]  0.0-10.2 sec  292 MBytes  233 Mbits/sec 
    
    

    这是默认 OpenVPN 隧道的性能。

  6. 停止客户端和服务器上的 OpenVPN 进程。

  7. 现在,我们切换到 AES-256 加密算法,以利用两款处理器都支持的 AES-NI 指令:

            [server]# openvpn --config basic-udp-server.conf --cipher aes-
            256-cbc
    
    

    以及客户端:

            [client]# openvpn --config basic-udp-client.conf --cipher aes-
            256-cbc
            ...
            ... Initialization Sequence Completed
    
    
  8. 再次,我们在隧道内测量性能,测试两个方向:

            [client]$ iperf -l 1M -n 8M -c 10.200.0.1 -r
            [  4]  0.0-10.2 sec  762 MBytes  610 Mbits/sec 
            [  5]  0.0-10.2 sec  807 MBytes  646 Mbits/sec 
    
    

    这清楚地表明,AES-NI 指令确实有所不同。

  9. 再次停止客户端和服务器的 OpenVPN 进程。

  10. 现在,我们切换到 AES-256-GCM,这是 OpenVPN 2.4 支持的新加密算法,相较于 AES-256 加密算法和 SHA2 HMAC 函数,它更为高效:

            [server]# openvpn --config basic-udp-server.conf --cipher aes-
            256-gcm
    
    

    以及客户端:

            [client]# openvpn --config basic-udp-client.conf --cipher aes-
            256-gcm
            ...
            ... Initialization Sequence Completed   
    
  11. 再次,我们在隧道内测量性能,测试两个方向:

            [client]$ iperf -l 1M -n 8M -c 10.200.0.1 -r
            [  4]  0.0-10.2 sec  1.07 GBytes  859 Mbits/sec 
            [  5]  0.0-10.2 sec  1.08 GBytes  865 Mbits/sec 
    
    

最后的性能数据实际上非常接近通过千兆以太网 OpenVPN 隧道能够实现的最大速度。

它是如何工作的...

当使用具有高时钟频率并支持 AES-NI 指令的处理器时,OpenVPN 和操作系统能够跟上以千兆以太网速度进出的大量数据包。

新的 AES-256-GCM 加密算法在这里尤其有帮助,因为加密和身份验证(HMAC)在一个步骤中完成。这大大提高了性能,部分原因是计算时间更短,部分原因是这种加密算法每个数据包的加密开销更小,为实际的“有效载荷”留下了更多字节。

还有更多...

在千兆以太网中调整网络性能在很大程度上取决于所使用的硬件和操作系统。

明文隧道

另一个有趣的测试是关闭所有加密和身份验证(--cipher none --auth none),然后再次运行iperf测试。在本配方中使用的硬件上,得到了以下结果:

[  4]  0.0-10.2 sec  1.09 GBytes  874 Mbits/sec  
[  5]  0.0-10.2 sec  1.10 GBytes  879 Mbits/sec  

这些数字甚至更接近实际的线路速度,主要是因为没有加密开销,为“有效载荷”留出了最佳空间。

Windows 性能

iperf 工具也可以在 Windows 上使用,因此上述方法也可以通过 Windows 客户端和/或服务器完成。与 Linux 客户端或服务器相比,结果会有所不同。我们可以通过使用[WinClient]> iperf -w 128K -c <openvpn-server-ip>实现类似的“原始”以太网速度。

然而,通过 OpenVPN 隧道的性能,无论是否加密,都远低于 200 Mbps,即使使用的是最快的处理器。这很可能是由于 Windows TAP 驱动程序的设计问题。此问题目前正在调查中。

压缩测试

如果正确编译,OpenVPN 内置支持 LZO 压缩。所有 Windows 二进制文件默认都支持 LZO 压缩。在本配方中,我们将展示在传输易于压缩的数据(如网页)和不可压缩数据(如照片或二进制文件)时,使用 LZO 压缩的性能。

准备工作

我们使用以下网络布局:

准备工作

使用第二章中的设置公钥和私钥食谱设置客户端和服务器证书,客户端-服务器 IP 仅网络。在这个食谱中,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11。第一个客户端运行的是 Fedora 22 Linux 和 OpenVPN 2.3.11。保留第二章中服务器端路由食谱中的basic-udp-server.conf配置文件,和客户端配置文件basic-udp-client.conf。该食谱还在第二个客户端运行 Windows 7 64 位和 OpenVPN 2.3.11 时进行了重复。保留第二章中服务器端路由食谱中的basic-udp-server.conf配置文件,和来自使用 ifconfig-pool 块的客户端配置文件basic-udp-client.ovpn

如何操作...

  1. 将以下行添加到basic-udp-server.conf文件中:

    comp-lzo
    
    

    将其保存为example8-6-server.conf

  2. 启动服务器:

    [root@server]# openvpn --config example8-6-server.conf
    
    
  3. 同样地,对于客户端,添加一行到basic-udp-client.conf文件中:

    comp-lzo
    
    

    将其保存为example8-6-client.conf

  4. 启动客户端:

    [root@client]# openvpn --config example8-6-client.conf
    
    
  5. 接下来,我们在服务器上启动iperf

    [server]$ iperf -s
    
    
  6. 首先,我们测量传输隧道外数据时的性能:

    [client]$ iperf -c <openvpn-server-ip>
    
    

    这导致在 802.11n 无线网络上大约达到 50 Mbps 的吞吐量。

  7. 接下来是不可压缩数据:

    [client]$ dd if=/dev/urandom bs=1024k count=60 of=random
    [client]$ iperf -c 10.200.0.1 -F random
    [  4]  0.0-10.0 sec  35.0 MBytes  29.3 Mbits/sec
    
    

    在第一步中,我们创建一个包含随机数据的 60MB 文件。然后,我们测量传输该文件时iperf的性能。

  8. 最后,压缩数据(一个填充零的文件):

    [client]$ dd if=/dev/zeroes bs=1024k count=60 of=zeroes
    [client]$ iperf -c 10.200.0.1 -F zeroes
    [  5]  0.0- 5.9 sec  58.6 MBytes  83.3 Mbits/sec
    
    

    显示了当传输可压缩数据(如文本文件和网页)时,VPN 隧道的性能。

  9. 相同的测量可以使用 Windows PC 进行。将以下行添加到basic-udp-client.ovpn文件中:

    comp-lzo
    
    

    将其保存为example8-6.ovpn

  10. 启动客户端。

iperf 测量结果略有不同:

  • 隧道外:50 Mbps

  • 不可压缩数据:16 Mbps

  • 可压缩数据:22 Mbps

显然,OpenVPN 的配置需要优化,但这不在本食谱的范围内。结果显示,对于 Windows 和 Linux 客户端,当通过隧道传输的可压缩数据时,性能有显著提升。

它是如何工作的...

启用压缩时,所有通过隧道发送的数据包会在加密和传输到另一端之前进行压缩。压缩使用 LZO 库,该库集成在 OpenVPN 中。此压缩是即时完成的,这意味着压缩比率没有事先压缩数据时那么好。但当传输文本页面时,性能提升依然显著。

还有更多内容...

当使用以下配置指令时,默认启用自适应压缩:

comp-lzo

当 OpenVPN 检测到某个数据不可压缩时,它会在不先压缩的情况下将数据发送到远程 VPN 端点。通过在两端指定以下设置,每个数据包都始终会被压缩:

comp-lzo yes

根据传输的数据类型,性能会稍微有所提高。

流量整形

在本例中,我们将使用流量整形来限制 OpenVPN 客户端的上传速度。这可以用来限制客户端到服务器之间的带宽,或客户端到客户端之间的带宽。请注意,OpenVPN 流量整形不能用于限制 OpenVPN 客户端的下载速度。限制下载速度最好使用外部流量控制工具,例如 Linux 上的 tc 工具,它是 LARTC 软件包的一部分。

准备中

我们使用以下网络布局:

准备中

使用 第二章 中的设置公钥和私钥食谱设置客户端和服务器证书,客户端-服务器 IP 仅网络。在本食谱中,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端运行 Windows 7 64 位和 OpenVPN 2.3.11。保留来自 第二章,客户端-服务器 IP 仅网络basic-udp-server.conf 配置文件,以及来自使用 ifconfig-pool 块食谱的客户端配置文件 basic-udp-client.ovpn

如何实现...

  1. 将以下行追加到basic-udp-server.conf文件中:

    push "shaper 100000"
    
    

    这将限制 VPN 客户端的上传速度为每秒 100,000 字节(100 kbps)。将其保存为 example8-7-server.conf

  2. 启动服务器:

    [root@server]# openvpn --config example8-7-server.conf
    
    
  3. 启动客户端:如何实现...

  4. 接下来,我们在服务器上启动iperf

    [server]$ iperf -s
    
    
  5. 当我们在 Windows PC 上运行iperf时,性能接近 100 KB/s:如何实现...

  6. 穿越隧道传输的 PNG 字节数(包括加密开销)实际上非常接近每秒 100,000 字节。

它是如何工作的...

当 OpenVPN 客户端连接到服务器时,服务器会推送一个选项,通过 VPN 隧道限制外发流量为 100 KB/s。每当流量通过隧道发送时,OpenVPN 客户端会将外发流量限制为最大 100 KB/s。下载速度不受此限制,注意,以下指令不能在 OpenVPN 服务器本身使用:

shaper 100000 

要限制离开服务器的流量,应该使用更高级的流量控制工具,例如 Linux 上的 tc

调整基于 UDP 的连接

在本教程中,我们重点介绍了一些优化基于 UDP 的 VPN 隧道的基本技巧。这些技巧需要谨慎使用,因为没有一种万无一失的方法可以优化 OpenVPN 性能。实际的性能提升因每个网络配置的不同而有所差异。因此,本教程仅展示了一些可以用于优化的配置指令。

准备工作

我们使用以下网络布局:

准备工作

使用第二章中的设置公共和私有密钥教程设置客户端和服务器证书,客户端-服务器 IP-only 网络。对于本教程,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端运行的是 Fedora 22 Linux 和 OpenVPN 2.3.11。保留第二章中服务器端路由教程的basic-udp-server.conf配置文件,以及客户端配置文件basic-udp-client.conf

如何操作...

  1. 将以下行添加到basic-udp-server.conf文件中:

    fragment 1400
    
    
  2. 将其保存为example8-8-server.conf

  3. 启动服务器:

    [root@server]# openvpn --config example8-8-server.conf
    
    
  4. 同样地,对于客户端,在basic-udp-client.conf文件中添加一行:

    fragment 1400
    
    
  5. 将其保存为example8-8-client.conf

  6. 启动客户端:

    [root@client]# openvpn --config example9-6-client.conf
    
    
  7. 接下来,我们在服务器上启动iperf

    [server]$ iperf -s
    
    
  8. 首先,我们测量隧道外的性能:

    [client]$ iperf -c <openvpn-server-ip>
    [  4]  0.0-16.7 sec  8.00 MBytes  4.03 Mbits/sec
    
    

    这实际上是测量发送到服务器的数据性能。本教程中使用的电缆网络理论上传输速率为 4 Mbps。请注意,这个结果几乎与使用 iperf 优化性能教程中的结果相同。

  9. 接下来,我们在隧道内测量性能:

    [client]$ iperf -c 10.200.0.1
    [  4]  0.0-18.3 sec  8.00 MBytes  3.66 Mbits/sec
    
    

    由于 OpenVPN 隧道的存在,性能会有所下降,但结果几乎与教程使用 iperf 优化性能中的结果相同。

    分片确实会影响ping的往返时间。

  10. 对于不同的fragment选项值,从客户端到服务器运行ping命令:

    [client]$ ping -c 10 10.200.0.1
    
    

结果列在以下表格中:

分片大小Ping 结果
默认 (1500)9.4 +/- 1.0 毫秒
14009.9 +/- 1.5 毫秒
40019.2 +/- 8 毫秒

因此,向服务器配置中添加fragment选项对于此网络配置而言不是一个可行的选项。然而,在其他网络配置中,这可能会提高性能。

它是如何工作的...

OpenVPN 配置指令fragment 1400会导致所有大于 1400 字节的加密数据包被分段。如果网络延迟足够低,则对性能没有明显影响,如iperf的结果所示。通过降低分段大小,数据包被分成更多的包。这会导致较大数据包的往返时间增加。如果网络延迟已经很高,这将导致更多的延迟问题。因此,fragment选项和相关的mssfix选项必须小心使用。

还有更多...

fragment指令通常与mssfix指令一起使用:

mssfix [maximum-segment-size] 

该指令通知通过隧道运行的 TCP 会话,要求它们限制发送数据包的大小,以便在 OpenVPN 将它们封装后,OpenVPN 发送到其对等端的 UDP 数据包大小不会超过最大分段大小。它也被 OpenVPN 内部使用,用于设置出站数据包的最大分段大小。如果未指定最大分段大小,则使用fragment指令中的值。

理想情况下,mssfixfragment指令一起使用,其中mssfix会尽量避免 TCP 需要数据包分段,如果仍然有大数据包通过(例如,来自非 TCP 协议的包),则fragment指令会对其进行内部分段。

另见

  • 本章中的下一个食谱解释了如何以非常相似的方式调整基于 TCP 的连接。

调整基于 TCP 的连接

在这个食谱中,我们专注于一些优化基于 TCP 的 VPN 隧道的基本技术。在基于 TCP 的 VPN 设置中,VPN 端点之间的连接是一个常规的 TCP 连接。这有其优点和缺点。主要优点是,通常设置 TCP 连接比设置 UDP 连接更容易,主要是由于防火墙的限制。通过 TCP 隧道传输 TCP 流量的主要缺点是,当网络连接较差时,可能会出现严重的性能损失。这种性能损失是由tcp-over-tcp症状引起的。TCP 协议保证数据包的顺序传送,因此如果某个数据包在传输过程中丢失,它将被重新发送。一旦新数据包被接收,数据包的顺序就会恢复。在此之前,所有在丢失数据包之后的数据包都将被暂停。通过 TCP 连接隧道传输 TCP 流量的问题在于,两个层都希望保证数据包按顺序传送。这可能会导致大量重传,从而带来巨大的性能损失。

然而,当正确调整时,通过 TCP 连接的 OpenVPN 隧道可以达到与通过 UDP 连接的 OpenVPN 隧道相同的性能。在本食谱中,我们将展示一些调整此类基于 TCP 的 OpenVPN 连接的技术。

准备开始

我们使用以下网络布局:

准备开始

使用第二章中的 设置公钥和私钥 配方设置客户端和服务器证书,客户端-服务器仅 IP 网络。对于这个配方,服务器计算机运行的是 CentOS 6 Linux 和 OpenVPN 2.3.11,而客户端运行的是 Windows 7 64 位和 OpenVPN 2.3.11。保留 第二章中的 服务器端路由 配方中的配置文件 basic-udp-server.conf,以及 使用 ifconfig-pool 块 配方中的客户端配置文件 basic-udp-client.ovpn

如何操作...

  1. 创建服务器配置文件:

            proto tcp 
            port 1194 
            dev tun 
            server 10.200.0.0 255.255.255.0 
    
            ca       /etc/openvpn/cookbook/ca.crt 
            cert     /etc/openvpn/cookbook/server.crt 
            key      /etc/openvpn/cookbook/server.key 
            dh       /etc/openvpn/cookbook/dh2048.pem 
            tls-auth /etc/openvpn/cookbook/ta.key 0 
    
            persist-key 
            persist-tun 
            keepalive 10 60 
    
            topology subnet 
    
            user  nobody 
            group nobody 
    
            daemon 
            log-append /var/log/openvpn.log 
    
            tcp-nodelay 
    
    
  2. 将其保存为 example8-9-server.conf

  3. 启动服务器:

    [root@server]# openvpn --config example8-9-server.conf
    
    
  4. 接下来,创建客户端配置文件:

            client 
            proto tcp 
            remote openvpnserver.example.com 
            port 1194 
    
            dev tun 
            nobind 
    
            remote-cert-tls server 
            ca       "c:/program files/openvpn/config/ca.crt" 
            cert     "c:/program files/openvpn/config/client2.crt" 
            key      "c:/program files/openvpn/config/client2.key" 
            tls-auth "c:/program files/openvpn/config/ta.key" 1 
    
    
  5. 将其保存为 example8-9.ovpn

  6. 启动客户端:如何操作...

  7. 接下来,在服务器上启动 iperf

    [server]$ iperf -s
    
    
  8. 然后,测量隧道的性能:

    [WinClient]> iperf -c 10.200.0.1 -w 128k
    
    

在这个特定网络上,测试了以下设置:

协议结果
UDP147 Mbits/sec
TCP115 Mbits/sec
带有 tcp-nodelay 的 TCP146 Mbits/sec

如图所示,使用 TCP 运行 OpenVPN 的性能几乎与使用 UDP 运行 OpenVPN 时的性能相同,前提是使用了 --tcp-nodelay 指令。

它是如何工作的...

当 OpenVPN 使用 TCP 作为底层协议时,所有数据包都通过常规的 TCP 连接传输。默认情况下,TCP 连接会使用 Nagle 算法,在该算法中,较小的数据包会被延迟并收集,直到它们被发送出去。对于 OpenVPN 隧道来说,在大多数情况下,这会对性能产生不利影响,因此禁用 Nagle 算法是有意义的。通过添加 --tcp-nodelay 指令,我们禁用了 Nagle 算法,并且性能得到了显著提升。

还有更多...

可调的两个重要参数是:

  • --tcp-nodelay 指令

  • 通过 --tun-mtu--link-mtu 指令调整 TUN/TAP-Win32 适配器的 MTU 大小

在 Linux 上,TUN(或 TAP)适配器的 MTU 大小可以动态调整,但在 Windows 上,这并不容易。必须配置 OpenVPN 以匹配服务器上指定的 MTU 大小。然而,在使用新的 MTU 大小之前,必须调整 TAP 适配器的 MTU。自 Windows Vista 起,现在也可以使用 netsh 命令动态调整 MTU:

  • 首先,找到正确的子接口编号:

    [winclient]C:> netsh interface ipv4 show subinterfaces
    
    
  • 接下来,要更改子接口的 MTU 大小,请使用:

    [winclient]C:> netsh interface ipv4 set subinterface "1" 
            mtu=1400
    
    

请注意,这些命令必须以提升的权限运行。

如果 Windows TAP-Win32 适配器的 MTU 设置大于 OpenVPN 配置的 MTU 大小,OpenVPN 日志文件中可能会出现以下消息:

... read from TUN/TAP  [State=AT?c Err=[c:\src\21\tap-win32\tapdrvr.c/2447] #O=4 Tx=[29510,0] Rx=[15309,0] IrpQ=[0,1,16] PktQ=[0,22,64] InjQ=[0,1,16]]: More data is available.  (code=234) 

对于这个特定的网络,所有对 MTU 大小的更改(包括适当的 Windows 重启)对性能没有产生积极的影响。

使用 tcpdump 分析性能

在本食谱中,我们将分析使用tcpdump工具的 OpenVPN 配置性能。也可以使用 Wireshark 工具,它适用于 Linux、Windows 和 Mac OS X。本食谱虽然没有介绍任何新的 OpenVPN 功能,但展示了如何进行这样的分析,依然很有帮助。

准备就绪

我们使用以下网络布局:

准备就绪

使用设置公钥和私钥食谱来设置客户端和服务器证书,见第二章,客户端-服务器仅限 IP 网络。在此食谱中,服务器计算机运行 CentOS 6 Linux 和 OpenVPN 2.3.11,客户端运行 Fedora 22 Linux 和 OpenVPN 2.3.11。请保留example8-8-server.conf配置文件,来自第二章的Tuning UDP-based connections食谱,以及来自同一食谱的客户端配置example8-8-client.conf

操作步骤...

  1. 启动服务器:

            [root@server]# openvpn --config example8-8-server.conf
    
    
  2. 接下来,启动客户端:

            [root@client]# openvpn --config example8-8-client.conf
    
    
  3. 在服务器上,运行tcpdump来监控网络接口上的传入数据包(而不是隧道接口本身):

            [root@server]# tcpdump -nnl -i eth0 udp port 1194
    
    

    这指示tcpdump在本地网络接口上监听所有 UDP 流量,端口为1194,这是 OpenVPN 的默认端口。

  4. 从客户端 ping 服务器的 VPN IP 地址,使用两种不同的大小:

            [client]$ ping -c 2 -s 1300 10.200.0.1
            [client]$ ping -c 2 -s 1400 10.200.0.1
    
    

以下数据包出现在tcpdump屏幕上:

操作步骤...

第一个 ICMP 数据包未分段发送,因为它们小于 1400 字节。第二组加密的 ICMP 数据包大于分段大小(1400),因此被分成两部分。

工作原理...

OpenVPN 配置指令fragment 1400会导致所有大于 1400 字节的加密数据包被分段。在观察加密流量时,可以通过 ping OpenVPN 服务器来验证这一点。需要注意的是,需要分段的数据包会均匀分段:所有数据包的大小相同。

同时,请注意,以下命令会导致加密数据包大于 1400 字节:

[client]$ ping -c 2 -s 1400 10.200.0.1

用于安全隧道的加密会给传输的数据包增加额外的开销。这是使用 VPN 隧道(不仅仅是 OpenVPN)相比非加密流量时性能下降的根本原因之一。在大多数网络中,这个开销并不会被注意到,但它始终存在。

另见

  • 本章中的Tuning UDP-based connections食谱解释了如何使用fragment指令。