Netgate Discussion Forum
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Search
    • Register
    • Login

    Restarting OpenVPN

    Scheduled Pinned Locked Moved OpenVPN
    29 Posts 5 Posters 19.8k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • B
      Briantist
      last edited by

      I should still be able to use that then as long as I'm controlling it from pfSense then, as opposed to an outside machine, right? I'm thinking I would install NRPE, and use a cron job to run one of the NRPE scripts, and send control commands to OpenVPN based on the results. Any thoughts? Thanks for the quick response.

      1 Reply Last reply Reply Quote 0
      • jimpJ
        jimp Rebel Alliance Developer Netgate
        last edited by

        @cyboc:

        @jimp:

        Yeah I suppose that might be needed there. I don't think there is a way to make that happen inside of OpenVPN. Some Googling suggests that others do this (on other platforms) by shutting down the other server processes when they want to, so you're probably doing about the best you can.

        Yup, I googled around too, hoping that OpenVPN had a built-in feature to restart the tunnels periodically. It seems the answer is "no". Pity. I can't be the only person in the world who needs that feature.  :-[
        [/quote]

        Yeah, it seems like it would be a nice feature, but in reality that would involve the client periodically retrying the server connection on its own, potentially disrupting the tunnel there. Favoring stability over speed is usually a good choice, but it would be nice to have the option.

        Remember: Upvote with the 👍 button for any user/post you find to be helpful, informative, or deserving of recognition!

        Need help fast? Netgate Global Support!

        Do not Chat/PM for help!

        1 Reply Last reply Reply Quote 0
        • jimpJ
          jimp Rebel Alliance Developer Netgate
          last edited by

          @Briantist:

          I should still be able to use that then as long as I'm controlling it from pfSense then, as opposed to an outside machine, right? I'm thinking I would install NRPE, and use a cron job to run one of the NRPE scripts, and send control commands to OpenVPN based on the results. Any thoughts? Thanks for the quick response.

          Probably best to split that off into its own thread.

          Remember: Upvote with the 👍 button for any user/post you find to be helpful, informative, or deserving of recognition!

          Need help fast? Netgate Global Support!

          Do not Chat/PM for help!

          1 Reply Last reply Reply Quote 0
          • B
            Briantist
            last edited by

            @cyboc:

            @jimp:

            Yeah I suppose that might be needed there. I don't think there is a way to make that happen inside of OpenVPN. Some Googling suggests that others do this (on other platforms) by shutting down the other server processes when they want to, so you're probably doing about the best you can.

            Yup, I googled around too, hoping that OpenVPN had a built-in feature to restart the tunnels periodically. It seems the answer is "no". Pity. I can't be the only person in the world who needs that feature.  :-[
            [/quote]
            I found a way. You have to know which VPN server you're using (pfSense seems to assign them sequential numbers, server1, server2, etc.).

            Then in cron you can use this:

            
            nc -U /var/etc/openvpn/serverX.sock %signal SUGHUP%exit%
            
            

            The X in serverX.sock is the server number I referenced above. The exit is probably not necessary, at least that's what it seems in my testing, but I put it in anyway (the OpenVPN management interface only supports one connection at a time, so the OpenVPN status page in the web GUI is inoperable while you are connected).

            HTH

            1 Reply Last reply Reply Quote 0
            • C
              cyboc
              last edited by

              Hi Brian,

              Thanks for the tip.

              After thinking more about the requirements, I have refined them a bit. Instead of restarting the VPN client every night, I am going to restart the VPN whenever I detect that it is connected to the VPN server's secondary (slow) IP address instead of the primary (fast) IP address.

              The primary IP address is specified by the Server host or address setting on the VPN client config screen (i.e. in the server_addr setting in the pfSense XML config file). The secondary IP address is specified by the remote setting inside the Advanced setting on the VPN client config screen (i.e. in the custom_options setting in the pfSense XML config file).

              Here is a hackish, unfinished, unpolished PHP script that I am currently working on for this purpose. Please don't flame saying the code sucks. I know it sucks. It's not done yet. So far it seems to work okay in my testing.

              require_once('config.inc');
              include('openvpn.inc');
              
              // Read in all of the OpenVPN client configs.
              global $config;
              if (!is_array($config['openvpn']['openvpn-client']))
              {
                return;
              }
              
              // Find client config for London VPN server
              $found_config = false;
              $name = "London";
              foreach ($config['openvpn']['openvpn-client'] as & $settings)
              {
                if (stripos($settings['description'], $name) !== false)
                {
                   $found_config = true;
                   break;
                }
              }
              
              // If client config not found, exit.
              if (!$found_config)
              {
                echo "Did not find client VPN config for \"$name\".\n";
                exit;
              }
              
              // Print the client config.
              echo "vpnid: {$settings['vpnid']}\n";
              echo "description: {$settings['description']}\n";
              echo "server_addr: {$settings['server_addr']}\n";   
              echo "custom_options: {$settings['custom_options']}\n";
              
              // Resolve the configured host name to an IP address.
              // This is the VPN server's primary IP.
              $ip = gethostbyname($settings['server_addr']);
              echo "VPN server primary IP: $ip\n";
              
              // Tokenize the custom_options to get the VPN 
              // server's secondary IP.
              $tok = strtok($settings['custom_options'], " \n\t;");
              while ($tok !== false) {
                if (strcasecmp($tok, 'remote') == 0)
                {
                  $tok = strtok(" \n\t;");
                  if ($tok !== false)
                  {
                    $ip_secondary = gethostbyname($tok);
                    echo "VPN server secondary IP: $ip_secondary\n";    
                  }
                }
                $tok = strtok(" \n\t;");
              }
              
              // Find the status for the VPN client.
              $found_status = false;
              $clients = openvpn_get_active_clients();
              foreach ($clients as $client) 
              {
                if (stripos($client['name'], $name) !== false)
                {
                  $found_status = true;
                  break;
                }
              }
              
              // If client status not found, exit.
              if (!$found_config)
              {
                echo "Did not find VPN client status for \"$name\".\n";
                exit;
              }
              
              // Print the client status
              echo "name: {$client['name']}\n";
              echo "mgmt: {$client['mgmt']}\n";
              echo "status: {$client['status']}\n";
              echo "remote_host: {$client['remote_host']}";
              
              // See if the client is connected to a secondary IP.
              // Note: $client['remote_host'] has trailing white space that
              // we must trim before comparing to $ip
              $remote_host = rtrim($client['remote_host']);
              if ($client['status'] == 'up' && $remote_host !== $ip)
              {
                echo "The VPN client is connected to the server's secondary IP.\n";
              }
              else 
              {
                echo "The VPN client is NOT connected to the server's secondary IP.\n";
                echo "NOT restarting VPN.\n";
                exit;
              }
              
              // If we got this far, we will want to restart the VPN client
              // so it connects to primary IP instead of secondary IP.
              
              // Before trying to connect to primary IP, we have to make sure
              // we can ping it. If we can't ping it, there's no sense in
              // trying to connect to it. Furthermore, that is probably
              // the reason why the client is currently connected to the 
              // secondary IP.
              exec("ping -t 10 -o $ip", $output, $return_var);
              echo "ping return_var: $return_var\n";
              
              // A return value of 0 means at least one ping was replied to.
              // In other words, the IP address is up and we should be
              // able to restart to VPN to connect to it.
              if ($return_var == 0)
              {
                echo "Restarting VPN.\n";
                openvpn_restart('client', $settings);
                echo "Finished restarting VPN.\n";
              }
              ?>
              

              As you can see, the script makes use of the handy config variables in config.inc and the handy functions in openvpn.inc. Rather than hard-coding the client vpnid number in my script, I scan all of the client configs for a description containing 'London'; that will be the client that connects to our London server.

              When finished, I will store this script somewhere in the pfSense filesystem (any suggestions as to an appropriate directory to store it in?). Then I will call it periodically with cron, perhaps once per hour.

              HTH

              1 Reply Last reply Reply Quote 0
              • B
                Briantist
                last edited by

                Neat! For my purposes, I am actually doing more than just restarting, and I considered using PHP but I was really looking for something that didn't rely on external files. Basically, I want to be able to restore a config backup and not have to restore any random files to get this working again. I don't like that I am hard coding the server number, but I'm okay with it for now.

                I looked into the add-on package that lets you enter PHP code in the web gui (PHP Service) and runs it every second, but it was buggy and didn't work right, so I promptly uninstalled it. Please post your final solution; I'm interested, even if it's just to run the PHP via cron.

                1 Reply Last reply Reply Quote 0
                • C
                  cyboc
                  last edited by

                  I haven't tried it yet but I think the Shellcmd package might let you echo some PHP code to a file in /tmp on system startup. Then you should be able to call that script from cron. This should allow you to store the script (somewhat indirectly) inside the exported config file. Again, I haven't tried this yet but it seems like it could do the job.

                  1 Reply Last reply Reply Quote 0
                  • C
                    cyboc
                    last edited by

                    Meh. I tried Shellcmd. The GUI for it only lets you enter one line. Not really convenient for what I wanted to do.

                    1 Reply Last reply Reply Quote 0
                    • C
                      cyboc
                      last edited by

                      Brian,

                      I am running the embedded version of pfSense. I just tried storing my custom script in /scripts. Then I rebooted. Low and behold, after rebooting, my script was still in /scripts. I expected that it would have been wiped out by a reboot but I was wrong. That's good. Now I can store my script there. It's not quite the same as storing it in the XML config file but at least I can keep all my scripts in one place.

                      1 Reply Last reply Reply Quote 0
                      • B
                        Briantist
                        last edited by

                        Yeah the embedded version won't delete your files on you. You might have some trouble writing things if you don't mount the filesystem as RW first, but anyway that's not what I was worried about. I am doing something that will be used at multiple locations, and I just don't want to have backup additional files with the config that I would then have to put back in place in the event of a restore.

                        From what I could tell there is no way to do multiple lines with echo in the bourne shell, so you would need to install bash (another dependency I don't want). It just happens that cron does have a way to send mult-line input (the % character) so that helped me out there.

                        1 Reply Last reply Reply Quote 0
                        • C
                          cyboc
                          last edited by

                          /scripts already seems to be mounted writeable. I did not have to do a remount-rw. :)

                          1 Reply Last reply Reply Quote 0
                          • C
                            cyboc
                            last edited by

                            Brian,

                            I have revised the code a bit. For example, I now write some messages to the system log by calling the logger command. I ran this script once per hour last night using cron. It seems to work fine so far. Still requires more testing. For example, I will have to pull the plug on our primary internet connection to force the VPN client to switchover to the secondary IP (i.e. backup connection). Then I will have to verify that this script forces the connection back to the primary IP.

                            Here's the current version of the script:

                            #!/usr/local/bin/php -f
                            require_once('config.inc');
                            include('openvpn.inc');
                            
                            // Read in all of the OpenVPN client configs.
                            global $config;
                            if (!is_array($config['openvpn']['openvpn-client']))
                            {
                              return;
                            }
                            
                            // Find client config for London VPN server
                            $found_config = false;
                            $name = "London";
                            foreach ($config['openvpn']['openvpn-client'] as & $settings)
                            {
                              if (stripos($settings['description'], $name) !== false)
                              {
                                 $found_config = true;
                                 break;
                              }
                            }
                            
                            // If client config not found, exit.
                            if (!$found_config)
                            {
                              echo "Did not find client VPN config for \"$name\".\n";
                              exit;
                            }
                            
                            // Print the client config.
                            echo "vpnid: {$settings['vpnid']}\n";
                            echo "description: {$settings['description']}\n";
                            echo "server_addr: {$settings['server_addr']}\n";   
                            echo "custom_options: {$settings['custom_options']}\n";
                            
                            // Resolve the configured host name to an IP address.
                            // This is the VPN server's primary IP.
                            $ip = gethostbyname($settings['server_addr']);
                            echo "VPN server primary IP: $ip\n";
                            
                            // Tokenize the custom_options to get the VPN 
                            // server's secondary IP.
                            $tok = strtok($settings['custom_options'], " \n\t;");
                            while ($tok !== false) {
                              if (strcasecmp($tok, 'remote') == 0)
                              {
                                $tok = strtok(" \n\t;");
                                if ($tok !== false)
                                {
                                  $ip_secondary = gethostbyname($tok);
                                  echo "VPN server secondary IP: $ip_secondary\n";    
                                }
                              }
                              $tok = strtok(" \n\t;");
                            }
                            
                            // Find the status for the VPN client.
                            $found_status = false;
                            $clients = openvpn_get_active_clients();
                            foreach ($clients as $client) 
                            {
                              if (stripos($client['name'], $name) !== false)
                              {
                                $found_status = true;
                                break;
                              }
                            }
                            
                            // If client status not found, exit.
                            if (!$found_config)
                            {
                              echo "Did not find VPN client status for \"$name\".\n";
                              exit;
                            }
                            
                            // Print the client status
                            echo "name: {$client['name']}\n";
                            echo "mgmt: {$client['mgmt']}\n";
                            echo "status: {$client['status']}\n";
                            echo "remote_host: {$client['remote_host']}";
                            
                            // See if the client is connected to a secondary IP.
                            // Note: $client['remote_host'] has trailing white space that
                            // we must trim before comparing to $ip
                            $remote_host = rtrim($client['remote_host']);
                            if ($client['status'] == 'up' && $remote_host == $ip)
                            {
                              $msg = "The $name VPN client is connected to $ip (VPN server primary IP). Not restarting client.\n";
                              echo $msg;
                              exec("logger '$msg'");
                              exit;
                            }
                            else if ($client['status'] == 'up')
                            {
                              $msg = "The $name VPN client is connected to $ip (VPN server secondary IP).\n";
                              echo $msg;
                              exec("logger '$msg'");
                            }
                            else
                            {
                              $msg = "The $name VPN client is not connected. Current status is {$client['status']}.\n";
                              echo $msg;
                              exec("logger '$msg'");
                              exit;
                            }
                            
                            // If we got this far, we will want to restart the VPN client
                            // so it connects to primary IP instead of secondary IP.
                            
                            // Before trying to connect to primary IP, we have to make sure
                            // we can ping it. If we can't ping it, there's no sense in
                            // trying to connect to it. Furthermore, that is probably
                            // the reason why the client is currently connected to the 
                            // secondary IP.
                            exec("ping -t 10 -o $ip", $output, $return_var);
                            echo "ping return_var: $return_var\n";
                            
                            // A return value of 0 means at least one ping was replied to.
                            // In other words, the IP address is up and we should be
                            // able to restart to VPN to connect to it.
                            if ($return_var == 0)
                            {
                              $msg = "VPN server primary IP is up. Restarting $name VPN client.\n";
                              echo $msg;
                              exec("logger '$msg'");
                              openvpn_restart('client', $settings);
                              $msg = "Finished restarting $name VPN client.\n";
                              echo $msg;
                              exec("logger '$msg'");
                            }
                            else
                            {
                              $msg = "VPN server primary IP is down. Not restarting $name VPN client.\n";
                              echo $msg;
                              exec("logger '$msg'");
                            }
                            ?>
                            
                            1 Reply Last reply Reply Quote 0
                            • GruensFroeschliG
                              GruensFroeschli
                              last edited by

                              Having just read this thread i something think you're shooting on sparrows with cannons. (Writing scripts, changing php).

                              Is it not feasible to have 2 OpenVPN tunnels up with two different tranfer subnets.
                              Each VPN tunnel on it's own WAN.

                              When both tunnels are up a failover-pool forces all traffic to the fast connection.
                              Should it go down, the backup connection will be used.
                              If the connection comes back up everything switches to the fast connection again.

                              Should asymetric routing occur (for whatever reasons) traffic could even be NATed into the tunnel to ensure the answer comes back the same tunnel.

                              We do what we must, because we can.

                              Asking questions the smart way: http://www.catb.org/esr/faqs/smart-questions.html

                              1 Reply Last reply Reply Quote 0
                              • C
                                cyboc
                                last edited by

                                Hi GruensFroeschli,

                                Thank you for your comments.

                                @GruensFroeschli:

                                Is it not feasible to have 2 OpenVPN tunnels up with two different tranfer subnets.
                                Each VPN tunnel on it's own WAN.

                                We don't have 2 tunnels. We have only 1. We use OpenVPN's standard way of connecting to redundant server IPs. That is, our VPN client is configured with two remote settings in the config file. For example:

                                
                                remote fast.foo.com 1194
                                remote slow.foo.com 1194
                                
                                

                                Note that we do not use the –remote-random setting in our config. Therefore, the client will always try the first remote IP first and the second remote IP next. If the first IP is up, the client connects to that one but if it is down, the client connects to the second IP.

                                @GruensFroeschli:

                                When both tunnels are up a failover-pool forces all traffic to the fast connection.
                                Should it go down, the backup connection will be used.

                                Yes, I absolutely agree. Our current configuration already does that.

                                @GruensFroeschli:

                                If the connection comes back up everything switches to the fast connection again.

                                I have never seen an OpenVPN client connection switch back to the first remote IP after connecting to the second remote IP. As far as I know, there is NO mechanism in OpenVPN to keep trying the first IP after connecting to the second IP and then automatically switch back to the first one when it is up. If someone can tell me the name of the OpenVPN setting to enable this behaviour, I'm all ears.

                                We have successfully been running our VPN server like this for months. When the primary IP goes down, the clients automatically switch over to the secondary IP after a short timeout period (about 1 minute). All of this happens without any intervention required by me. That's good!

                                However, since the server side's secondary internet connection is way slower than the primary internet connection, we don't like to run the VPN on the secondary connection for long periods of time.

                                We usually don't realize that the VPN clients have switched over to the secondary IP until a user calls a couple of days later to say that the VPN seems kind of slow. When we get that phone call, we manually restart the VPN client to force a reconnection to the primary IP.

                                The purpose of my script is to automatically switch the VPN clients back to the primary IP when it comes back up.

                                Note that my script runs only on the client side, not on the server side. Note also that we run our VPN in UDP mode. Perhaps the behaviour of the failover pool is different for TCP mode?

                                If someone can suggest a simpler way of automatically switching the OpenVPN client back to the primary IP after connecting to the secondary IP, I'm all ears. (And by suggest, I mean, showing the exact config settings or code to accomplish this).

                                1 Reply Last reply Reply Quote 0
                                • GruensFroeschliG
                                  GruensFroeschli
                                  last edited by

                                  You misunderstand me.
                                  My suggestion is to not have a single OpenVPN connection with two remote entries,
                                  but to actually have two OpenVPN connections with only one remote entry.

                                  Let pfSense handle the switching between the tunnels.
                                  Or is this OpenVPN client not a pfSense?

                                  With pfSense you can create failover-pools under "System –> Routing"

                                  We do what we must, because we can.

                                  Asking questions the smart way: http://www.catb.org/esr/faqs/smart-questions.html

                                  1 Reply Last reply Reply Quote 0
                                  • C
                                    cyboc
                                    last edited by

                                    Yes, do use failover pools. For example, we use failover pools at our main office, the office with the two internet connections (which is also the office where the VPN server is). With the failover pool, users at our main office are automatically switched over the the secondary internet connection when the primary internet connection goes down. Thus, they can keep browsing the web, sending email, etc, even when the primary internet connection goes down.

                                    Are you suggesting is to user failover pools on the client side (i.e. at our remote offices), which has only one internet connection? If so, how do I make the two tunnels appear as gateways on the Failover group configuration screen? I have never heard of this. If you have a step-by-step configuration for this, I'd like to see it. When I try to add a gateway using the Edit Gateway screen, the only interfaces I see are the actual WAN and LAN interfaces, not any sort of OpenVPN "pseudo" interface.

                                    Even if it is possible to have the tunnels appear as gateways in the failover group configuration screen, I'm not sure that would work for our requirements. We do NOT want ALL of the client's traffic to go over the VPN. We want inter-office traffic to go over the VPN but we want all other traffic (web browsing, etc) to go over the client's own internet connection. We have only limited bandwidth at the main office and we don't want all of the web browsing traffic from the several remote offices to go over the VPN.

                                    Again, if you have an actual working example, it would be helpful. Otherwise your comments will not really help me solve the problem, which has already been solved by my script.

                                    1 Reply Last reply Reply Quote 0
                                    • GruensFroeschliG
                                      GruensFroeschli
                                      last edited by

                                      Well not a step by step for this exact scenario but i'll write something up here:

                                      (Everything assuming you're running 2.0)

                                      Server:
                                      1: Create on the server side two OpenVPN instances and bind one to each WAN.
                                      If not specified, OpenVPN will bind to all interfaces. You can specify by setting the "–local host" in the "Advanced" configuration field.
                                      Make sure that you use two different transfer-subnets for these servers. (eg. 172.21.0.1/24 and 172.22.0.1/24)

                                      2: Assign the created OpenVPN clients as interface (Interface --> assign).
                                      Enable these newly added OPTs and set the IP configuration to "none".

                                      3: Go to "System --> Routing" and create two gateways. One for each client-side IP inside the tunnel.
                                      (If you're using the above subnets, that would be 172.21.0.2 and 172.22.0.2)

                                      4: Create a failover-pool under the tab "Groups" (main-VPN Tier1, backup-VPN Tier2)

                                      5: Create on your LAN tab firewall-rules.
                                      #1: allow, source: LAN-subnet, destination: off_site_subnet, gateway: failoverpool
                                      #2: allow, source: LAN-subnet, destination: any, gateway: default

                                      Client:
                                      1: Create two clients on the pfSense, one pointing to each WAN on your server-side.

                                      2: Assign the created OpenVPN clients as interface (Interface --> assign).
                                      Enable these newly added OPTs and set the IP configuration to "none".

                                      3: Go to "System --> Routing" and create two gateways. One for each server-side IP inside the tunnel.
                                      (If you're using the above subnets, that would be 172.21.0.1 and 172.22.0.1)

                                      4: Create a failover-pool under the tab "Groups" (main-VPN Tier1, backup-VPN Tier2)

                                      5: Create on your LAN tab firewall-rules.
                                      #1: allow, source: LAN-subnet, destination: main_site_subnet, gateway: failoverpool
                                      #2: allow, source: LAN-subnet, destination: any, gateway: default.

                                      With such a setup, the failoverpool is responsible for sending traffic via the correct WAN and no longer the OpenVPN client.

                                      Addendum:
                                      I'm not sure if OpenVPN-traffic for WAN2 will actually leave via WAN2.
                                      If it doesn't, a workaround would be that you bind the two OpenVPN server instances on different ports to the LAN interface and create two portforwards to the LAN interface.
                                      With NAT involved i know that traffic will leave via the correct WAN.

                                      Another thing i see which might be problematic.
                                      Traffic from the pfSense itself also needs to use the failover-pools.
                                      I never actually did this, but from what i read you can do this via the floating rules.
                                      Maybe someone else can help you on this.

                                      Also if one of the WANs goes down and a connection has been established on the second WAN and then the first WAN comes back up, it could be that you have asymetric routing.
                                      If NAT is not a problem for your setup you could NAT all traffic into the VPN tunnel.
                                      For the other side this would effectively hide the subnet behind the pfSense and traffic would always flow correctly.
                                      To configure this go to "Firewall --> NAT --> outbound" and enable manual rule generation.
                                      Now create a rule for both WANs and the assigned OpenVPN interfaces.

                                      We do what we must, because we can.

                                      Asking questions the smart way: http://www.catb.org/esr/faqs/smart-questions.html

                                      1 Reply Last reply Reply Quote 0
                                      • C
                                        cyboc
                                        last edited by

                                        Thank you very much for that procedure GruensFroeschli! When I have a few spare hours, I'll try that technique in a lab.

                                        As for my script, below is a new version with a couple of changes. First, the "VPN client is connected to <ip address="">" message was printing the wrong IP address. Second, I modified the code that does the restart of the openvpn process. Instead of calling the openvpn_restart function in the file /etc/inc/openvpn.inc, I mimicked the restart code in /usr/local/www/status_services.php, which seems to be more reliable. In my testing, the openvpn_restart function sometimes failed to start the new openvpn process after terminating the old process. The main difference between the two seems to be in the way they wait for the old process to terminate.

                                        More testing is required but it seems to work right now.

                                        #!/usr/local/bin/php -f
                                        require_once('config.inc');
                                        include('openvpn.inc');
                                        
                                        // Read in all of the OpenVPN client configs.
                                        global $config;
                                        if (!is_array($config['openvpn']['openvpn-client']))
                                        {
                                          return;
                                        }
                                        
                                        // Find client config for London VPN server
                                        $found_config = false;
                                        $name = "London";
                                        foreach ($config['openvpn']['openvpn-client'] as & $settings)
                                        {
                                          if (stripos($settings['description'], $name) !== false)
                                          {
                                             $found_config = true;
                                             break;
                                          }
                                        }
                                        
                                        // If client config not found, exit.
                                        if (!$found_config)
                                        {
                                          echo "Did not find client VPN config for \"$name\".\n";
                                          exit;
                                        }
                                        
                                        // Print the client config.
                                        echo "vpnid: {$settings['vpnid']}\n";
                                        echo "description: {$settings['description']}\n";
                                        echo "server_addr: {$settings['server_addr']}\n";   
                                        echo "custom_options: {$settings['custom_options']}\n";
                                        
                                        // Resolve the configured host name to an IP address.
                                        // This is the VPN server's primary IP.
                                        $ip_primary = gethostbyname($settings['server_addr']);
                                        echo "VPN server primary IP: $ip_primary\n";
                                        
                                        // Tokenize the custom_options to get the VPN 
                                        // server's secondary IP.
                                        $tok = strtok($settings['custom_options'], " \n\t;");
                                        while ($tok !== false) {
                                          if (strcasecmp($tok, 'remote') == 0)
                                          {
                                            $tok = strtok(" \n\t;");
                                            if ($tok !== false)
                                            {
                                              $ip_secondary = gethostbyname($tok);
                                              echo "VPN server secondary IP: $ip_secondary\n";    
                                            }
                                          }
                                          $tok = strtok(" \n\t;");
                                        }
                                        
                                        // Find the status for the VPN client.
                                        $found_status = false;
                                        $clients = openvpn_get_active_clients();
                                        foreach ($clients as $client) 
                                        {
                                          if (stripos($client['name'], $name) !== false)
                                          {
                                            $found_status = true;
                                            break;
                                          }
                                        }
                                        
                                        // If client status not found, exit.
                                        if (!$found_config)
                                        {
                                          echo "Did not find VPN client status for \"$name\".\n";
                                          exit;
                                        }
                                        
                                        // Print the client status
                                        echo "name: {$client['name']}\n";
                                        echo "mgmt: {$client['mgmt']}\n";
                                        echo "status: {$client['status']}\n";
                                        echo "remote_host: {$client['remote_host']}";
                                        
                                        // See if the client is connected to a secondary IP.
                                        // Note: $client['remote_host'] has trailing white space that
                                        // we must trim before comparing to $ip_primary
                                        $remote_host = rtrim($client['remote_host']);
                                        if ($client['status'] == 'up' && $remote_host == $ip_primary)
                                        {
                                          $msg = "The $name VPN client is connected to $remote_host (VPN server primary IP). Not restarting client.\n";
                                          echo $msg;
                                          exec("logger '$msg'");
                                          exit;
                                        }
                                        else if ($client['status'] == 'up')
                                        {
                                          $msg = "The $name VPN client is connected to $remote_host (VPN server secondary IP).\n";
                                          echo $msg;
                                          exec("logger '$msg'");
                                        }
                                        else
                                        {
                                          $msg = "The $name VPN client is not connected. Current status is {$client['status']}.\n";
                                          echo $msg;
                                          exec("logger '$msg'");
                                          exit;
                                        }
                                        
                                        // If we got this far, we will want to restart the VPN client
                                        // so it connects to primary IP instead of secondary IP.
                                        
                                        // Before trying to connect to primary IP, we have to make sure
                                        // we can ping it. If we can't ping it, there's no sense in
                                        // trying to connect to it. Furthermore, that is probably
                                        // the reason why the client is currently connected to the 
                                        // secondary IP.
                                        exec("ping -t 10 -o $ip_primary", $output, $return_var);
                                        echo "ping return_var: $return_var\n";
                                        
                                        // A return value of 0 means at least one ping was replied to.
                                        // In other words, the IP address is up and we should be
                                        // able to restart to VPN to connect to it.
                                        if ($return_var == 0)
                                        {
                                          $msg = "VPN server primary IP is up. Restarting $name VPN client.\n";
                                          echo $msg;
                                          exec("logger '$msg'");
                                        
                                          // Restart the VPN client by mimicking the technique used in
                                          // the file /usr/local/www/status_services.php
                                          //openvpn_restart('client', $settings);
                                          $configfile = "{$g['varetc_path']}/openvpn/client{$settings['vpnid']}.conf";
                                          $pidfile =    "{$g['varrun_path']}/openvpn_client{$settings['vpnid']}.pid";
                                          if (file_exists($configfile)) {
                                            $msg = "Terminating openvpn using pidfile: $pidfile\n";
                                            echo $msg;
                                            exec("logger '$msg'");
                                            killbypid($pidfile);
                                            // wait for process to terminate
                                            sleep(1);
                                            $msg = "Starting new openvpn process using config file: $configfile\n";
                                            echo $msg;
                                            exec("logger '$msg'");
                                            mwexec_bg("/usr/local/sbin/openvpn --config {$configfile}");
                                          }
                                        
                                          $msg = "Finished restarting $name VPN client.\n";
                                          echo $msg;
                                          exec("logger '$msg'");
                                        }
                                        else
                                        {
                                          $msg = "VPN server primary IP is down. Not restarting $name VPN client.\n";
                                          echo $msg;
                                          exec("logger '$msg'");
                                        }
                                        ?>
                                        ```</ip>
                                        1 Reply Last reply Reply Quote 0
                                        • P
                                          Philay
                                          last edited by

                                          Hi cyboc,

                                          Very nice work right here. I have try your solution today (we have similar dual WAN config) and it is very simple indeed! Contrary to what GruensFroeschli said, I thinks his solution is the one that is "shooting on sparrows with cannons." Heck, his solution hasn't even been tried yet!

                                          One suggestion for your code however. There is problem with these two lines:

                                          $ip_primary = gethostbyname($settings['server_addr']);
                                          // ...
                                          if ($client['status'] == 'up' && $remote_host == $ip_primary)
                                          

                                          You assuming that PHP function gethostbyname will work correctly every time. However, I had temporary problem with my ISP DNS server today. It was down. So when I try to do the lookup with gethostbyname it fails (timeout). So instead of returning IP address, it returns the hostname I was looking up. So, expression $remote_host == $ip_primary will return false eventhough hostname should resolve to that $remote_host IP if lookup was working. So, VPN as a result will restart when it shouldn't. See it?

                                          To explain with example, let's say foo.com should resolve to 192.168.1.1 and $remote_host is currently 192.168.1.1 and that is the primary IP. If lookup works, $remote_host == $ip_primary will return true and VPN will not restart (because 192.168.1.1 equals 192.168.1.1). This is what we want if connection currently on primary IP (is good). But if lookup fails, expression will return false and VPN will restart when it should not (192.168.1.1 is NOT equal to foo.com). Get it?

                                          So maybe if lookup fails, you should just abort? Just suggestion here.

                                          1 Reply Last reply Reply Quote 0
                                          • C
                                            cyboc
                                            last edited by

                                            @Philay:

                                            You assuming that PHP function gethostbyname will work correctly every time. However, I had temporary problem with my ISP DNS server today. It was down. So when I try to do the lookup with gethostbyname it fails (timeout). So instead of returning IP address, it returns the hostname I was looking up.

                                            Philay, good catch. You are right. The documentation for gethostbyname does say that it "Returns the IPv4 address or a string containing the unmodified hostname on failure."

                                            Well when I started this thread, I did say that I'm not a PHP guy. :)

                                            I will fix this. Thanks!

                                            BTW, I think you are too hard on GruensFroeschli. I think his solution is fine and will probably work. I just don't have time to try it right now.

                                            1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Copyright 2025 Rubicon Communications LLC (Netgate). All rights reserved.