From bf92bc791de6b04246c7a2f67945ce1412906d95 Mon Sep 17 00:00:00 2001 From: Seth Mos Date: Thu, 15 Jan 2009 09:00:30 +0000 Subject: [PATCH] - Add proper support for using hostnames for the remote IPsec gateway. - Make IPsec reloading granular, this resolves the long standing issue that a IPsec reload will cause all tunnels to drop. - Change IPsec edit screen description for remote gateway that a IP address or hostname is allowed here. We already accepted hostnames before. - Add /etc/rc.newipsecdns, when a hostname changes IP we invoke this script to remove the old tunnel and setup the new one. --- etc/inc/pfsense-utils.inc | 95 +++++++++- etc/inc/util.inc | 22 ++- etc/inc/vpn.inc | 300 +++++++++++++++++++++++++----- etc/rc.newipsecdns | 52 ++++++ usr/local/www/diag_ipsec.php | 122 +++++------- usr/local/www/diag_ipsec_sad.php | 34 +--- usr/local/www/diag_logs_ipsec.php | 3 + usr/local/www/vpn_ipsec.php | 11 +- usr/local/www/vpn_ipsec_edit.php | 24 ++- 9 files changed, 501 insertions(+), 162 deletions(-) create mode 100755 etc/rc.newipsecdns diff --git a/etc/inc/pfsense-utils.inc b/etc/inc/pfsense-utils.inc index 56ab75744b..27945433f3 100644 --- a/etc/inc/pfsense-utils.inc +++ b/etc/inc/pfsense-utils.inc @@ -1434,7 +1434,7 @@ function find_interface_ip($interface, $flush = false) { } function guess_interface_from_ip($ipaddress) { - $ret = exec_command("/usr/bin/netstat -rn | /usr/bin/awk '/^{$ipaddress}/ {print \$6}'"); + $ret = exec_command("/usr/bin/netstat -rn | /usr/bin/awk '/^{$ipaddress}/ {print $6}'"); if(empty($ret)) { return false; } @@ -3740,4 +3740,97 @@ function safe_write_file($file, $content, $force_binary) { return true; } +/* Write out all the found IP addresses to a file + * so we can compare it on change */ +function add_hostname_to_watch($hostname) { + if(!is_dir("/var/db/dnscache")) { + mkdir("/var/db/dnscache"); + } + if((is_fqdn($hostname)) && (!is_ipaddr($hostname))) { + $domrecords = array(); + $domips = array(); + exec("host -t A $hostname", $domrecords, $rethost); + if($rethost == 0) { + foreach($domrecords as $domr) { + $doml = explode(" ", $domr); + $domip = $doml[3]; + /* fill array with domain ip addresses */ + if(is_ipaddr($domip)) { + $domips[] = $domip; + } + } + } + sort($domips); + $contents = ""; + if(! empty($domips)) { + foreach($domips as $ip) { + $contents .= "$ip\n"; + } + } + file_put_contents("/var/db/dnscache/$hostname", $contents); + } +} + +/* Compare the current hostname DNS to the DNS cache we made + * if it has changed we return the old records + * if no change we return true */ +function compare_hostname_to_dnscache($hostname) { + if(!is_dir("/var/db/dnscache")) { + mkdir("/var/db/dnscache"); + } + $hostname = trim($hostname); + if(is_readable("/var/db/dnscache/{$hostname}")) { + $oldcontents = file_get_contents("/var/db/dnscache/{$hostname}"); + } else { + $oldcontents = ""; + } + if((is_fqdn($hostname)) && (!is_ipaddr($hostname))) { + $domrecords = array(); + $domips = array(); + exec("host -t A $hostname", $domrecords, $rethost); + if($rethost == 0) { + foreach($domrecords as $domr) { + $doml = explode(" ", $domr); + $domip = $doml[3]; + /* fill array with domain ip addresses */ + if(is_ipaddr($domip)) { + $domips[] = $domip; + } + } + } + sort($domips); + $contents = ""; + if(! empty($domips)) { + foreach($domips as $ip) { + $contents .= "$ip\n"; + } + } + } + + if(trim($oldcontents) != trim($contents)) { + log_error("DNSCACHE: Found old IP {$oldcontents} and new IP {$contents}"); + return ($oldcontents); + } else { + return false; + } +} + +function is_fqdn($fqdn) { + $hostname = false; + if(preg_match("/[-A-Z0-9\.]+\.[-A-Z0-9\.]+/i", $fqdn)) { + $hostname = true; + } + if(preg_match("/\.\./", $fqdn)) { + $hostname = false; + } + if(preg_match("/^\./i", $fqdn)) { + $hostname = false; + } + if(preg_match("/\//i", $fqdn)) { + $hostname = false; + } + return($hostname); +} + + ?> diff --git a/etc/inc/util.inc b/etc/inc/util.inc index 022777dca5..1fbc0bf2ce 100644 --- a/etc/inc/util.inc +++ b/etc/inc/util.inc @@ -566,4 +566,24 @@ function mac_format($clientmac) { } } -?> \ No newline at end of file +function resolve_retry($hostname, $retries = 5) { + + if (is_ipaddr($hostname)) + return $hostname; + + for ($i = 0; $i < $retries; $i++) { + $ip = gethostbyname($hostname); + + if ($ip && $ip != $hostname) { + /* success */ + return $ip; + } + + sleep(1); + } + + return false; +} + + +?> diff --git a/etc/inc/vpn.inc b/etc/inc/vpn.inc index b2ddefc414..2cd4043754 100644 --- a/etc/inc/vpn.inc +++ b/etc/inc/vpn.inc @@ -104,19 +104,6 @@ function vpn_ipsec_configure($ipchg = false) { unlink_if_exists("/var/db/ipsecpinghosts"); touch("/var/db/ipsecpinghosts"); - if($g['booting'] == true) { - /* determine if we should load the via padlock module */ - $dmesg_boot = `cat /var/log/dmesg.boot | grep CPU`; - if(stristr($dmesg_boot, "ACE") == true) { - //echo "Enabling [VIA Padlock] ..."; - //mwexec("/sbin/kldload padlock"); - //mwexec("/sbin/sysctl net.inet.ipsec.crypto_support=1"); - //mwexec("/usr/local/sbin/setkey -F"); - //mwexec("/usr/local/sbin/setkey -FP"); - //echo " done.\n"; - } - } - if(isset($config['ipsec']['preferredoldsa'])) { mwexec("/sbin/sysctl net.key.preferred_oldsa=0"); } else { @@ -143,6 +130,8 @@ function vpn_ipsec_configure($ipchg = false) { /* kill racoon */ mwexec("/usr/bin/killall racoon", true); + killbypid("{$g['varrun_path']}/dnswatch-ipsec.pid"); + /* wait for process to die */ sleep(2); @@ -164,7 +153,7 @@ function vpn_ipsec_configure($ipchg = false) { if (isset($ipseccfg['enable'])) { /* fastforwarding is not compatible with ipsec tunnels */ - system("/sbin/sysctl net.inet.ip.fastforwarding=0 >/dev/null 2>&1"); + mwexec("/sbin/sysctl net.inet.ip.fastforwarding=0"); if (!$curwanip) { /* IP address not configured yet, exit */ @@ -180,6 +169,9 @@ function vpn_ipsec_configure($ipchg = false) { if ((is_array($ipseccfg['tunnel']) && count($ipseccfg['tunnel'])) || isset($ipseccfg['mobileclients']['enable'])) { + $dnswatch_list = array(); + $rgmap = array(); + if (is_array($ipseccfg['tunnel']) && count($ipseccfg['tunnel'])) { /* generate spd.conf */ @@ -200,30 +192,47 @@ function vpn_ipsec_configure($ipchg = false) { continue; $ep = vpn_endpoint_determine($tunnel, $curwanip); + /* see if this tunnel has a hostname for the remote-gateway, and if so, + * try to resolve it now and add it to the list for dnswatch */ + if (!is_ipaddr($tunnel['remote-gateway'])) { + $dnswatch_list[] = $tunnel['remote-gateway']; + $rgip = resolve_retry($tunnel['remote-gateway']); + add_hostname_to_watch($tunnel['remote-gateway']); + if (!$rgip) { + log_error("Could not deterimine VPN endpoint for {$tunnel['descr']}"); + continue; + } + } else { + $rgip = $tunnel['remote-gateway']; + } + $rgmap[$tunnel['remote-gateway']] = $rgip; if (!$ep) { log_error("Could not deterimine VPN endpoint for {$tunnel['descr']}"); continue; } + vpn_localnet_determine($tunnel['local-subnet'], $sa, $sn); if(is_domain($tunnel['remote-gateway'])) { $tmp = gethostbyname($tunnel['remote-gateway']); - if($tmp) + if($tmp) { $tunnel['remote-gateway'] = $tmp; + } } /* add entry to host pinger */ if ($tunnel['pinghost']) { $pfd = fopen("/var/db/ipsecpinghosts", "a"); $iflist = array("lan" => "lan", "wan" => "wan"); - for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) - $iflist['opt' . $i] = "opt{$i}"; - foreach ($iflist as $ifent => $ifname) { - $interface_ip = find_interface_ip($config['interfaces'][$ifname]['if']); - if (ip_in_subnet($interface_ip, $sa . "/" . $sn)) - $srcip = find_interface_ip($config['interfaces'][$ifname]['if']); - } + for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) + $iflist['opt' . $i] = "opt{$i}"; + + foreach ($iflist as $ifent => $ifname) { + $interface_ip = find_interface_ip($config['interfaces'][$ifname]['if']); + if (ip_in_subnet($interface_ip, $sa . "/" . $sn)) + $srcip = find_interface_ip($config['interfaces'][$ifname]['if']); + } $dstip = $tunnel['pinghost']; fwrite($pfd, "$srcip|$dstip|3\n"); fclose($pfd); @@ -287,7 +296,10 @@ function vpn_ipsec_configure($ipchg = false) { return 1; } - $racoonconf = ""; + $racoonconf = "# This file is automatically generated. Do not edit\n"; + $racoonconf .= "listen {\n"; + $racoonconf .= " adminsock \"/var/run/racoon.sock\" \"root\" \"wheel\" 0660;\n"; + $racoonconf .= "}\n"; $racoonconf .= "path pre_shared_key \"{$g['varetc_path']}/psk.txt\";\n\n"; $racoonconf .= "path certificate \"{$g['varetc_path']}\";\n\n"; @@ -322,6 +334,10 @@ function vpn_ipsec_configure($ipchg = false) { if (isset($tunnel['disabled'])) continue; + $rgip = $rgmap[$tunnel['remote-gateway']]; + if (!$rgip) + continue; + $ep = vpn_endpoint_determine($tunnel, $curwanip); if (!$ep) continue; @@ -411,11 +427,11 @@ EOD; } } $racoonconf .= << 0) { + $interval = 60; + if ($ipseccfg['dns-interval']) + $interval = $ipseccfg['dns-interval']; + + $hostnames = ""; + foreach ($dnswatch_list as $dns) + $hostnames .= " " . escapeshellarg($dns); + killbypid("{$g['varrun_path']}/dnswatch-ipsec.pid"); + mwexec("/usr/local/sbin/dnswatch {$g['varrun_path']}/dnswatch-ipsec.pid $interval " . + escapeshellarg("/etc/rc.newipsecdns") . $hostnames, false); + } + + } } @@ -1086,9 +1117,6 @@ function vpn_ipsec_force_reload() { $ipseccfg = $config['ipsec']; - /* kill any ipsec communications regardless when we are invoked */ - mwexec("/sbin/ifconfig enc0 down"); - /* kill racoon */ mwexec("/usr/bin/killall racoon", true); @@ -1109,4 +1137,190 @@ function vpn_ipsec_force_reload() { } +/* Walk the tunnels for hostname endpoints. If the hostnames + * resolve to a different IP now compared to the DNS cache + * we reload the policies if the endpoint has changed */ +function vpn_ipsec_refresh_policies() { + global $config; + global $g; + + $ipseccfg = $config['ipsec']; + + if (! isset($ipseccfg['enable'])) { + return true; + } + + /* Walk the Ipsec tunnel array */ + if (is_array($ipseccfg['tunnel']) && count($ipseccfg['tunnel'])) { + foreach ($ipseccfg['tunnel'] as $tunnel) { + if (isset($tunnel['disabled'])) { + continue; + } + if (is_ipaddr($tunnel['remote-gateway'])) { + continue; + } + + if (!is_ipaddr($tunnel['remote-gateway'])) { + $dnscache = compare_hostname_to_dnscache($tunnel['remote-gateway']); + $dnscache = trim($dnscache); + /* we should have the old IP addresses in the dnscache now */ + if($dnscache <> "") { + $oldtunnel = $tunnel; + $oldtunnel['remote-gateway'] = trim($dnscache); + reload_tunnel_spd_policy ($tunnel, $oldtunnel); + } + } + } + } + + /* process all generated spd.conf files from tmp which are left behind + * behind by either changes of dynamic tunnels or manual edits + * scandir() is only available in PHP5 */ + $tmpfiles = array(); + $dh = opendir($g['tmp_path']); + while (false !== ($filename = readdir($dh))) { + $tmpfiles[] = $filename; + } + sort($tmpfiles); + foreach($tmpfiles as $tmpfile) { + if(preg_match("/^spd.conf./", $tmpfile)) { + $ret = mwexec("/usr/local/sbin/setkey -f {$g['tmp_path']}/{$tmpfile} 2>&1", false); + if($ret == 0) { + unlink("{$g['tmp_path']}/{$tmpfile}"); + } else { + rename("{$g['tmp_path']}/{$tmpfile}", ("{$g['tmp_path']}/failed.{$tmpfile}")); + } + unlink("{$g['tmp_path']}/{$tmpfile}"); + } + } +} + +function reload_tunnel_spd_policy($tunnel, $oldtunnel) { + global $config; + global $g; + + /* if we are not passed a old tunnel array we create one */ + if(empty($oldtunnel)) { + $oldtunnel = $tunnel; + } + + $curwanip = get_current_wan_address(); + $sad_arr = return_ipsec_sad_array(); + + $ep = vpn_endpoint_determine($tunnel, $curwanip); + vpn_localnet_determine($tunnel['local-subnet'], $sa, $sn); + + /* make sure we pass the oldtunnel array with a IP for the remote gw */ + $oldgw = trim($oldtunnel['remote-gateway']); + $oldep = vpn_endpoint_determine($oldtunnel, $curwanip); + vpn_localnet_determine($oldtunnel['local-subnet'], $oldsa, $oldsn); + + /* see if this tunnel has a hostname for the remote-gateway, and if so, + * try to resolve it now and add it to the list for dnswatch */ + if (!is_ipaddr($tunnel['remote-gateway'])) { + $rgip = resolve_retry($tunnel['remote-gateway']); + add_hostname_to_watch($tunnel['remote-gateway']); + if (!$rgip) { + log_error("Could not determine VPN endpoint for {$tunnel['descr']}"); + return false; + } + } else { + $rgip = $tunnel['remote-gateway']; + } + if (!$ep) { + log_error("Could not determine VPN endpoint for {$tunnel['descr']}"); + return false; + } + + $spdconf = ""; + + /* Delete old SPD policies if there are changes between the old and new */ + if(($tunnel != $oldtunnel) && (is_ipaddr($oldgw))) { + $spdconf .= "spddelete {$oldsa}/{$oldsn} " . + "{$oldtunnel['remote-subnet']} any -P out ipsec " . + "{$oldtunnel['p2']['protocol']}/tunnel/{$oldep}-" . + "{$oldgw}/unique;\n"; + $spdconf .= "spddelete {$oldtunnel['remote-subnet']} " . + "{$oldsa}/{$oldsn} any -P in ipsec " . + "{$oldtunnel['p2']['protocol']}/tunnel/{$oldgw}-" . + "{$oldep}/unique;\n"; + + /* zap any existing SA entries */ + foreach($sad_arr as $sad) { + if(($sad['dst'] == $oldep) && ($sad['src'] == $oldgw)) { + $spdconf .= "delete {$oldep} {$oldgw} {$tunnel['p2']['protocol']} 0x{$sad['spi']};\n"; + } + if(($sad['src'] == $oldep) && ($sad['dst'] == $oldgw)) { + $spdconf .= "delete {$oldgw} {$oldep} {$tunnel['p2']['protocol']} 0x{$sad['spi']};\n"; + } + } + } + + /* Create new SPD entries for the new configuration */ + /* zap any existing SA entries beforehand */ + foreach($sad_arr as $sad) { + if(($sad['dst'] == $ep) && ($sad['src'] == $rgip)) { + $spdconf .= "delete {$ep} {$rgip} {$tunnel['p2']['protocol']} 0x{$sad['spi']};\n"; + } + if(($sad['src'] == $ep) && ($sad['dst'] == $rgip)) { + $spdconf .= "delete {$rgip} {$ep} {$tunnel['p2']['protocol']} 0x{$sad['spi']};\n"; + } + } + /* add new SPD policies to replace them */ + $spdconf .= "spdadd {$sa}/{$sn} " . + "{$tunnel['remote-subnet']} any -P out ipsec " . + "{$tunnel['p2']['protocol']}/tunnel/{$ep}-" . + "{$rgip}/unique;\n"; + $spdconf .= "spdadd {$tunnel['remote-subnet']} " . + "{$sa}/{$sn} any -P in ipsec " . + "{$tunnel['p2']['protocol']}/tunnel/{$rgip}-" . + "{$ep}/unique;\n"; + + log_error("IPSEC: Tunnel '{$tunnel['descr']}' has changed IP from '{$oldgw}' to '{$rgip}', reloading policy"); + + $now = time(); + $spdfile = tempnam("{$g['tmp_path']}", "spd.conf.reload.{$now}."); + /* generate temporary spd.conf */ + file_put_contents($spdfile, $spdconf); + return true; +} + +/* Dump SAD database to array */ +function return_ipsec_sad_array() { + /* query SAD */ + $fd = @popen("/usr/local/sbin/setkey -D", "r"); + $sad = array(); + if ($fd) { + while (!feof($fd)) { + $line = chop(fgets($fd)); + if (!$line) + continue; + if ($line == "No SAD entries.") + break; + if ($line[0] != "\t") { + if (is_array($cursa)) + $sad[] = $cursa; + $cursa = array(); + list($cursa['src'],$cursa['dst']) = explode(" ", $line); + $i = 0; + } else { + $linea = explode(" ", trim($line)); + if ($i == 1) { + $cursa['proto'] = $linea[0]; + $cursa['spi'] = substr($linea[2], strpos($linea[2], "x")+1, -1); + } else if ($i == 2) { + $cursa['ealgo'] = $linea[1]; + } else if ($i == 3) { + $cursa['aalgo'] = $linea[1]; + } + } + $i++; + } + if (is_array($cursa) && count($cursa)) + $sad[] = $cursa; + pclose($fd); + } + return($sad); +} + ?> diff --git a/etc/rc.newipsecdns b/etc/rc.newipsecdns new file mode 100755 index 0000000000..7621a8eb7d --- /dev/null +++ b/etc/rc.newipsecdns @@ -0,0 +1,52 @@ +#!/usr/local/bin/php -f +. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + + /* parse the configuration and include all functions used below */ + require_once("config.inc"); + require_once("functions.inc"); + + /* make sure to wait until the boot scripts have finished */ + while (file_exists("{$g['varrun_path']}/booting")) { + sleep(1); + } + + log_error("IPSEC: One or more IPSEC tunnel endpoints has changed IP. Refreshing."); + /* We will walk the list of hostnames found in the ipsec tunnel + * configuration. Since we are already triggered by dnswatch + * that a hostname has changed we can proceed to compare the + * new IP address with the old address from the DNS cache. + */ + vpn_ipsec_refresh_policies(); + + vpn_ipsec_configure(); + +?> + diff --git a/usr/local/www/diag_ipsec.php b/usr/local/www/diag_ipsec.php index 30d201b543..e3de673f1b 100644 --- a/usr/local/www/diag_ipsec.php +++ b/usr/local/www/diag_ipsec.php @@ -34,6 +34,50 @@ $pgtitle = "Status: IPsec"; require("guiconfig.inc"); include("head.inc"); + +function get_ipsec_tunnel_src($tunnel) { + global $g, $config, $sad; + $if = "WAN"; + if ($tunnel['interface']) { + $if = $tunnel['interface']; + $realinterface = convert_friendly_interface_to_real_interface_name($if); + $interfaceip = find_interface_ip($realinterface); + } + return $interfaceip; +} + +function output_ipsec_tunnel_status($tunnel) { + global $g, $config, $sad; + $if = "WAN"; + $interfaceip = get_ipsec_tunnel_src($tunnel); + $foundsrc = false; + $founddst = false; + if(!is_ipaddr($tunnel['remote-gateway'])) + $tunnel['remote-gateway'] = resolve_retry($tunnel['remote-gateway']); + + foreach($sad as $sa) { + if($sa['src'] == $interfaceip) + $foundsrc = true; + if($sa['dst'] == $tunnel['remote-gateway']) + $founddst = true; + } + if($foundsrc && $founddst) { + /* tunnel is up */ + $iconfn = "pass"; + } else { + /* tunnel is down */ + $iconfn = "reject"; + } + echo ""; +} + +/* query SAD */ +$sad = return_ipsec_sad_array(); + +if (!is_array($config['ipsec']['tunnel'])) { + $config['ipsec']['tunnel'] = array(); +} + ?> "> @@ -54,47 +98,6 @@ include("head.inc"); -
@@ -162,40 +165,3 @@ foreach ($config['ipsec']['tunnel'] as $ipsec) { - -"; -} - -?> diff --git a/usr/local/www/diag_ipsec_sad.php b/usr/local/www/diag_ipsec_sad.php index 69f7c89c8c..66135f02b7 100755 --- a/usr/local/www/diag_ipsec_sad.php +++ b/usr/local/www/diag_ipsec_sad.php @@ -66,38 +66,8 @@ if ($_GET['act'] == "del") { } /* query SAD */ -$fd = @popen("/usr/local/sbin/setkey -D", "r"); -$sad = array(); -if ($fd) { - while (!feof($fd)) { - $line = chop(fgets($fd)); - if (!$line) - continue; - if ($line == "No SAD entries.") - break; - if ($line[0] != "\t") { - if (is_array($cursa)) - $sad[] = $cursa; - $cursa = array(); - list($cursa['src'],$cursa['dst']) = explode(" ", $line); - $i = 0; - } else { - $linea = explode(" ", trim($line)); - if ($i == 1) { - $cursa['proto'] = $linea[0]; - $cursa['spi'] = substr($linea[2], strpos($linea[2], "x")+1, -1); - } else if ($i == 2) { - $cursa['ealgo'] = $linea[1]; - } else if ($i == 3) { - $cursa['aalgo'] = $linea[1]; - } - } - $i++; - } - if (is_array($cursa) && count($cursa)) - $sad[] = $cursa; - pclose($fd); -} +$sad = return_ipsec_sad_array(); + ?>
diff --git a/usr/local/www/diag_logs_ipsec.php b/usr/local/www/diag_logs_ipsec.php index 0a8b8d1d1b..a6623f6f00 100755 --- a/usr/local/www/diag_logs_ipsec.php +++ b/usr/local/www/diag_logs_ipsec.php @@ -40,6 +40,9 @@ $search = array(); $replace = array(); if(is_array($config['ipsec']['tunnel'])) foreach($config['ipsec']['tunnel'] as $tunnel) { + if(!is_ipaddr($tunnel['remote-gateway'])) + $tunnel['remote-gateway'] = resolve_retry($tunnel['remote-gateway']); + $gateway = "{$tunnel['remote-gateway']}"; $search[] = "/(racoon: )([A-Z:].*?)({$gateway}\[[0-9].+\]|{$gateway})(.*)/i"; $replace[] = "$1[{$tunnel['descr']}]: $2$3$4"; diff --git a/usr/local/www/vpn_ipsec.php b/usr/local/www/vpn_ipsec.php index a4065b38c2..e5894369ab 100755 --- a/usr/local/www/vpn_ipsec.php +++ b/usr/local/www/vpn_ipsec.php @@ -43,7 +43,10 @@ if ($_POST) { if ($_POST['apply']) { $retval = 0; + $retval = vpn_ipsec_refresh_policies(); $retval = vpn_ipsec_configure(); + /* reload the filter in the background */ + filter_configure(); $savemsg = get_std_save_message($retval); if ($retval == 0) { if (file_exists($d_ipsecconfdirty_path)) @@ -59,6 +62,7 @@ if ($_POST) { $retval = 0; config_lock(); + $retval = vpn_ipsec_refresh_policies(); $retval = vpn_ipsec_configure(); config_unlock(); /* reload the filter in the background */ @@ -76,9 +80,11 @@ if ($_GET['act'] == "del") { if ($a_ipsec[$_GET['id']]) { /* remove static route if interface is not WAN */ if($a_ipsec[$_GET['id']]['interface'] <> "wan") { - mwexec("/sbin/route delete -host {$$a_ipsec[$_GET['id']]['remote-gateway']}"); + $oldgw = resolve_retry($a_ipsec[$_GET['id']]['remote-gateway']); + mwexec("/sbin/route delete -host {$oldgw}"); } unset($a_ipsec[$_GET['id']]); + vpn_ipsec_configure(); filter_configure(); write_config(); header("Location: vpn_ipsec.php"); @@ -99,7 +105,8 @@ include("head.inc");

You must apply the changes in order for them to take effect.");?>
+ print_info_box_np("The IPsec tunnel configuration has been changed.
You must apply the changes +in order for them to take effect.");?>

+ Enter the public IP address or hostname of the remote gateway
diff --git a/usr/local/www/vpn_ipsec_edit.php b/usr/local/www/vpn_ipsec_edit.php index aa1810d5fb..5e4959af9c 100755 --- a/usr/local/www/vpn_ipsec_edit.php +++ b/usr/local/www/vpn_ipsec_edit.php @@ -46,6 +46,7 @@ if (isset($_GET['dup'])) { } if (isset($id) && $a_ipsec[$id]) { + $oldipsecent = $a_ipsec[$id]; $pconfig['disabled'] = isset($a_ipsec[$id]['disabled']); $pconfig['auto'] = isset($a_ipsec[$id]['auto']); @@ -61,6 +62,7 @@ if (isset($id) && $a_ipsec[$id]) { list($pconfig['remotenet'],$pconfig['remotebits']) = explode("/", $a_ipsec[$id]['remote-subnet']); $pconfig['remotegw'] = $a_ipsec[$id]['remote-gateway']; + $pconfig['p1mode'] = $a_ipsec[$id]['p1']['mode']; if (isset($a_ipsec[$id]['p1']['myident']['myaddress'])) @@ -209,13 +211,24 @@ if ($_POST) { $ipsecent['interface'] = $pconfig['interface']; pconfig_to_address($ipsecent['local-subnet'], $_POST['localnet'], $_POST['localnetmask']); $ipsecent['remote-subnet'] = $_POST['remotenet'] . "/" . $_POST['remotebits']; - /* if the remote gateway changed and the interface is not WAN then remove route */ - /* the vpn_ipsec_configure() handles adding the route */ - if($_POST['interface'] <> "wan") { - if($ipsecent['remote-gateway'] <> $_POST['remotegw']) { + + /* if the old endpoint is different from the new one we make sure to purge + * the old policy and add a new one. If the old endpoint IP is empty we + * only add new SPD entries. */ + if(!is_ipaddr($oldipsecent['remote-gateway'])) { + $oldipsecent['remote-gateway'] = resolve_retry($oldipsecent['remote-gateway']); + } + if($ipsecent['remote-gateway'] <> $_POST['remotegw']) { + if(!is_ipaddr($ipsecent['remote-gateway'])) { + $ipsecent['remote-gateway'] = resolve_retry($ipsecent['remote-gateway']); + } + /* if the remote gateway changed and the interface is not WAN then remove route */ + /* the vpn_ipsec_configure() handles adding the route */ + if($_POST['interface'] <> "wan") { mwexec("/sbin/route delete -host {$ipsecent['remote-gateway']}"); } } + $ipsecent['remote-gateway'] = $_POST['remotegw']; $ipsecent['p1']['mode'] = $_POST['p1mode']; @@ -261,6 +274,7 @@ if ($_POST) { $a_ipsec[] = $ipsecent; write_config(); + reload_tunnel_spd_policy($ipsecent, $oldipsecent); touch($d_ipsecconfdirty_path); header("Location: vpn_ipsec.php"); @@ -401,7 +415,7 @@ function methodsel_change() {
- Enter the public IP address of the remote gateway
Description