#!/usr/local/bin/php-cgi -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. * * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgment: * "This product includes software developed by the pfSense Project * for use in the pfSense® software distribution. (http://www.pfsense.org/). * * 4. The names "pfSense" and "pfSense Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * coreteam@pfsense.org. * * 5. Products derived from this software may not be called "pfSense" * nor may "pfSense" appear in their names without prior written * permission of the Electric Sheep Fencing, LLC. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * * "This product includes software developed by the pfSense Project * for use in the pfSense software distribution (http://www.pfsense.org/). * * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY * EXPRESSED 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 pfSense PROJECT OR * ITS CONTRIBUTORS 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. */ require_once("globals.inc"); require_once("config.inc"); require_once("functions.inc"); require_once("filter.inc"); require_once("shaper.inc"); require_once("xmlrpc.inc"); require_once("interfaces.inc"); /* * backup_vip_config_section($section): returns as an xml file string of * the configuration section */ function backup_vip_config_section() { global $config; if (!is_array($config['virtualip']['vip'])) { return; } $temp = array(); $temp['vip'] = array(); foreach ($config['virtualip']['vip'] as $section) { if (($section['mode'] == 'proxyarp' || $section['mode'] == 'ipalias') && (strpos($section['interface'], '_vip') === FALSE) && (strpos($section['interface'], 'lo0') === FALSE)) { continue; } if ($section['advskew'] <> "") { $section_val = intval($section['advskew']); $section_val=$section_val+100; if ($section_val > 254) { $section_val = 254; } $section['advskew'] = $section_val; } if ($section['advbase'] <> "") { $section_val = intval($section['advbase']); if ($section_val > 254) { $section_val = 254; } $section['advbase'] = $section_val; } $temp['vip'][] = $section; } return $temp; } function remove_special_characters($string) { $match_array = ""; preg_match_all("/[a-zA-Z0-9\_\-]+/", $string, $match_array); $string = ""; foreach ($match_array[0] as $ma) { if ($string <> "") { $string .= " "; } $string .= $ma; } return $string; } function carp_check_version($url, $username, $password, $port = 80, $method = 'pfsense.host_firmware_version') { global $config, $g; if (file_exists("{$g['varrun_path']}/booting") || platform_booting()) { return; } $params = array( XML_RPC_encode($password) ); $numberofruns = 0; while ($numberofruns < 2) { $msg = new XML_RPC_Message($method, $params); $cli = new XML_RPC_Client('/xmlrpc.php', $url, $port); $cli->setCredentials($username, $password); if ($numberofruns > 0) { $cli->setDebug(1); } /* send our XMLRPC message and timeout after 240 seconds */ $resp = $cli->send($msg, "240"); if (!is_object($resp)) { $error = "A communications error occurred while attempting XMLRPC sync with username {$username} {$url}:{$port}."; } elseif ($resp->faultCode()) { $error = "An error code was received while attempting XMLRPC sync with username {$username} {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString(); } else { $parsed_response = XML_RPC_decode($resp->value()); if (!is_array($parsed_response)) { if (trim($parsed_response) == "Authentication failed") { $error = "An authentication failure occurred while trying to access {$url}:{$port} ({$method})."; log_error($error); file_notice("sync_settings", $error, "Settings Sync", ""); return false; } } else { if (!isset($parsed_response['config_version']) || ($parsed_response['config_version'] < $config['version']) || ($parsed_response['config_version'] > $config['version'])) { update_filter_reload_status("The other member is on a different configuration version of {$g['product_name']}. Sync will not be done to prevent problems!"); log_error("The other member is on a different configuration version of {$g['product_name']}. Sync will not be done to prevent problems!"); return false; } else { return true; } } } log_error($error); file_notice("sync_settings", $error, "Settings Sync", ""); $numberofruns++; } return false; } function carp_sync_xml($url, $username, $password, $sections, $port = 80, $method = 'pfsense.restore_config_section') { global $config, $g; if (file_exists("{$g['varrun_path']}/booting") || platform_booting()) { return; } update_filter_reload_status("Syncing CARP data to {$url}"); /* make a copy of config */ $config_copy = $config; /* strip out nosync items */ if (is_array($config_copy['nat']['outbound']['rule'])) { $rulescnt = count($config_copy['nat']['outbound']['rule']); for ($x = 0; $x < $rulescnt; $x++) { $config_copy['nat']['outbound']['rule'][$x]['descr'] = remove_special_characters($config_copy['nat']['outbound']['rule'][$x]['descr']); if (isset ($config_copy['nat']['outbound']['rule'][$x]['nosync'])) { unset ($config_copy['nat']['outbound']['rule'][$x]); } } } if (is_array($config_copy['nat']['rule'])) { $natcnt = count($config_copy['nat']['rule']); for ($x = 0; $x < $natcnt; $x++) { $config_copy['nat']['rule'][$x]['descr'] = remove_special_characters($config_copy['nat']['rule'][$x]['descr']); if (isset ($config_copy['nat']['rule'][$x]['nosync'])) { unset ($config_copy['nat']['rule'][$x]); } } } if (is_array($config_copy['filter']['rule'])) { $filtercnt = count($config_copy['filter']['rule']); for ($x = 0; $x < $filtercnt; $x++) { $config_copy['filter']['rule'][$x]['descr'] = remove_special_characters($config_copy['filter']['rule'][$x]['descr']); if (isset ($config_copy['filter']['rule'][$x]['nosync'])) { unset ($config_copy['filter']['rule'][$x]); } } } if (is_array($config_copy['aliases']['alias'])) { $aliascnt = count($config_copy['aliases']['alias']); for ($x = 0; $x < $aliascnt; $x++) { $config_copy['aliases']['alias'][$x]['descr'] = remove_special_characters($config_copy['aliases']['alias'][$x]['descr']); if (isset ($config_copy['aliases']['alias'][$x]['nosync'])) { unset ($config_copy['aliases']['alias'][$x]); } } } if (is_array($config_copy['dnsmasq']['hosts'])) { $dnscnt = count($config_copy['dnsmasq']['hosts']); for ($x = 0; $x < $dnscnt; $x++) { $config_copy['dnsmasq']['hosts'][$x]['descr'] = remove_special_characters($config_copy['dnsmasq']['hosts'][$x]['descr']); if (isset ($config_copy['dnsmasq']['hosts'][$x]['nosync'])) { unset ($config_copy['dnsmasq']['hosts'][$x]); } } } if (is_array($config_copy['ipsec']['tunnel'])) { $ipseccnt = count($config_copy['ipsec']['tunnel']); for ($x = 0; $x < $ipseccnt; $x++) { $config_copy['ipsec']['tunnel'][$x]['descr'] = remove_special_characters($config_copy['ipsec']['tunnel'][$x]['descr']); if (isset ($config_copy['ipsec']['tunnel'][$x]['nosync'])) { unset ($config_copy['ipsec']['tunnel'][$x]); } } } if (is_array($config_copy['dhcpd'])) { foreach ($config_copy['dhcpd'] as $dhcpif => $dhcpifconf) { if ($dhcpifconf['failover_peerip'] <> "") { $int = guess_interface_from_ip($dhcpifconf['failover_peerip']); $intip = find_interface_ip($int); $config_copy['dhcpd'][$dhcpif]['failover_peerip'] = $intip; } } } foreach ($sections as $section) { /* we can't use array_intersect_key() * due to the vip 'special case' */ switch ($section) { case 'virtualip': $xml[$section] = backup_vip_config_section(); break; case 'user': $xml['system'][$section] = $config_copy['system'][$section]; $xml['system']['nextuid'] = $config_copy['system']['nextuid']; break; case 'group': $xml['system'][$section] = $config_copy['system'][$section]; $xml['system']['nextgid'] = $config_copy['system']['nextgid']; break; case 'authserver': $xml['system'][$section] = $config_copy['system'][$section]; default: $xml[$section] = $config_copy[$section]; } } $params = array( XML_RPC_encode($password), XML_RPC_encode($xml) ); $numberofruns = 0; while ($numberofruns < 2) { log_error("Beginning XMLRPC sync to {$url}:{$port}."); $msg = new XML_RPC_Message($method, $params); $cli = new XML_RPC_Client('/xmlrpc.php', $url, $port); $cli->setCredentials($username, $password); if ($numberofruns > 0) { $cli->setDebug(1); } /* send our XMLRPC message and timeout after 240 seconds */ $resp = $cli->send($msg, "240"); if (!is_object($resp)) { $error = "A communications error occurred while attempting XMLRPC sync with username {$username} {$url}:{$port}."; log_error($error); file_notice("sync_settings", $error, "Settings Sync", ""); } elseif ($resp->faultCode()) { $error = "An error code was received while attempting XMLRPC sync with username {$username} {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString(); log_error($error); file_notice("sync_settings", $error, "Settings Sync", ""); } else { $parsed_response = XML_RPC_decode($resp->value()); if (!is_array($parsed_response) && trim($parsed_response) == "Authentication failed") { $error = "An authentication failure occurred while trying to access {$url}:{$port} ($method)."; log_error($error); file_notice("sync_settings", $error, "Settings Sync", ""); return -1; } else { log_error("XMLRPC sync successfully completed with {$url}:{$port}."); update_filter_reload_status("XMLRPC sync successfully completed with {$url}:{$port}."); } $numberofruns = 3; } $numberofruns++; } } if (platform_booting()) { return; } if (is_array($config['hasync'])) { update_filter_reload_status("Building high availability sync information"); $hasync = $config['hasync']; if (empty($hasync['synchronizetoip'])) { /* this gets hit on every filter sync on the secondary, a log here creates a lot of log spam and I never saw it actually log anything useful */ return; } /* * XXX: The way we're finding the port right now is really suboptimal - * we can't assume that the other machine is setup identically. */ if (!empty($config['system']['webgui']['protocol'])) { $synchronizetoip = $config['system']['webgui']['protocol']; $synchronizetoip .= "://"; } /* if port is empty lets rely on the protocol selection */ $port = $config['system']['webgui']['port']; if (empty($port)) { if ($config['system']['webgui']['protocol'] == "http") { $port = "80"; } else { $port = "443"; } } if (is_ipaddrv6($hasync['synchronizetoip'])) { $hasync['synchronizetoip'] = "[{$hasync['synchronizetoip']}]"; } $synchronizetoip .= $hasync['synchronizetoip']; if ($hasync['synchronizerules'] != "") { if (!is_array($config['filter'])) { $config['filter'] = array(); } $sections[] = 'filter'; } if ($hasync['synchronizenat'] != "") { if (!is_array($config['nat'])) { $config['nat'] = array(); } $sections[] = 'nat'; } if ($hasync['synchronizealiases'] != "") { if (!is_array($config['aliases'])) { $config['aliases'] = array(); } $sections[] = 'aliases'; } if ($hasync['synchronizedhcpd'] != "" and is_array($config['dhcpd'])) { $sections[] = 'dhcpd'; } if ($hasync['synchronizewol'] != "") { if (!is_array($config['wol'])) { $config['wol'] = array(); } $sections[] = 'wol'; } if ($hasync['synchronizetrafficshaper'] != "" and is_array($config['shaper'])) { $sections[] = 'shaper'; } if ($hasync['synchronizetrafficshaperlimiter'] != "" and is_array($config['dnshaper'])) { $sections[] = 'dnshaper'; } if ($hasync['synchronizestaticroutes'] != "") { if (!is_array($config['staticroutes'])) { $config['staticroutes'] = array(); } if (!is_array($config['staticroutes']['route'])) { $config['staticroutes']['route'] = array(); } $sections[] = 'staticroutes'; if (!is_array($config['gateways'])) { $config['gateways'] = array(); } $sections[] = 'gateways'; } if ($hasync['synchronizevirtualip'] != "") { if (!is_array($config['virtualip'])) { $config['virtualip'] = array(); } $sections[] = 'virtualip'; } if ($hasync['synchronizelb'] != "") { if (!is_array($config['load_balancer'])) { $config['load_balancer'] = array(); } $sections[] = 'load_balancer'; } if ($hasync['synchronizeipsec'] != "") { if (!is_array($config['ipsec'])) { $config['ipsec'] = array(); } $sections[] = 'ipsec'; } if ($hasync['synchronizeopenvpn'] != "") { if (!is_array($config['openvpn'])) { $config['openvpn'] = array(); } $sections[] = 'openvpn'; } if ($hasync['synchronizecerts'] != "" || $hasync['synchronizeopenvpn'] != "") { if (!is_array($config['cert'])) { $config['cert'] = array(); } $sections[] = 'cert'; if (!is_array($config['ca'])) { $config['ca'] = array(); } $sections[] = 'ca'; if (!is_array($config['crl'])) { $config['crl'] = array(); } $sections[] = 'crl'; } if ($hasync['synchronizeusers'] != "") { $sections[] = 'user'; $sections[] = 'group'; } if ($hasync['synchronizeauthservers'] != "") { $sections[] = 'authserver'; } if ($hasync['synchronizednsforwarder'] != "") { if (is_array($config['dnsmasq'])) { $sections[] = 'dnsmasq'; } if (is_array($config['unbound'])) { $sections[] = 'unbound'; } } if ($hasync['synchronizeschedules'] != "" || $hasync['synchronizerules'] != "") { if (!is_array($config['schedules'])) { $config['schedules'] = array(); } $sections[] = 'schedules'; } if ($hasync['synchronizecaptiveportal'] != "" and is_array($config['captiveportal'])) { $sections[] = 'captiveportal'; } if ($hasync['synchronizecaptiveportal'] != "" and is_array($config['vouchers'])) { $sections[] = 'vouchers'; } if (count($sections) <= 0) { log_error("Nothing has been configured to be synched. Skipping...."); return; } if (empty($hasync['username'])) { $username = "admin"; } else { $username = $hasync['username']; } if (!carp_check_version($synchronizetoip, $username, $hasync['password'], $port)) { return; } update_filter_reload_status("Signaling CARP reload signal..."); if (carp_sync_xml($synchronizetoip, $username, $hasync['password'], $sections, $port) == -1) { return; } $cli = new XML_RPC_Client('/xmlrpc.php', $synchronizetoip, $port); $params = array( XML_RPC_encode($hasync['password']) ); $msg = new XML_RPC_Message('pfsense.filter_configure', $params); $cli->setCredentials($username, $hasync['password']); $resp = $cli->send($msg, "900"); if (!is_object($resp)) { $error = "A communications error occurred while attempting Filter sync with username {$username} {$synchronizetoip}:{$port}."; log_error($error); file_notice("sync_settings", $error, "Settings Sync", ""); } elseif ($resp->faultCode()) { $error = "An error code was received while attempting Filter sync with username {$username} {$synchronizetoip}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString(); log_error($error); file_notice("sync_settings", $error, "Settings Sync", ""); } else { log_error("Filter sync successfully completed with {$synchronizetoip}:{$port}."); $numberofruns = 3; } } ?>