PPTP Client

The PPTP Client can be used to create a VPN tunnel between the client system and a Microsoft (or other) VPN server that uses the PPTP protocol. This will allow you to route traffic through the VPN tunnel to the VPN server from the client system.

Before you proceed any further, if you have an OS with a kernel version below 2.6.15 or a pppd below version 2.4.2, see the notes at http://pptpclient.sourceforge.net/#tryit for more information on what to do. Alternately, the latest versions of commonly-available operating systems (e.g. CentOS 5.5 or Ubuntu 10.04) have kernels that are fine so you might want to consider an upgrade as your first step.

Begin by downloading the latest PPTP Client source from the downloads page at:

     http://sourceforge.net/projects/pptpclient/files/

Note that you'll have to look in the "All Files" section under the portion of the tree that contains the version you want to use. At the bottom of the tree you should see the straight source tar file which will look something like this:

     pptp-1.7.2.tar.gz

Once you've downloaded the package, build and install it like this:

     tar -xvzf pptp-1.7.2.tar.gz
     cd pptp-1.7.2
     make
     su
     make install

After you've installed the pptp package and before proceeding any further, you should check that the iproute package is installed on your system, since pptp uses the ip command, which is included in the iproute package. You can simply look for /bin/ip or /sbin/ip to verify that the iproute package is installed. If it isn't, install it like this:

     su
     yum install iproute

Or, if you are using a Debian-based system, such as Ubuntu, install it like this:

     sudo apt-get install iproute

Next, add a symlink for /bin/ip that points to /sbin/ip, if there isn't one already or if ip isn't already installed in /bin (i.e. on Debian-based systems):

     su
     ln -s /sbin/ip /bin/ip

This is required because the pptp code includes a hard-coded reference to /bin/ip in the module routing.c. On Debian-based systems, ip is actually installed in /bin. Then, a symlink from /sbin/ip is thoughtfully provided for those of us who know that ip is really supposed to be in /sbin. However on Redhat-based systems, such as CentOS, ip is installed in /sbin, where it is supposed to be but the symlink is not thoughtfully provided from /bin. So, pptp does not work properly and you will see the message "sh: /bin/ip: No such file or directory" when you start up the pptp tunnel via pppd. The alternative to adding the symlink is to fix the hard-coded reference to /bin/ip in routing.c and rebuild/reinstall pptp.

The install step will copy an "options.pptp" file into /etc/ppp. You should edit this file and check to make sure that the following options are present (the usually are):

     lock
     noauth
     nobsdcomp
     nodeflate

You may need to comment out some or all of these options, depending on which protocol the server is using to authenticate the username/password used to set up the tunnel:

     refuse-pap
     refuse-eap
     refuse-chap
     refuse-mschap

Unless all of the above protocols are commented out, pptp will only accept MSCHAP-V2.

If you wish to use MPPE encryption, which you probably will (note that MPPE requires the use of MSCHAP-V2 during authentication), be aware that there have been multiple versions of PPP with encryption support deployed. You should be able to figure out which one (CentOS uses ppp_mppe.ko, for example) by running this command as root:

     find /lib/modules -name ppp_mppe\*

If the find turns up one or more modules named "ppp_mppe.ko" you should include this option in your "options.pptp" file:

     # Require MPPE 128-bit encryption
     require-mppe-128

If the find turns up one or more modules named "ppp_mppe_mppc.ko" you should include this option in your "options.pptp" file:

     # Require MPPE 128-bit encryption
     mppe required,stateless

If it doesn't exist, create a /etc/ppp/chap-secrets file. It should only have permissions for root, since it holds usernames and passwords. The file should look like this:

     -rw------- 1 root root 256 Aug 23  2008 /etc/ppp/chap-secrets

Add a line to this file with the username and password that is used to login to the VPN server. If the username requires a domain name the line should look like this:

     vpndomain\\vpnuser PPTP secretpassword *

If the username does not require a domain name the line should look like this:

     vpnuser PPTP secretpassword *

In both cases, above, substitute your actual VPN authentication domain (if used), VPN username and the actual secret password for the three placeholders shown.

Note that, if the passwords contain any special characters, quote them in double quotes (as described in "man pppd").

Create a file for the tunnel in the /etc/ppp/peers subdirectory, for example as shown for the "mytunnel" connection here:

/etc/ppp/peers/mytunnel:

     pty "/usr/sbin/pptp vpnserver --nolaunchpppd"
     name vpndomain\\vpnuser
     remotename PPTP
     require-mppe-128
     file /etc/ppp/options.pptp
     ipparam mytunnel

The "vpnserver" parameter should be replaced with the actual server name or IP address of the VPN server.

On the "name" line, the VPN authentication domain (if used) and the VPN user should look exactly as they are entered in the chap-secrets file. Pick one of:

     vpndomain\\vpnuser

or

     vpnuser

If you won't be using MPPE encryption, omit the line that reads:

     require-mppe-128

Finally, the "mytunnel" parameter should be replaced by the actual name of the tunnel used to name this tunnel in the /etc/ppp/peers subdirectory.

And, as with the chap-secrets file, this file should only have permissions for root, since it holds information about the VPN tunnel. The file should look like this:

     -rw------- 1 root root 57 Oct 25 23:11 mytunnel

At this point, you should have everything set up that is necessary to establish a VPN tunnel with your remote VPN server. You can test it with the following command:

     /usr/sbin/pppd call mytunnel mtu 1435 mru 1435 debug dump logfd 2 nodetach

The "mytunnel" parameter should be replaced by the actual name of the tunnel that you used in the /etc/ppp/peers subdirectory. The debug output will show the tunnel coming up, the logon being negotiated, etc. If anything goes wrong, the tunnel will be terminated and you'll see an error message. Otherwise, the tunnel will stay up, waiting for you to make the next move. Terminate the tunnel by pressing <Ctrl-C>.

If you're happy with simply starting and stopping the tunnel whenever you need it, you can copy the "pon" and "poff" scripts that are supplied with PPP:

     su
     cp /usr/share/doc/ppp-2.4.4/scripts/pon /etc/ppp
     cp /usr/share/doc/ppp-2.4.4/scripts/poff /etc/ppp
     chmod ugo+x /etc/ppp/pon /etc/ppp/poff

Don't forget to give them execute permissions. Then, you can start/stop the VPN tunnel like this:

     /etc/ppp/pon mytunnel mtu 1435 mru 1435
     /etc/ppp/poff mytunnel

Once the VPN tunnel is set up, routing packets through the VPN tunnel can be as simple as setting up the default gateway:

     su
     /sbin/route add default gw 195.12.34.56

In this example, we're assuming that, when the VPN tunnel came up, the actual IP address of the tunnel was "195.12.34.56". You can check that the route to the VPN tunnel is correctly set up like this:

     /sbin/route

Note that, if you do happen to check your routing tables, you may see a route to 169.254.0.0. Huh? Where'd that come from? Well, its a new feature brought to you by those ever-thinking, clever guys in distroland. Somebody thought it would be real cool to add a default route to this bogus address so there would always be something in the routing table, even if you forget to configure things properly (it probably has something to do with that gem of an application, Network Manager or maybe even Windoze). Nice! Thanks for helping me out. All this does is confuse the issue. If you want to get rid of it, you can add a parameter to the network file in sysconfig.

/etc/sysconfig/network:

     NOZEROCONF=yes

This will get rid of the 169.254.0.0 route and make sure that all of your packets only go where you say they should.

Incidentally, if you don't want to add a permanent route (by adding a line to your rc.local script, for example) or want to add the route by hand every time you bring up the VPN tunnel, you can automate the process of adding/removing such a route by modifying the ip-up.local and ip-down.local scripts.

These two scripts are called every time pppd either brings up or shuts down a ppp connection. You can simply add a few lines at the start of each one to check the ppp connection name and, if it is greater than 1, call a special VPN up/down script. This will allow you to have a couple of regular PPP connections and then start up a VPN tunnel on a special PPP connection (which may even route the tunnel through one of the regular PPP connections). To start the tunnel, you would use:

     su
     /usr/sbin/pppd call mytunnel unit 2

The modifications to the two scripts (whose full description is provided elsewhere in these notes), are as follows:

/etc/ppp/ip-up.local:

     #!/bin/sh
     #
     # This shell script is called by pppd whenever it brings up a PPP connection
     # to the remote host.  Its purpose is to do any work that is required to
     # complete the PPP connection such as adding firewall rules specific to the
     # connection or routes to remote host.
     #
     # Since pppd is also used to bring VPN tunnels up and down via pptp, as well
     # as for regular ppp connections, the first thing this script does is check
     # to see if the connection is a VPN tunnel.  By convention, we will always
     # start VPN tunnels so that they use an interface name of ppp2 or greater.
     # This is typically done with a command like this:
     #
     #      /usr/sbin/pppd call my-tunnel unit 2
     #
     # If this script sees such a ppp connection, it will invoke vpn-updown to
     # take care of starting up the VPN tunnel.  Once vpn-updown completes,
     # the script exits.
     #
     # Otherwise, for regular ppp connections this script's purpose is to add
     # into the router's routing tables a default routing to the gateway
     # machine at the other end of the PPP link.  This will cause all
     # non-specifically routed packets to be passed to the gateway at the other
     # end of the PPP link for forwarding to the Internet.
     #
     # This script also registers the regular ppp connection's device via the
     # firewall rules script to change the rules so that they use the correct
     # active device when the link is up.
     #
     # The parameters that pppd passes to this script are (see pppd(8)):
     #
     #      <iface> <ttydev> <speed> <local-ip> <remote-ip> <ipparam>
     #
     #
     # First, since pppd can start VPN tunnels via pptp, we'll check to see if
     # this is a VPN tunnel and act accordingly, if it is.
     #
     if [ "$1" = ppp2 ] ; then
         /etc/ppp/vpn-updown $1 $4 $5
         exit 0
     fi
          .
          .
          .

/etc/ppp/ip-down.local:

     Note that, for many PPP configurations, the ip-down.local script is not
     necessary.  If that's the case, you simply use the code shown here, up to
     the elipsis, as the entire script.
     #!/bin/sh
     #
     # This shell script is called by pppd whenever it brings down a PPP
     # connection to a remote host.  Its purpose is to undo anything that was
     # done by ip-up.local when the connection was brought up.
     #
     # There is nothing to be done for regular PPP connections but pppd is also
     # used to bring VPN tunnels up and down via pptp.  By convention, we always
     # start VPN tunnels so that they use an interface name of ppp2 or greater.
     # If this script sees such a PPP connection, it will invoke vpn-updown to
     # take care of shutting down the VPN tunnel.
     #
     # The parameters that pppd passes to this script are (see pppd(8)):
     #
     #      <iface> <ttydev> <speed> <local-ip> <remote-ip> <ipparam>
     #
     #
     # If this is a VPN tunnel, invoke vpn-updown to handle it.
     #
     if [ $1 = 'ppp2' ] ; then
         /etc/ppp/vpn-updown $1
         exit 0
     fi
          .
          .
          .

All of the work of bringing up and shutting down the VPN connection is then done by a single script, which decides on what to do based on the parameters passed to it, either by ip-up.local or ip-down.local. You should put this script in the PPP directory, with all of the other PPP scripts.

/etc/ppp/vpn-updown:

     #!/bin/bash
     #
     # Script to handle the bringup and shutdown of a VPN tunnel started by
     # pppd/pptp.
     #
     # This script is called by the ppp ip-up.local script when it determines
     # that a VPN tunnel is being brought up.  It decides this when it sees an
     # interface with a number greater than one (e.g. ppp2, ppp3).  This number
     # is chosen when the VPN tunnel is started by passing a number to pppd via
     # the unit parameter, like this:
     #
     #      /usr/sbin/pppd call my-tunnel unit 2
     #
     # When ip-up.local calls this script, it must pass the VPN interface name
     # (e.g. ppp2), the interface's local IP address (e.g. 192.168.1.99), and
     # VPN tunnel's remote IP address (e.g. 93.182.128.101) as the first three
     # parameters.  In that case, this script will set up the default route
     # through the VPN tunnel.
     #
     # This script is also called by the ppp ip-down.local script when it
     # determines that a VPN tunnel is being shut down.  As with ip-up.local,
     # ip-down.local decides this when it sees an interface with a number
     # greater than one (e.g. ppp2, ppp3).
     #
     # When ip-down.local calls this script, it must pass the VPN interface
     # name (e.g. ppp2) as the only parameter.  The fact that the interface's
     # local IP address is not passed indicates to this script that it should
     # shut down the VPN tunnel, rather than start it up.  In that case, this
     # script will remove the default route through the VPN tunnel.
     #
     #
     # Pick up the VPN interface that was created by pppd/pptp.
     #
     VPN_INTERFACE=$1
     VPN_INTERFACE_IP=$2
     VPN_REMOTE_IP=$3
     #
     # If the VPN tunnel is being brought up, set everything up.
     #
     if [ x"$VPN_INTERFACE_IP" != x ] ; then
         /sbin/route add default gw $VPN_REMOTE_IP
     #
     # Otherwise, shut everything down.
     #
     else
         /sbin/route del default
     fi

Starting and stopping your PPTP VPN tunnel using commands from the command line and adding a default route via the above script is fairly rudimentary. You can consult the section "Routing Traffic Through a VPN Connection" for notes on how to selectively route only the traffic that you want through your VPN connection, instead of all the traffic. And, the "Starting/Stopping a PPTP Tunnel and Routing Traffic Through It" section shows the scripts that you'll need to bring up the tunnel and shut it down, as well as to alter the routing table to route traffic through the tunnel and modify the firewall rules to ensure that bringing up the tunnel doesn't open up the system to any security breaches.