Starting/Stopping a PPTP Tunnel and Routing Traffic Through It

In preceeding sections, we discussed how to bring up a PPTP VPN tunnel and how to use iproute2 to route traffic through the VPN tunnel. The Setting up and tearing down of the tunnel was handled by a rudimentary vpn-updown script that was invoked by the ip-up.local and ip-down.local scripts when the ppp daemon brought up or shut down the tunnel.

However, the setting up or tearing down of a VPN tunnel is actually a lot more complicated than was shown in the vpn-updown script. A more complete list of the steps involved follows.

Starting the VPN tunnel on the router itself is simple enough but if packets from other locally-connected systems are also to be routed through it, the router's kernel packet filters must be adjusted to masquerade the outbound packets so that they all appear to be coming from the tunneled client (i.e. the router). This is because the remote VPN server has no knowledge of the other locally-connected, routed systems, just the tunneled client.

However, if masquerading is used, confusion within the kernel code about the proper route being used for the masqueraded/tunneled reply packets causes the router to incorrectly apply reverse path filtering to all packets received on the tunnel interface and, hence, drop them as Martians. Consequently, reverse path filtering must be disabled on the tunnel interface, each time the tunnel is brought up, or response packets will just disappear. The VPN tunnel will appear to be set up correctly but no connections from the locally-connected systems will work (although traffic from/to the router itself will work fine).

In many instances, a VPN tunnel can be used to create a connection to a remote location that is wild and wooley (e.g. a connection to the Internet that is located in a faraway place, far from prying eyes). But, bringing up such a VPN tunnel, especially if reverse path filtering is disabled, can allow all sorts of nasty stuff into the local system. This makes it prudent to alter the firewall (iptables) rules, whenever the VPN tunnel is brought up, to do spoof checking and handle the inbound reception and forwarding of VPN packets.

Once all of the VPN tunnel's ducks are in a row, iptables can then be used to figure out which packets should be routed through the VPN tunnel and mark them accordingly, as noted in "Routing Traffic Through a VPN Connection". When that is done, iproute2 can be used to establish special routing tables that will route the marked packets through the VPN tunnel and route any replies back to the local systems. This means that the routing tables must set up and knocked down whenever the VPN tunnel is brought up or down.

Lastly, it would be great if the operation of the VPN tunnel could be automated so that it is started on the router whenever the router system is brought up and stopped whenever it is shut down. Thus, we need a startup script that ties all of these steps together.

To begin with, in preparation for running an PPTP VPN tunnel, if you haven't already done so, obtain the actual username and password along with the IP address of the PPTP server that you will be using to make the PPTP VPN tunnel connection and put them in the /etc/ppp/chap-secrets and /etc/ppp/peers/mytunnel files, as described in the "PPTP Client" section. You can test the tunnel connection manually before you proceed any furnther, if you wish. Its probably a good idea to keep things simple until the connection is known to be working OK.

The next preparatory step is to set up the "special" routing table that will be used by iproute2 to route packets through the VPN tunnel, as outlined in the "Routing Traffic Through a VPN Connection" section. The table itself will not be activated until the tunnel is actually brought up but it must be configured ahead of time.

The last preparatory step is to determine what criteria you will use to select the packets that will be routed through the VPN tunnel and decide how you will implement the selection. As we said in the section on "Routing Traffic Through a VPN Connection", we prefer to use iptables to mark the packets that are to be routed through the PPTP VPN tunnel using iptables and then direct the marked packets to the "special" routing table and thence through the VPN tunnel.

It is definitely possible to set the rules directly with the iptables command and then save/restore them with the iptables-save and iptables-restore commands. However, our preferred method of setting up iptables is the NARC firewall package so instead we add the rules to the /etc/narc/narc-custom.conf file and allow NARC to set them up when the firewall is brought up, presumably at system startup.

In the "PPTP Client" section, we already put forth a simple vpn-updown script that is invoked by either by ip-up.local or ip-down.local. Its purpose was solely to alter the routing of all packets through the VPN tunnel by setting up a route using iproute2. Here, we show a vpn-updown script which may be used to handle the starting and stopping of PPTP tunnels, including updating the firewall rules in a manner that is consistent with the NARC firewall, masquerading the outbound traffic, and establishing the routing of marked packets through the VPN tunnel.

/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.
     #
     # This script will add special rules to the iptables netfilter tables
     # that will assist with firewalling of traffic on the VPN connection.  In
     # addition, the regular firewall rules will still be in effect and, in
     # conjunction with the special rules, they will ensure that all inbound
     # VPN tunnel traffic is properly firewalled.
     #
     # This script will then add a rule to the routing tables that will send
     # all traffic marked as VPN traffic to a special routing table which will
     # be set up with a default gateway that points to the VPN tunnel.  This
     # will route all non-local traffic marked as VPN traffic out 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.
     #
     # During shutdown, this script undoes all of the work it did during
     # startup that added a rule and route, to the routing tables, to send
     # all traffic marked as VPN traffic out through the VPN tunnel.  It also
     # deletes all of the special rules from the iptables netfilter tables
     # that assist with firewalling of traffic on the VPN connection.
     #
     # Note that you must modify /etc/ppp/ip-up.local and /etc/ppp/ip-down.local
     # to call this script when either of these scripts determines that a VPN
     # tunnel is being brought up or shut down by pppd.
     #
     ##########################################################################
     #
     # We add rules to the netfilter tables in the same manner that NARC does
     # so we need to know where its config file is.
     #
     CONFIG="/etc/narc/narc.conf"
     #
     # Minimum acceptable NARC config file version.
     #
     MINCONF_VERSION=0.6.3
     #
     # Pick up the VPN interface that was created by pppd/pptp.
     #
     VPN_INTERFACE=$1
     VPN_INTERFACE_IP=$2
     VPN_REMOTE_IP=$3
     ##########################################################################
     #
     # Routine to allow us to bail out of failed rules, etc.
     #
     abortexit()
         {
         echo "Failed to set up iptables and/or iproute2."
         exit 1
         }
     ##########################################################################
     #
     # Load the NARC Configuration file.
     #
     if test -f $CONFIG ; then
         . $CONFIG
     else
         echo "Cannot find the NARC config file $CONFIG."
         exit 1
     fi
     #
     # Check for the iptables binary.
     #
     if ! test -f "$IPTABLES" ; then
         echo "The iptables binary $IPTABLES is missing."
         exit 1
     else
         $IPTABLES -V >/dev/null 2>&1 || BADBIN='yes'
         if [ "$BADBIN" == 'yes' ] ; then
             echo "The iptables binary $IPTABLES exists but it failed to run."
             echo "Try `which iptables`."
             exit 1
         fi
     fi
     #
     # Check if the config file is compatible with this script.
     #
     if [ `expr $CONF_VERSION \< $MINCONF_VERSION` == 1 ] ; then
         echo "The NARC config file $CONFIG is at an incompatibile version."
         echo "The minimum vesion is $MINCONF_VERSION."
         echo "The config file is at $CONF_VERSION.  Please upgrade it."
         exit 1
     fi
     #
     # Set the SPOOF_CHK target.
     #
     if [[ "$LOG_DROPS" == 'yes' && "$LOG_SPOOF" == 'yes' ]] ; then
         SPOOF_TARGET='CUST_LOG'
     else
         SPOOF_TARGET='DROP'
     fi
     #
     # If the VPN tunnel is being brought up, set everything up.
     #
     if [ x"$VPN_INTERFACE_IP" != x ] ; then
         #
         # Create the VPN_CHK chain.
         #
         # This chain does anti-spoofing checking for the reserved and private
         # IP addresses on the VPN tunnel.  It also checks for established
         # connections and lets their packets in.  All other packets are
         # dumped as invalid.
         #
         # This chain is hung off the INPUT chain for all inbound traffic on
         # the tunnel device.
         #
         $IPTABLES -N VPN_CHK || abortexit
         #
         # Check reserved networks.
         #
         if [ "$RESERVED_NETWORKS" != '' ] ; then
             echo -n "Enabling spoof checking on $VPN_INTERFACE for reserved \
                 network(s): "
             for network in $RESERVED_NETWORKS ; do
                 $IPTABLES -A VPN_CHK -s $network -i $VPN_INTERFACE \
                     -j $SPOOF_TARGET || abortexit
                 echo -n "$network "
             done
             echo "."
         fi
         #
         # Check private networks.
         #
         if [ "$PRIVATE_NETWORKS" != '' ] ; then
             echo -n "Enabling spoof checking on $VPN_INTERFACE for private \
                 network(s): "
             for network in $PRIVATE_NETWORKS ; do
                 IPVAL2=`echo $VPN_INTERFACE_IP | cut -d . -f 1`
                 NETVAL2=`echo $network | cut -d . -f 1`
              if [[ $IPVAL2 == 10 && $NETVAL2 == 10 ]] ; then
                  DONOTHING=1
              else
                  IPVAL=`echo $VPN_INTERFACE_IP | cut -d . -f 1,2`
                  NETVAL=`echo $network | cut -d . -f 1,2`
                  if [ $IPVAL != $NETVAL ] ; then
                      DONOTHING=0
                      $IPTABLES -A VPN_CHK -s $network -i $VPN_INTERFACE \
                         -j $SPOOF_TARGET || abortexit
                  else
                      DONOTHING=1
                  fi
              fi
              if [ "$DONOTHING" != 1 ] ; then
                  echo -n "$network "
              fi
          done
          echo "."
      fi
      #
      # Add the local IP address of the VPN tunnel too.
      #
      echo "Enabling spoof checking on $VPN_INTERFACE for tunnel IP: \
          $VPN_INTERFACE_IP."
      $IPTABLES -A VPN_CHK -s $VPN_INTERFACE_IP -i $VPN_INTERFACE \
          -j $SPOOF_TARGET || abortexit
      #
      # At this point, we can let responses to any packets that we sent out
      # back in through the tunnel.
      #
      $IPTABLES -A VPN_CHK -i $VPN_INTERFACE -m state \
          --state RELATED,ESTABLISHED -j ACCEPT || abortexit
      #
      # Anything that makes it this far is logged as failing the VPN check
      # and then dropped.  For the VPN tunnel, only established sessions are
      # allowed.
      #
      $IPTABLES -A VPN_CHK -i $VPN_INTERFACE -j LOG \
          --log-level $WARN_LOG_LEVEL --log-tcp-options --log-ip-options \
          --log-prefix "VPN_CHK " || abortexit
      $IPTABLES -A VPN_CHK -i $VPN_INTERFACE -j DROP || abortexit
      #
      # Hang the VPN tunnel spoof checking chain off the INPUT filter
      # chain.
      #
      # This chain also accepts any established packets, which lets them
      # in if they are OK.
      #
      echo "Hooking VPN rules into the INPUT and FORWARD filter chains."
      $IPTABLES -I INPUT -i $VPN_INTERFACE -j VPN_CHK || abortexit
      #
      # Now that we are checking to make sure that all incoming packets on
      # the VPN tunnel are legit, we can allow forwarding from the tunnel
      # to our internal network.  This turns on the tunnel.
      #
      $IPTABLES -I FORWARD -i $VPN_INTERFACE -j ACCEPT || abortexit
      #
      # Packets sent out on the VPN tunnel must be masqueraded so that the
      # recipient at the other end can send the answers back to this system.
      # It will then forward them to the original sender.
      #
      # Note that we acutally use SNAT, which allows us to specify the IP
      # address to masquerade to, and which doesn't monitor the state of
      # the connection.  Otherwise, SNAT works just like MASQUERADE (or
      # vice versa).
      #
      $IPTABLES -A POSTROUTING -t nat -o $VPN_INTERFACE \
          -j SNAT --to $VPN_INTERFACE_IP || abortexit
      #
      # For some reason, when packets are masqueraded and sent down the
      # tunnel, the router gets confused and doesn't add the proper route
      # to its forwarding table.  This means that, if reverse path filtering
      # is turned on, replies to any packets sent out the VPN tunnel will be
      # dropped as Martians.  To fix this problem, we turn off reverse path
      # filtering on the VPN tunnel.
      #
      # Note that other firewall rules that we add herein should take care
      # of any real Martians.
      #
      # Also note that there's no need to undo this step upon shutdown, since
      # the rp_filter file is deleted when the VPN tunnel is torn down.
      #
      echo 0 >/proc/sys/net/ipv4/conf/${VPN_INTERFACE}/rp_filter
      #
      # Use iproute2 to add a default route to the vpn.tunnel table to send
      # all packets bound for the WAN out through the VPN tunnel.  Then,
      # send all packets with the firewall (i.e. iptables) mark value of 1
      # to to this table.
      #
      echo "Routing all marked packets to the VPN tunnel."
      /sbin/ip route add default via $VPN_REMOTE_IP dev $VPN_INTERFACE \
          table vpn.tunnel
      /sbin/ip route flush cache
      /sbin/ip rule add fwmark 1 table vpn.tunnel

#
# Otherwise, shut everything down.
#
else

      #
      # Tell iproute2 not to do anything special with marked packets and
      # not to send WAN packets out through the VPN tunnel.
      #
      echo "Suspending routing of all marked packets to the VPN tunnel."
      /sbin/ip rule del fwmark 1 table vpn.tunnel
      /sbin/ip route del default table vpn.tunnel
      /sbin/ip route flush cache
      #
      # We need the IP address of the VPN interface so that we can delete the
      # masquerading rule from iptables.  However, the ip-down.local script
      # didn't pass it to us.  But, since the masquerade rule in the
      # POSTROUTING table is the only one in effect for the VPN interface, we
      # can simply look up the IP address bound to it.
      #
      VPN_INTERFACE_IP=`$IPTABLES -v -t nat -L POSTROUTING | grep $VPN_INTERFACE | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}'`
      #
      # Turn off masquerading for the VPN tunnel packets.
      #
      $IPTABLES -D POSTROUTING -t nat -o $VPN_INTERFACE \
          -j SNAT --to $VPN_INTERFACE_IP
      #
      # Disallow forwarding from the VPN tunnel.
      #
      echo "Unhooking VPN rules from the INPUT and FORWARD filter chains."
      $IPTABLES -D FORWARD -i $VPN_INTERFACE -j ACCEPT
      #
      # Unhook the VPN tunnel spoof checking chain from the INPUT chain.
      #
      $IPTABLES -D INPUT -i $VPN_INTERFACE -j VPN_CHK
      #
      # Delete all of the rules from the VPN tunnel spoof checking chain and
      # then get rid of the chain itself.
      #
      echo "Deleting all rules from VPN tunnel spoof checking chain."
      FOUNDRULE=1
      while [ "$FOUNDRULE" != 0 ] ; do
          $IPTABLES -D VPN_CHK 1 >/dev/null 2>&1 || FOUNDRULE=0
      done
      #
      # Get rid of the now-empty chain.
      #
      $IPTABLES -X VPN_CHK

fi
#
# That's all she wrote.
#
exit 0

Now that we have the VPN tunnel all set up and ready to go, we'll create a startup script that will start and stop the PPTP tunnel at system startup and shutdown. Or, if you'd rather that operation was not automatic, you can simply use this script to start/stop the PPTP tunnel manually. But, wait a second. We already created this script, back in the "Starting/Stopping an OpenVPN Tunnel and Routing Traffic Through It" section. We created a script that starts either an OpenVPN connection or a PPTP connection so here's a reprint of it, just in case you're too lazy to go look it up.

/etc/init.d/vpnconnect:

     #!/bin/sh
     #
     # vpnconnect    This script starts or stops an OpenVPN or PPTP connection to
     #               a faraway land, via the primary server in the cluster, over
     #               the Internet.  Its purpose is to tunnel packets from/to
     #               certain local IP addresses (or even just certain types of
     #               traffic) transparently to those addresses' related local
     #               systems.  This lets the local systems send/receive packets
     #               from the remote, VPN-connected world without even knowing
     #               that a VPN tunnel is involved.  Also, packets from more
     #               than one local system can be tunneled to the remote world
     #               over a single VPN tunnel.
     #
     #               Since the VPN tunnel must run over a pre-existing
     #               Internet connection, this script must start after the WAN
     #               connection is brought up (i.e. after either the adsl or
     #               wanconnect script has run).  And, since starting up the
     #               VPN connection also alters the routing tables and changes
     #               the filetering done by iptales, this script must obviously
     #               be run and after the iptables script.
     #
     #               Aditionally, we may want the VPN connection to be up and
     #               running before we start any of the application-type
     #               services (e.g. sendmail, smb), if those services expect to
     #               be able to talk over the VPN tunnel to systems at the
     #               remote location.  Thus, we must carefully choose the
     #               sequence in which the VPN connection is started/stopped.
     #
     #               Finally, all of the real work is done by iptables, which
     #               marks the actual packets bound for the VPN tunnel with
     #
     # chkconfig: 2345 51 59
     # description: Connects to a remote system, via an OpenVPN or PPTP tunnel, \
     #              over a preexisting Internet connection, on the the primary \
     #              server in the cluster.
     #
     # Revision History:
     # ewilde      2011Nov6   Initial coding.
     # ewilde      2011Dec4   Add PPTP support.
     #
     #
     # Define the type of VPN connection.
     #
     VPNCONN=OpenVPN
     #VPNCONN=PPTP
     #
     # Define paths to the programs used herein.
     #
     OPENVPN=/usr/local/sbin/openvpn
     PPTP=/usr/sbin/pppd
     #
     # If logging is desired, define the path to the logfile here.  Otherwise,
     # set this variable to empty.
     #
     #LOG_FILE=""
     LOG_FILE="/var/log/vpnconnect"
     #
     # Start/stop scripts used to configure/deconfigure the OpenVPN tunnel
     # after it is brought up or before it is shut down.
     #
     # Typically, these scripts can add or remove rules with iptables to
     # control the flow of packets to/from the tunnel, for example by marking
     # packets from one or more IP addresses for tunneling or alternately only
     # by marking certain types of traffic (e.g. all sendmail traffic).  The
     # added rules can also be used to firewall the VPN tunnel, in case the
     # other end of the tunnel leads to a non-secure location.
     #
     # These scripts are also frequently tasked with setting up special routing
     # tables that direct marked packets down the VPN tunnel for deliver to the
     # remote location.
     #
     # Note that, through clever coding, you may be able to use a single script
     # to handle both the tunnel up and tunnel down events.
     #
     # Also note that there is an equivalent script to the OpenVPN script or
     # scripts chosen here but it is not chosen by parameters within this script.
     # Rather the PPP local start/stop scripts ip-up.local and ip-down.local call
     # it directly and its name is hard-coded therein.  By convention, the name
     # of the script is /etc/ppp/vpn-updown.
     #
     TUNUP="/etc/openvpn/tun-updown"
     TUNDOWN="/etc/openvpn/tun-updown"
     #
     # OpenVPN configuration file for the remote VPN server or servers.  This
     # file lists all of the tunnels that are possible and contains the
     # parameters that give the connection password, certificates and keys,
     # along with the tunnel configuration information.
     #
     # Note that this script assumes that the config file name ends with ".conf"
     # and that the full path name to the file is given below.
     #
     #TUNCONFIG="/etc/openvpn/Hostizzle.conf"
     TUNCONFIG="/etc/openvpn/Cryptocloud.conf"
     #
     # Name of PPTP tunnel.  This name actually selects the tunnel configuration
     # file from the /etc/ppp/peers directory.
     #
     VPNNAME=ipredator-tunnel
     #
     # Load the function library if it exists.
     #
     if [ -f /etc/rc.d/init.d/functions ]; then
         . /etc/rc.d/init.d/functions
     fi
     #
     # Source networking configuration.
     #
     if [ -f /etc/sysconfig/network ]; then
         . /etc/sysconfig/network
     else
         NETWORKING="no"
     fi
     #
     # Source the clustering configuration.
     #
     if [ -f /etc/sysconfig/clustering ]; then
         . /etc/sysconfig/clustering
     else
         SERVERROLE=Standalone
     fi
     if [ x"$SERVERROLE" == x ]; then
         SERVERROLE=Standalone
     fi
     #
     # Check that networking is up.
     #
     if [ ${NETWORKING} = "no" ]; then
         exit 0
     fi
     #
     # If this isn't the primary server in the cluster or this isn't a standalone
     # server, we're outta here.
     #
     if [ x"$SERVERROLE" != xPrimary ] && [ x"$SERVERROLE" != xStandalone ]; then
         exit 0
     fi
     #
     # Get the name of the VPN connection from the config file.
     #
     TUNNAME="generic"
     if [ x"$VPNCONN" == xOpenVPN ]; then
         TUNNAME=`echo ${TUNCONFIG} | /bin/grep -e "\/[-_0-9A-Za-z]\+\.conf" -o | \
             /bin/grep -e "\/[-_0-9A-Za-z]\+" -o`
         TUNNAME=${TUNNAME:1}
         PIDFILE="/var/lock/subsys/vpnconnect.${TUNNAME}"
     fi
     if [ x"$VPNCONN" == xPPTP ]; then
         TUNNAME=${VPNNAME}
      if echo ${TUNNAME} | /bin/grep -e "-tunnel\$" ; then
          TUNNAME=${TUNNAME:0:${#TUNNAME}-7}
      fi
      PIDFILE="/var/run/ppp-${TUNNAME}.pid"

fi
#
# Routine to start up the VPN connection. #
start()

      {
      if [ ! -f /var/lock/subsys/vpnconnect.${TUNNAME} ]; then
          #
          # Bring up the VPN connection.  We redirect the output depending
          # on whether the user wants a logfile or not.
          #
          echo -n "Bringing up a VPN connection to $TUNNAME "
          if [ -z "$LOG_FILE" ]; then
              #
              # Bring up an OpenVPN connection without a log file.
              #
              if [ x"$VPNCONN" == xOpenVPN ]; then
                  ${OPENVPN} --route-noexec --script-security 2 \
                      --up ${TUNUP} --down ${TUNDOWN} --down-pre \
                      --config ${TUNCONFIG} >/dev/null 2>&1 &
              fi
              #
              # Bring up a PPTP connection without a log file.
              #
              if [ x"$VPNCONN" == xPPTP ]; then
                  ${PPTP} call mytunnel mtu 1435 mru 1435 \
                      linkname ${TUNNAME} >/dev/null 2>&1
              fi
          else
              #
              # Bring up an OpenVPN connection with a log file.
              #
              if [ x"$VPNCONN" == xOpenVPN ]; then
                  ${OPENVPN} --route-noexec --script-security 2 \
                      --up ${TUNUP} --down ${TUNDOWN} --down-pre \
                      --config ${TUNCONFIG} >>${LOG_FILE} 2>&1 &
              fi
              #
              # Bring up a PPTP connection with a log file.
              #
              if [ x"$VPNCONN" == xPPTP ]; then
                  ${PPTP} call mytunnel mtu 1435 mru 1435 \
                      linkname ${TUNNAME} logfile ${LOG_FILE} >/dev/null 2>&1
              fi
          fi
          #
          # If everything went OK, create a PID file.
          #
          if [ $? = 0 ]; then
              echo_success
              if [ x"$VPNCONN" == xOpenVPN ]; then
                  echo $! >${PIDFILE}
              fi
          else
              echo_failure
          fi
          echo ""
      fi
      }

#
# Routine to stop the VPN connection. #
stop()

      {
      #
      # If the VPN connection is up, shut it down.  Sending OpenVPN a SIGTERM
      # (signal 15) causes it to gracefully shut down.
      #
      if [ -f ${PIDFILE} ]; then
          echo -n "Shutting down the VPN connection to $TUNNAME "
          TUNPID=`cat ${PIDFILE}`
  #       kill -15 $TUNPID >/dev/null 2>&1
          killproc -p ${PIDFILE}
          #
          # If everything went OK, delete the PID file.
          #
          if [ $? = 0 ]; then
              rm -f ${PIDFILE} >/dev/null 2>&1
  #           echo_success
          #
          # Otherwise, if the PID doesn't exist, ditch the PID file.
          #
          else
              if ! checkpid $TUNPID ; then
                  rm -f ${PIDFILE} >/dev/null 2>&1
              fi
              echo_failure
          fi
          echo ""
      fi
      }

#
# Based on which operation we were asked to perform, have at it. #
case "$1" in

      #
      # Fire up the VPN connection.
      #
      start)
          start
          ;;
      #
      # Bye, bye VPN connection.
      #
      stop)
          stop
          ;;
      #
      # Refresh the VPN connection.
      #
      restart)
          echo "Restarting the VPN connection to $TUNNAME"
          stop
          start
          ;;
      #
      # Waaaaa 'sappenin'?
      #
      status)
          if [ -f ${PIDFILE} ]; then
              echo "Connected to $TUNNAME via VPN"
          else
              echo "No VPN connection to $TUNNAME"
          fi
          ;;
      #
      # Help text.
      #
      *)
          echo "Usage: vpnconnect {start|stop|restart|status}"
          exit 1

esac
#
# Heading home.
#
exit 0

Now that we've created the script, we need to give it execute permissions and make sure that it is started/stopped at the proper runlevels:

     su
     chown root:root /etc/init.d/vpnconnect
     chmod ugo+x /etc/init.d/vpnconnect
     /sbin/chkconfig --add vpnconnect
     /sbin/chkconfig vpnconnect on
     /sbin/chkconfig --list vpnconnect

You should see a result that looks like this:

     vpnconnect      0:off   1:off   2:on    3:on    4:on    5:on    6:off

Incidentally, although the script shown will bring up the VPN tunnel and ensure the smooth flow of traffic, here are, in case you need them, some useful commands for debugging or just observing the operation of the tunnel and the packet filters:

     /usr/sbin/pppd call mytunnel debug dump logfd 2 nodetach unit 2
     /sbin/iptables -v [-t (nat|filter|mangle)] -L [NAME]
     /sbin/iptables -v -t filter -L INPUT
     /sbin/iptables -v -t filter -L FORWARD
     /sbin/iptables -v -t filter -L VPN_CHK
     /sbin/iptables -v -t nat -L POSTROUTING
     /sbin/ip route list table vpn.tunnel
     cat /proc/sys/net/ipv4/conf/*/rp_filter
     dmesg | tail -20

When you have the script working properly and it is starting and stopping the PPTP connection automatically (by rebooting the system, you can verify that the script works under real operating circumstances or you can just check that "vpnconnect start" and "vpnconnect stop" work from the command line), the final step is to set up logrotate to rotate the logfiles.

Either hack the /etc/logrotate.conf file itself to include the following lines or you can add the log file named above to an individual configuration file in the logrotate directory. We prefer the latter approach so that setting up logrotate can be accomplished by simply dropping the file into the logrotate directory, just like the various install programs do.

/etc/logrotate.d/vpnconnect:

     /var/log/vpnconnect
         missingok
         notifempty
         copytruncate
     }

You may run into trouble with the setup described herein, when running a PPTP VPN connection through your firewall, although we have used this arrangement with multiple PPTP servers and it works quite well. Probably the biggest show stopper is the problem with reverse path filtering, which we expressly handle in our examples. However there could be other problems lurking, in which case this chapter on "Troubleshooting Linux Firewalls/Running a PPTP Server Behind a NAT Firewall" may prove useful:

     http://flylib.com/books/en/3.105.1.141/1/

Various and sundry other notes deal with problems with PPTP and netfilter connection tracking. If you think you have a connection tracking problem, make sure that ip_nat_pptp and ip_conntrack_pptp modules are being loaded into the kernel by doing:

     /sbin/lsmod | grep pptp

You should see something like this:

     ip_nat_pptp             9797  0 
     ip_conntrack_pptp      15441  1 ip_nat_pptp

If you don't, you can force the modules to be loaded by adding the following lines to /etc/modprobe.conf:

.

       .

install ip_nat_pptp /bin/true
install ip_conntrack_pptp /bin/true

       .
       .
       .

You may be able to get a picture of what's happening by running a packet sniffer such as Wireshark on the various network devices on the firewall. If you prefer the command line approach, try this tcpdump command (tracing the packets on the router 192.168.11.1, from the locally-connected system 192.168.11.101):

     /usr/sbin/tcpdump -i any -n -nn host 192.168.11.1 or \
         host 192.168.11.101

Adding rules to iptables that log packets as they pass through various stages of netfilter may also show you where things are going wrong. And, looking at any messages regarding packets being dumped, with dmesg, may shed some light.

Although we think that these notes referring to connection tracking and GRE are probably red herrings, one never knows. Take them with a grain of salt but they could be helpful.