Troubleshooting OpenVPN
Troubleshooting OpenVPN
Routing gives an insight into troubleshooting routing problems when setting up a VPN using
OpenVPN. You will learn how to detect, diagnose, and repair common routing issues.
In this article by Jan Just Keijser, author of OpenVPN 2 Cookbook, we will cover:
• Cipher mismatches
• TUN versus TAP mismatches
• Compression mismatches
• Key mismatches
• Troubleshooting MTU and tun-mtu issues
• Troubleshooting network connectivity
• Troubleshooting client-config-dir issues
• How to read the OpenVPN log files
OpenVPN 2 Cookbook
100 simple and incredibly effective recipes for harnessing the power of the
OpenVPN 2 network
• Set of recipes covering the whole range of tasks for working with
OpenVPN
• The quickest way to solve your OpenVPN problems!
• Set up, configure, troubleshoot and tune OpenVPN
• Uncover advanced features of OpenVPN and even some undocumented
options
Introduction
The topic of this article is troubleshooting OpenVPN. This article will focus on troubleshooting
OpenVPN misconfigurations.
The recipes in this article will therefore deal first with breaking the things. We will then provide the
tools on how to find and solve the configuration errors. Some of the configuration directives used in
this article have not been demonstrated before, so even if you are not interested in breaking things this
article will still be insightful.
Cipher mismatches
In this recipe, we will change the cryptographic ciphers that OpenVPN uses. Initially, we will change
the cipher only on the client side, which will cause the initialization of the VPN connection to fail. The
primary purpose of this recipe is to show the error messages that appear, not to explore the different
types of ciphers that OpenVPN supports.
Getting ready
Install OpenVPN 2.0 or higher on two computers. Make sure the computers are connected over a
network. Set up the client and server certificates. For this recipe, the server computer was running
CentOS 5 Linux and OpenVPN 2.1.1. The client was running Fedora 13 Linux and OpenVPN 2.1.1.
Keep the server configuration file basic-udp-server.conf (download code, ch:2) and the client
configuration file basic-udp-client.conf at hand.
How to do it...
1. Start the server using the configuration file basic-udp-server.conf:
[root@server]# openvpn --config basic-udp-server.conf
2. Next, create the client configuration file by appending a line to the basic-udp-client.conf file:
cipher CAST5-CBC
Save it as example7-1-client.conf.
3. Start the client, after which the following message will appear in the client log:
[root@client]# openvpn --config example7-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 192.168.200.2/24 broadcast
192.168.200.255
... Initialization Sequence Completed
... Authenticate/Decrypt packet error: cipher final failed
The connection will not be successfully established, but it will also not be disconnected immediately.
How it works...
During the connection phase, the client and the server negotiate several parameters needed to secure the
connection. One of the most important parameters in this phase is the encryption cipher, which is used
to encrypt and decrypt all the messages. If the client and server are using different ciphers, then they
are simply not capable of talking to each other.
By adding the following configuration directive to the server configuration file, the client and the
server can communicate again:
cipher CAST5-CBC
There's more...
OpenVPN supports quite a few ciphers, although support for some of the ciphers is still experimental.
To view the list of supported ciphers, type:
$ openvpn --show-ciphers
This will list all ciphers with both variables and fixed cipher length. The ciphers with variable cipher
length are very well supported by OpenVPN, the others can sometimes lead to unpredictable results.
Getting ready
Install OpenVPN 2.0 or higher on two computers. Make sure the computers are connected over a
network. Set up the client and server certificates (Download code-ch:2 here). For this recipe, the server
computer was running CentOS 5 Linux and OpenVPN 2.1.1. The client was running Fedora 13 Linux
and OpenVPN 2.1.1. Keep the server configuration file basic-udp-server.conf (Download code-ch:2
here) and the client configuration file basic-udp-client.confat hand.
How to do it...
1. Start the server using the configuration file basic-udp-server.conf:
[root@server]# openvpn --config basic-udp-server.conf
At this point, you can try pinging the server, but it will respond with an error:
[client]$ ping 192.168.200.1
PING 192.168.200.1 (192.168.200.1) 56(84) bytes of data.
From 192.168.200.2 icmp_seq=2 Destination Host Unreachable
From 192.168.200.2 icmp_seq=3 Destination Host Unreachable
From 192.168.200.2 icmp_seq=4 Destination Host Unreachable
How it works...
A TUN-style interface offers a point-to-point connection over which only TCP/IP traffic can be
tunneled. A TAP-style interface offers the equivalent of an Ethernet interface that includes extra
headers. This allows a user to tunnel other types of traffic over the interface. When the client and the
server are misconfigured, the expected packet size is different:
... WARNING: 'tun-mtu' is used inconsistently, local='tun-mtu 1532',
remote='tun-mtu 1500'
This shows that each packet that is sent through a TAP-style interface is 32 bytes larger than the
packets sent through a TUN-style interface.
By correcting the client configuration, this problem is resolved.
Compression mismatches
OpenVPN supports on-the-fly compression of the traffic that is sent over the VPN tunnel. This can
improve the performance over a slow network line, but it does add a little overhead. When transferring
uncompressible data (such as ZIP files), the performance actually decreases slightly.
If the compression is enabled on the server but not on the client, then the VPN connection will fail.
Getting ready
Install OpenVPN 2.0 or higher on two computers. Make sure the computers are connected over a
network. Set up the client and server certificates. For this recipe, the server computer was running
CentOS 5 Linux and OpenVPN 2.1.1. The client was running Fedora 13 Linux and OpenVPN 2.1.1.
Keep the server configuration file basic-udp-server.conf (Download code-ch:2 here) and the client
configuration file basic-udp-client.confat hand..
How to do it...
1. Append a line to the server configuration file basic-udp-server.conf:
comp-lzo
Save it as example7-3-server.conf.
2. Start the server:
[root@server]# openvpn --config example7-3-server.conf
The connection will initiate but when data is sent over the VPN connection, the following
messages will appear:
Initialization Sequence Completed
... write to TUN/TAP : Invalid argument (code=22)
... write to TUN/TAP : Invalid argument (code=22)
How it works...
During the connection phase, no compression is used to transfer information between the client and the
server. One of the parameters that is negotiated is the use of compression for the actual VPN payload. If
there is a configuration mismatch between the client and the server, then both the sides will get
confused by the traffic that the other side is sending.
With a network fully comprising OpenVPN 2.1 clients and an OpenVPN 2.1 server, this can be fixed
for all the clients by just adding another line:
push "comp-lzo"
There's more...
OpenVPN 2.0 did not have the ability to push compression directives to the clients. This means that an
OpenVPN 2.0 server does not understand this directive, nor do OpenVPN 2.0 clients. So, if an
OpenVPN 2.1 server pushes out this directive to an OpenVPN 2.0 client, the connection will fail.
Key mismatches
OpenVPN offers extra protection for its TLS control channel in the form of HMAC keys. These keys
are exactly the same as the static "secret" keys used in point-to-point style networks. For multi-client
style networks, this extra protection can be enabled using the tls-auth directive . If there is a mismatch
between the client and the server related to this tls-auth key , then the VPN connection will fail to get
initialized.
Getting ready
Install OpenVPN 2.0 or higher on two computers. Make sure the computers are connected over a
network. Set up the client and server certificates using the first recipe. For this recipe, the server
computer was running CentOS 5 Linux and OpenVPN 2.1.1. The client was running Fedora 13 Linux
and OpenVPN 2.1.1. Keep the server configuration file basic-udp-server.conf (Download code-ch:2
here) and the client configuration file basic-udp-client.conf at hand.
How to do it...
1. Start the server using the configuration file basic-udp-server.conf:
[root@server]# openvpn --config basic-udp-server.conf
Note the lack of the second parameter for tls-auth. Save it as example7-4-client.conf file.
3. Start the client:
[root@client]# openvpn --config example7-4-client.conf
The client log will show no errors, but the connection will not be established either. In the
server log we'll find:
... Initialization Sequence Completed
... Authenticate/Decrypt packet error: packet HMAC authentication
failed
... TLS Error: incoming packet authentication failed from client-
ip:54454
This shows that the client openvpnclient1 is connecting using the wrong tls-auth parameter and the
connection is refused.
How it works...
At the very first phase of the connection initialization, the client and the server verify each other's
HMAC keys. If an HMAC key is not configured correctly, then the initialization is aborted and the
connection will fail to establish. As the OpenVPN server is not able to determine whether the client is
simply misconfigured or whether a malicious client is trying to overload the server, the connection is
simply dropped. This causes the client to keep listening for the traffic from the server, until it
eventually times out.
In this recipe, the misconfiguration consisted of the missing parameter 1 behind:
tls-auth /etc/openvpn/cookbook/ta.key
The second parameter to the tls-auth directive is the direction of the key. Normally, the following
convention is used:
• 0: from server to client
• 1: from client to server
This parameter causes OpenVPN to derive its HMAC keys from a different part of the ta.key file. If the
client and server disagree on which parts the HMAC keys are derived from, the connection cannot be
established. Similarly, when the client and server are deriving the HMAC keys from different ta.key
files, the connection can also not be established.
OpenVPN 2 Cookbook
100 simple and incredibly effective recipes for harnessing the power of the
OpenVPN 2 network
Published:
ReadFebruary 2011 this book
more about
eBook Price: £18.99
(For more resources on this subject, see here.)
Book Price: £30.99
See more
Troubleshooting MTU and tun-mtu issues
One of the more advanced features of OpenVPN is the ability to tune the network parameters of both
the TUN (or TAP) adapter and the parameters of the encrypted link itself. This is a frequent cause of
configuration mistakes, leading to low performance or even the inability to successfully transfer data
across the VPN tunnel. This recipe will show what happens if there is an MTU (Maximum Transfer
Unit) mismatch between the client and the server and how this mismatch can cause the VPN tunnel to
fail only under certain circumstances.
Getting ready
Install OpenVPN 2.0 or higher on two computers. Make sure the computers are connected over a
network. Set up the client and server certificates. For this recipe, the server computer was running
CentOS 5 Linux and OpenVPN 2.1.1. The client was running Fedora 13 Linux and OpenVPN 2.1.1.
Keep the server configuration file basic-udp-server.conf and the client configuration file basic-udp-
client.conf at hand (Download code-ch:2 here).
How to do it...
1. Start the server using the configuration file basic-udp-server.conf:
[root@server]# openvpn --config basic-udp-server.conf
2. Next, create the client configuration file by appending a line to the basic-udp-client.conf file:
tun-mtu 1400
Save it as example7-5-client.conf.
3. Start the client and look at the client log:
[root@client]# openvpn --config example7-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 192.168.200.2/24 broadcast
192.168.200.255
... Initialization Sequence Completed
There are a few warnings when the tunnel comes up, but the connection is initialized.
4. It is possible to send a traffic over the link, which we can verify using the ping command:
[client]$ ping -c 2 192.168.200.1
PING 192.168.200.1 (192.168.200.1) 56(84) bytes of data.
64 bytes from 192.168.200.1: icmp_seq=1 ttl=64 time=30.6 ms
64 bytes from 192.168.200.1: icmp_seq=2 ttl=64 time=30.7 ms
The same thing will happen if the client tries to download a large file.
How it works...
The MTU or Maximum Transfer Unit determines how large packets can be that are sent over the tunnel
without breaking up (fragmenting) the packet into multiple pieces. If the client and the server disagree
on this MTU size, then the server will send packets to the client that are simply too large. This causes
an HMAC failure (if tls-auth is used, as in this recipe) or the part of the packet that is too large is
thrown away.
There's more...
On the Windows platform, it is not easy to change the MTU setting for the Tap-Win32 adapter that
OpenVPN uses. The directive tun-mtu can be specified but the Windows version of OpenVPN cannot
alter the actual MTU setting, as Windows did not support this until Windows Vista. OpenVPN,
however, does not yet have the capability of altering the MTU size on Windows Vista or Windows 7.
Getting Ready
Install OpenVPN 2.0 or higher on two computers. Make sure the computers are connected over a
network. Set up the client and server certificates. For this recipe, the server computer was running
CentOS 5 Linux and OpenVPN 2.1.1. The client was running Fedora 13 Linux and OpenVPN 2.1.1.
Keep the server configuration file basic-udp-server.conf and the client configuration file basic-udp-
client.conf at hand (Download code-ch:2 here).
How to do it...
1. Start the server using the configuration file basic-udp-server.conf
[root@server]# openvpn --config basic-udp-server.conf
The client will try to connect the server using the UDP protocol. After a while, a timeout will occur
because no traffic is getting through and the client will restart:
... 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
Abort the client and stop the server.
How it works...
When OpenVPN is configured to use the default UDP protocol, the client will wait for an answer from
the server for 60 seconds. If no answer was received, the connection is restarted. As we are explicitly
blocking UDP traffic, the timeout occurs and the client is never able to connect.
The amount of time the client waits for the connection to start is controlled using the directive:
hand-window N
Here, N is the number of seconds to wait for the initial handshake to complete. The default value is 60
seconds.
Of course, the connection can be repaired by removing the firewall rule.
There's more...
One of the major differences between the UDP protocol and the TCP protocol is the way connections
are established: every TCP connection is started using a TCP handshake by both the client and the
server. If the handshake fails, then the connection is not established. There is no need to wait for traffic
coming back from the server, as the connection itself is dropped:
... Attempting to establish TCP connection with openvpnserver:1194
[nonblock]
... TCP: connect to openvpnserver:1194 failed, will try again in
5 seconds:
Connection refused
Getting ready
Install OpenVPN 2.0 or higher on two computers. Make sure the computers are connected over a
network. Set up the client and server certificates. For this recipe, the server computer was running
CentOS 5 Linux and OpenVPN 2.1.1. The client was running Fedora 13 Linux and OpenVPN 2.1.1.
Keep the server configuration file basic-udp-server.conf and the client configuration file basic-udp-
client.confat hand (Download code-ch:2 here). For this recipe, the server computer was running
CentOS 5 Linux and OpenVPN 2.1.1. The client was running Fedora 13 Linux and OpenVPN 2.1.1.
Keep the server configuration file basic-udp-server.conf (Download code-ch:2 here).
How to do it...
1. Append the following lines to the configuration file basic-udp-server.conf:
client-config-dir /etc/openvpn/cookbook/clients
ccd-exclusive
Save it as example7-7-server.conf.
2. Make sure the directory /etc/openvpn/cookbook/clients is accessible only to root:
[root@server]# chown root /etc/openvpn/cookbook/clients
[root@server]# chmod 700 /etc/openvpn/cookbook/clients
The server log file is a bit confusing: first, it mentions that there was a problem reading the CCD file
openvpnclient1 but then it states that the client is connected:
... client-ip:42692 TLS Auth Error: --client-config-dir
authentication failed for common name 'openvpnclient1' file=
'/etc/openvpn/cookbook/clients/openvpnclient1'
... client-ip:42692 [openvpnclient1] Peer Connection Initiated with
client-ip:42692
How it works...
The following directives are used by the OpenVPN server to look in the directory
/etc/openvpn/cookbook/clients for a CCD file with the name (CN) of the client certificate:
client-config-dir /etc/openvpn/cookbook/clients
ccd-exclusive
The purpose of the second directive, ccd-exclusive, is to only allow clients for which a CCD file is
present. If a CCD file for a client is not present, the client will be denied the access.
The name of the client certificate is listed in the server log:
But, it can also be retrieved using:
... client-ip:42692 TLS Auth Error: --client-config-dir
authentication failed for common name 'openvpnclient1'
Look for the first part starting with /CN= and convert all spaces to underscores.
The OpenVPN server process is running as user nobody and because we have set very restrictive
permissions on the directory /etc/openvpn/cookbook/clients, this user is not capable of reading any files
in that directory. When the client with certificate openvpnclient1> connects, the OpenVPN server is not
capable of reading the CCD file (even though it might be there). Because of the ccd-exclusive directive,
the client is then denied access.
There's more...
In this section, we will explain how to increase the logging verbosity and what some of the most
common client-config-dir mistakes are.
If this message is not present in the server log, then it is safe to assume that the CCD file has not been
read.
• This might work in some cases, but you have to be very careful when starting the server or
when combining this with directives such as --chroot or --cd>. Especially when the --chroot
directive is used, all paths, including the absolute path, will be relative to the chroot path.
• The CCD file itself must be correctly named, without any extension. This typically tends to
confuse the Windows users. Look in the server log to see what the OpenVPN server thinks; the
/CN= name is of the client certificate. Also, be aware that OpenVPN rewrites some characters
of the /CN= name, such as spaces. For the full list of characters that will be remapped, see the
manual page, section String Types and Remapping.
• The CCD file and the full path to it must be readable to the user under which the OpenVPN
server process is running (usually nobody).
OpenVPN 2 Cookbook
100 simple and incredibly effective recipes for harnessing the power of the
OpenVPN 2 network
Published:
ReadFebruary 2011 this book
more about
eBook Price: £18.99
(For more resources on this subject, see here.)
Book Price: £30.99
See more
How to read the OpenVPN log files
Troubleshooting an OpenVPN setup often comes down to reading and interpreting the OpenVPN log
file correctly. In this recipe, no new features of OpenVPN will be introduced, but a detailed walk-
through of an OpenVPN log file will be given. The setup from the recipe Troubleshooting MTU and
tun-mtu issues earlier in this article will be used as a starting point.
Getting ready
Use the same setup as in the recipe Troubleshooting MTU and tun-mtu issues earlier in this article. For
this recipe, the server computer was running CentOS 5 Linux and OpenVPN 2.1.1. The client was
running Fedora 13 Linux and OpenVPN 2.1.1. Keep the configuration file, basic-udp-server.conf, at
hand. For the client, keep the configuration file, example7-5-client.conf, from the recipe
Troubleshooting MTU and tun-mtu issues at hand.
How to do it...
1. Start the server using the configuration file basic-udp-server.conf:
[root@server]# openvpn --config basic-udp-server.conf
2. Next, start the client with an increased verbosity setting and without timestamps in the log file:
[root@client]# openvpn --config example7-5-client.conf \
--verb 7 --suppress-timestamps
The connection will initiate, but it will not be possible to send large packets.
3. Trigger an error by typing:
[client]$ ping -c 1 192.168.200.1
[client]$ ping -c 1 -s 1450 192.168.200.1
4. Abort the client. The log file will have become large quite quickly.
5. Open the log file using a text editor and browse through it. An explanation of the general
structure of the log file is given in the next section
How it works...
The first part of the log file contains the configuration as specified in the configuration file and from
the command-line parameters. This is the section starting with:
Current Parameter Settings:
config = 'example7-5-client.conf'
This section is about 250 lines long depending on the configuration and it contains what OpenVPN
thinks is the configuration. Check this section carefully to make sure that you agree.
The next interesting section is:
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 ...
This part shows that a tls-auth key is read and used and that the two separate HMAC keys are derived.
The keys are actually printed in the log file, so you can reference them with the output from the server
log file. The server incoming key should be the same as the client outgoing key and vice versa. The
misconfiguration from the recipe Key mismatches earlier in this article would have appeared here.
Right after this section is the warning that is the root cause of the misconfiguration from the recipe
Troubleshooting MTU and tun-mtu issues earlier in this article:
WARNING: normally if you use --mssfix and/or --fragment, you should
also set --tun-mtu 1500 (currently it is 1400)
Log file messages starting with WARNING should always be given special attention to. In some cases,
they can be ignored but in this case it was the root cause of the VPN connection not working properly.
After this warning come a whole range of messages of the following form:
DPv4 WRITE [50] to server-ip:1194: P_ACK_V1 kid=0 pid=[ #74 ] [ 37 ]
UDPv4 READ [108] from server-ip:1194: P_CONTROL_V1 kid=0 pid=[ #73 ] [
] pid=38 DATA len=66
These messages are all part of the initial handshake between the client and the server to exchange
configuration information, encryption keys, and other information for setting up the VPN connection.
Right after this is another hint about the misconfiguration:
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'
We skip forward over a lot of TLS_prf messages to come to the processing of the configuration
directives pushed by the server:
PUSH: Received control message: 'PUSH_REPLY,route-gateway
192.168.200.1,topology subnet,ping 10,ping-restart 60,ifconfig
192.168.200.2 255.255.255.0'
This is another important line to check for, as it shows what the server has actually pushed to the client.
Verify that this actually matches what you thought the server should push.
After this the local TUN adapter is opened and initialized and the first packets can begin to fiow.
The first ping command worked fine, as we can see from this part:
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]
The TUN READ is the ping command being read from the TUN interface, followed by a write over the
encrypted channel to the remote server. Notice the difference in packet size: the packet sent over the
encrypted tunnel is 125 bytes, which is 41 bytes larger than the original packet read from the TUN
interface. This exactly matches the difference between the link-mtu and tun-mtu as shown earlier in the
log file.
Next comes the section where the ping -s 1450 breaks down. A ping of 1450 bytes cannot be read in
one piece if the MTU of the interface is set to 1400, hence two TUN READS are necessary to capture
all data:
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
Notice that the data is actually sent as two separate packets to the server. This is perfectly normal
behaviour, as the packet needs to be fragmented. Calculation of the packet sizes versus the MTU sizes
breaks down in this case, as the second packet is not a complete IP packet.
The server receives the large ping command and sends an equally large reply. As the server has an
MTU setting of 1500, there is no need to fragment the data, so it arrives at the client as a single packet:
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
The client, however, is expecting a packet with a maximum size of 1400 bytes. It is not able to properly
decode the larger packet and write out the packet HMAC authentication failed message.
Finally, when we abort the client, we see an interrupted system call message (in this case, Ctrl-C was
used to abort the client, plus a range of clean-up message before the clientactually stops:
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 192.168.200.2/24
PID packet_id_free
SIGINT[hard,] received, process exiting
There's more..
On UNIX-based operating systems, it is also possible to send the OpenVPN log output via syslog. This
allows a system administrator to effectively manage a large set of computers using a single system
logging interface. To send log messages via syslog, replace the directive log-append with:
syslog [name]
Here, name is an optional parameter to specify the name of the OpenVPN instance in the syslog log
files. This is particularly useful if there are multiple instances of OpenVPN running on a single host
and they are all using syslog to log their output and error messages.
Summary
Routing gives an insight into troubleshooting routing problems when setting up a VPN using
OpenVPN. You learned how to detect, diagnose, and repair common routing issues.