summaryrefslogtreecommitdiffstats
path: root/src/etc/inc/vslb.inc
diff options
context:
space:
mode:
authorRenato Botelho <renato@netgate.com>2015-08-25 08:08:24 -0300
committerRenato Botelho <renato@netgate.com>2015-08-25 14:49:54 -0300
commit46bc6e545a17e77202aaf01ec0cd8d5a46567525 (patch)
tree32d18dda436ec739c67c489ceb771e8629cd926f /src/etc/inc/vslb.inc
parent4d9801c2dbd2b3e54a39578ee62b93af66607227 (diff)
downloadpfsense-46bc6e545a17e77202aaf01ec0cd8d5a46567525.zip
pfsense-46bc6e545a17e77202aaf01ec0cd8d5a46567525.tar.gz
Move main pfSense content to src/
Diffstat (limited to 'src/etc/inc/vslb.inc')
-rw-r--r--src/etc/inc/vslb.inc564
1 files changed, 564 insertions, 0 deletions
diff --git a/src/etc/inc/vslb.inc b/src/etc/inc/vslb.inc
new file mode 100644
index 0000000..05bef31
--- /dev/null
+++ b/src/etc/inc/vslb.inc
@@ -0,0 +1,564 @@
+<?php
+/* $Id$ */
+/*
+ vslb.inc
+ Copyright (C) 2005-2008 Bill Marquette
+ 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.
+
+ */
+
+/*
+ pfSense_BUILDER_BINARIES: /usr/local/sbin/relayd
+ pfSense_MODULE: routing
+*/
+
+
+/* include all configuration functions */
+
+class Monitor {
+ private $conf = array();
+ function __construct($config) {
+ $this->conf = $config;
+ }
+
+ public function p() {
+ return "check {$this->get('proto')}";
+ }
+ private function get($var) {
+ return isset($this->$var) ? $this->$var : "";
+ }
+ protected function config($element) {
+ return isset($this->conf[$element]) ? $this->conf[$element] : "";
+ }
+}
+
+class TCPMonitor extends Monitor {
+ protected $proto = 'tcp';
+}
+
+class SSLMonitor extends Monitor {
+ protected $proto = 'ssl';
+}
+
+class ICMPMonitor extends Monitor {
+ protected $proto = 'icmp';
+}
+
+class HTTPMonitor extends Monitor {
+ protected $proto = 'http';
+ function __construct($config) {
+ parent::__construct($config);
+ }
+ public function p() {
+ $method = ($this->code() != "") ? $this->code() : $this->digest();
+ return "check {$this->proto} {$this->path()} {$this->host()} {$method}";
+ }
+
+ private function path() {
+ return $this->config('path') != "" ? "'{$this->config('path')}'" : "";
+ }
+
+ private function host() {
+ return $this->config('host') != "" ? "host {$this->config('host')}" : "";
+ }
+
+ private function code() {
+ return $this->config('code') != "" ? "code {$this->config('code')}" : "";
+ }
+
+ private function digest() {
+ return $this->config('digest') != "" ? "digest {$this->config('digest')}" : "";
+ }
+}
+
+class HTTPSMonitor extends HTTPMonitor {
+ protected $proto = 'https';
+}
+
+class SendMonitor extends Monitor {
+ private $proto = 'send';
+ function __construct($config) {
+ parent::__construct($config);
+ }
+ public function p() {
+ return "check {$this->proto} {$this->data()} expect {$this->pattern()} {$this->ssl()}";
+ }
+
+
+ private function data() {
+ return $this->config('send') != "" ? "\"{$this->config('send')}\"" : "\"\"";
+ }
+
+ private function pattern() {
+ return $this->config('expect') != "" ? "\"{$this->config('expect')}\"" : "\"\"";
+ }
+
+ private function ssl() {
+ return $this->config('ssl') == true ? "ssl" : "";
+ }
+}
+
+function echo_lbaction($action) {
+ global $config;
+
+ // Index actions by name
+ $actions_a = array();
+ for ($i = 0; isset($config['load_balancer']['lbaction'][$i]); $i++) {
+ $actions_a[$config['load_balancer']['lbaction'][$i]['name']] = $config['load_balancer']['lbaction'][$i];
+ }
+
+ $ret = "";
+ $ret .= "{$actions_a[$action]['direction']} {$actions_a[$action]['type']} {$actions_a[$action]['action']}";
+ switch ($actions_a[$action]['action']) {
+ case 'append':
+ $ret .= " \"{$actions_a[$action]['options']['value']}\" to \"{$actions_a[$action]['options']['akey']}\"";
+ break;
+ case 'change':
+ $ret .= " \"{$actions_a[$action]['options']['akey']}\" to \"{$actions_a[$action]['options']['value']}\"";
+ break;
+ case 'expect':
+ $ret .= " \"{$actions_a[$action]['options']['value']}\" from \"{$actions_a[$action]['options']['akey']}\"";
+ break;
+ case 'filter':
+ $ret .= " \"{$actions_a[$action]['options']['value']}\" from \"{$actions_a[$action]['options']['akey']}\"";
+ break;
+ case 'hash':
+ $ret .= " \"{$actions_a[$action]['options']['akey']}\"";
+ break;
+ case 'log':
+ $ret .= " \"{$actions_a[$action]['options']['akey']}\"";
+ break;
+ }
+ return $ret;
+}
+
+function relayd_configure($kill_first=false) {
+ global $config, $g;
+
+ // have to do this until every call to filter.inc is
+ // require_once() instead of require().
+ if (!function_exists('filter_expand_alias_array')) {
+ require_once("filter.inc");
+ }
+
+ $vs_a = $config['load_balancer']['virtual_server'];
+ $pool_a = $config['load_balancer']['lbpool'];
+ $protocol_a = $config['load_balancer']['lbprotocol'];
+ $setting = $config['load_balancer']['setting'];
+
+ $check_a = array();
+
+ foreach ((array)$config['load_balancer']['monitor_type'] as $type) {
+ switch ($type['type']) {
+ case 'icmp':
+ $mon = new ICMPMonitor($type['options']);
+ break;
+ case 'tcp':
+ $mon = new TCPMonitor($type['options']);
+ break;
+ case 'http':
+ $mon = new HTTPMonitor($type['options']);
+ break;
+ case 'https':
+ $mon = new HTTPSMonitor($type['options']);
+ break;
+ case 'send':
+ $mon = new SendMonitor($type['options']);
+ break;
+ }
+ if ($mon) {
+ $check_a[$type['name']] = $mon->p();
+ }
+ }
+
+
+ $fd = fopen("{$g['varetc_path']}/relayd.conf", "w");
+ $conf .= "log updates \n";
+
+ /* Global timeout, interval and prefork settings
+ if not specified by the user:
+ - use a 1000 ms timeout value as in pfsense 2.0.1 and above
+ - leave interval and prefork empty, relayd will use its default values */
+
+ if (isset($setting['timeout']) && !empty($setting['timeout'])) {
+ $conf .= "timeout ".$setting['timeout']." \n";
+ } else {
+ $conf .= "timeout 1000 \n";
+ }
+
+ if (isset($setting['interval']) && !empty($setting['interval'])) {
+ $conf .= "interval ".$setting['interval']." \n";
+ }
+
+ if (isset($setting['prefork']) && !empty($setting['prefork'])) {
+ $conf .= "prefork ".$setting['prefork']." \n";
+ }
+
+ /* reindex pools by name as we loop through the pools array */
+ $pools = array();
+ /* Virtual server pools */
+ if (is_array($pool_a)) {
+ for ($i = 0; isset($pool_a[$i]); $i++) {
+ if (is_array($pool_a[$i]['servers'])) {
+ if (!empty($pool_a[$i]['retry'])) {
+ $retrytext = " retry {$pool_a[$i]['retry']}";
+ } else {
+ $retrytext = "";
+ }
+ $conf .= "table <{$pool_a[$i]['name']}> {\n";
+ foreach ($pool_a[$i]['servers'] as $server) {
+ if (is_subnetv4($server)) {
+ foreach (subnetv4_expand($server) as $ip) {
+ $conf .= "\t{$ip}{$retrytext}\n";
+ }
+ } else {
+ $conf .= "\t{$server}{$retrytext}\n";
+ }
+ }
+ $conf .= "}\n";
+ /* Index by name for easier fetching when we loop through the virtual servers */
+ $pools[$pool_a[$i]['name']] = $pool_a[$i];
+ }
+ }
+ }
+// if (is_array($protocol_a)) {
+// for ($i = 0; isset($protocol_a[$i]); $i++) {
+// $proto = "{$protocol_a[$i]['type']} protocol \"{$protocol_a[$i]['name']}\" {\n";
+// if (is_array($protocol_a[$i]['lbaction'])) {
+// if ($protocol_a[$i]['lbaction'][0] == "") {
+// continue;
+// }
+// for ($a = 0; isset($protocol_a[$i]['lbaction'][$a]); $a++) {
+// $proto .= " " . echo_lbaction($protocol_a[$i]['lbaction'][$a]) . "\n";
+// }
+// }
+// $proto .= "}\n";
+// $conf .= $proto;
+// }
+// }
+
+ $conf .= "dns protocol \"dnsproto\" {\n";
+ $conf .= "\t" . "tcp { nodelay, sack, socket buffer 1024, backlog 1000 }\n";
+ $conf .= "}\n";
+
+ if (is_array($vs_a)) {
+ for ($i = 0; isset($vs_a[$i]); $i++) {
+
+ $append_port_to_name = false;
+ if (is_alias($pools[$vs_a[$i]['poolname']]['port'])) {
+ $dest_port_array = filter_expand_alias_array($pools[$vs_a[$i]['poolname']]['port']);
+ $append_port_to_name = true;
+ } else {
+ $dest_port_array = array($pools[$vs_a[$i]['poolname']]['port']);
+ }
+ if (is_alias($vs_a[$i]['port'])) {
+ $src_port_array = filter_expand_alias_array($vs_a[$i]['port']);
+ $append_port_to_name = true;
+ } else if ($vs_a[$i]['port']) {
+ $src_port_array = array($vs_a[$i]['port']);
+ } else {
+ $src_port_array = $dest_port_array;
+ }
+
+ $append_ip_to_name = false;
+ if (is_alias($vs_a[$i]['ipaddr'])) {
+ $ip_list = array();
+ foreach (filter_expand_alias_array($vs_a[$i]['ipaddr']) as $item) {
+ log_error("item is $item");
+ if (is_subnetv4($item)) {
+ $ip_list = array_merge($ip_list, subnetv4_expand($item));
+ } else {
+ $ip_list[] = $item;
+ }
+ }
+ $append_ip_to_name = true;
+ } else if (is_subnetv4($vs_a[$i]['ipaddr'])) {
+ $ip_list = subnetv4_expand($vs_a[$i]['ipaddr']);
+ $append_ip_to_name = true;
+ } else {
+ $ip_list = array($vs_a[$i]['ipaddr']);
+ }
+
+ for ($j = 0; $j < count($ip_list); $j += 1) {
+ $ip = $ip_list[$j];
+ for ($k = 0; $k < count($src_port_array) && $k < count($dest_port_array); $k += 1) {
+ $src_port = $src_port_array[$k];
+ $dest_port = $dest_port_array[$k];
+ if (is_portrange($dest_port)) {
+ $dest_ports = explode(':', $dest_port);
+ $dest_port = $dest_ports[0];
+ }
+
+ $name = $vs_a[$i]['name'];
+ if ($append_ip_to_name) {
+ $name .= "_" . $j;
+ }
+ if ($append_port_to_name) {
+ $name .= "_" . str_replace(":", "_", $src_port);
+ }
+
+ if (($vs_a[$i]['mode'] == 'relay') || ($vs_a[$i]['relay_protocol'] == 'dns')) {
+ $conf .= "relay \"{$name}\" {\n";
+ $conf .= " listen on {$ip} port {$src_port}\n";
+
+ if ($vs_a[$i]['relay_protocol'] == "dns") {
+ $conf .= " protocol \"dnsproto\"\n";
+ } else {
+ $conf .= " protocol \"{$vs_a[$i]['relay_protocol']}\"\n";
+ }
+ $lbmode = "";
+ if ($pools[$vs_a[$i]['poolname']]['mode'] == "loadbalance") {
+ $lbmode = "mode loadbalance";
+ }
+
+ $conf .= " forward to <{$vs_a[$i]['poolname']}> port {$dest_port} {$lbmode} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n";
+
+ if (isset($vs_a[$i]['sitedown']) && strlen($vs_a[$i]['sitedown']) > 0 && ($vs_a[$i]['relay_protocol'] != 'dns')) {
+ $conf .= " forward to <{$vs_a[$i]['sitedown']}> port {$dest_port} {$lbmode} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n";
+ }
+ $conf .= "}\n";
+ } else {
+ $conf .= "redirect \"{$name}\" {\n";
+ $conf .= " listen on {$ip} port {$src_port}\n";
+ $conf .= " forward to <{$vs_a[$i]['poolname']}> port {$dest_port} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n";
+
+ if (isset($config['system']['lb_use_sticky'])) {
+ $conf .= " sticky-address\n";
+ }
+
+ /* sitedown MUST use the same port as the primary pool - sucks, but it's a relayd thing */
+ if (isset($vs_a[$i]['sitedown']) && strlen($vs_a[$i]['sitedown']) > 0 && ($vs_a[$i]['relay_protocol'] != 'dns')) {
+ $conf .= " forward to <{$vs_a[$i]['sitedown']}> port {$dest_port} {$check_a[$pools[$vs_a[$i]['sitedown']]['monitor']]} \n";
+ }
+
+ $conf .= "}\n";
+ }
+ }
+ }
+ }
+ }
+ fwrite($fd, $conf);
+ fclose($fd);
+
+ if (is_process_running('relayd')) {
+ if (!empty($vs_a)) {
+ if ($kill_first) {
+ mwexec('pkill relayd');
+ /* Remove all active relayd anchors now that relayd is no longer running. */
+ cleanup_lb_anchor("*");
+ mwexec("/usr/local/sbin/relayd -f {$g['varetc_path']}/relayd.conf");
+ } else {
+ // it's running and there is a config, just reload
+ mwexec("/usr/local/sbin/relayctl reload");
+ }
+ } else {
+ /*
+ * XXX: Something breaks our control connection with relayd
+ * and makes 'relayctl stop' not work
+ * rule reloads are the current suspect
+ * mwexec('/usr/local/sbin/relayctl stop');
+ * returns "command failed"
+ */
+ mwexec('pkill relayd');
+ /* Remove all active relayd anchors now that relayd is no longer running. */
+ cleanup_lb_anchor("*");
+ }
+ } else {
+ if (!empty($vs_a)) {
+ // not running and there is a config, start it
+ /* Remove all active relayd anchors so it can start fresh. */
+ cleanup_lb_anchor("*");
+ mwexec("/usr/local/sbin/relayd -f {$g['varetc_path']}/relayd.conf");
+ }
+ }
+}
+
+function get_lb_redirects() {
+/*
+# relayctl show summary
+Id Type Name Avlblty Status
+1 redirect testvs2 active
+5 table test2:80 active (3 hosts up)
+11 host 192.168.1.2 91.55% up
+10 host 192.168.1.3 100.00% up
+9 host 192.168.1.4 88.73% up
+3 table test:80 active (1 hosts up)
+7 host 192.168.1.2 66.20% down
+6 host 192.168.1.3 97.18% up
+0 redirect testvs active
+3 table test:80 active (1 hosts up)
+7 host 192.168.1.2 66.20% down
+6 host 192.168.1.3 97.18% up
+4 table testvs-sitedown:80 active (1 hosts up)
+8 host 192.168.1.4 84.51% up
+# relayctl show redirects
+Id Type Name Avlblty Status
+1 redirect testvs2 active
+0 redirect testvs active
+# relayctl show redirects
+Id Type Name Avlblty Status
+1 redirect testvs2 active
+ total: 2 sessions
+ last: 2/60s 2/h 2/d sessions
+ average: 1/60s 0/h 0/d sessions
+0 redirect testvs active
+*/
+ $rdr_a = array();
+ exec('/usr/local/sbin/relayctl show redirects 2>&1', $rdr_a);
+ $relay_a = array();
+ exec('/usr/local/sbin/relayctl show relays 2>&1', $relay_a);
+ $vs = array();
+ $cur_entry = "";
+ for ($i = 0; isset($rdr_a[$i]); $i++) {
+ $line = $rdr_a[$i];
+ if (preg_match("/^[0-9]+/", $line)) {
+ $regs = array();
+ if ($x = preg_match("/^[0-9]+\s+redirect\s+([^\s]+)\s+([^\s]+)/", $line, $regs)) {
+ $cur_entry = trim($regs[1]);
+ $vs[trim($regs[1])] = array();
+ $vs[trim($regs[1])]['status'] = trim($regs[2]);
+ }
+ } elseif (($x = preg_match("/^\s+total:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
+ $vs[$cur_entry]['total'] = trim($regs[1]);
+ } elseif (($x = preg_match("/^\s+last:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
+ $vs[$cur_entry]['last'] = trim($regs[1]);
+ } elseif (($x = preg_match("/^\s+average:(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
+ $vs[$cur_entry]['average'] = trim($regs[1]);
+ }
+ }
+ $cur_entry = "";
+ for ($i = 0; isset($relay_a[$i]); $i++) {
+ $line = $relay_a[$i];
+ if (preg_match("/^[0-9]+/", $line)) {
+ $regs = array();
+ if ($x = preg_match("/^[0-9]+\s+relay\s+([^\s]+)\s+([^\s]+)/", $line, $regs)) {
+ $cur_entry = trim($regs[1]);
+ $vs[trim($regs[1])] = array();
+ $vs[trim($regs[1])]['status'] = trim($regs[2]);
+ }
+ } elseif (($x = preg_match("/^\s+total:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
+ $vs[$cur_entry]['total'] = trim($regs[1]);
+ } elseif (($x = preg_match("/^\s+last:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
+ $vs[$cur_entry]['last'] = trim($regs[1]);
+ } elseif (($x = preg_match("/^\s+average:(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
+ $vs[$cur_entry]['average'] = trim($regs[1]);
+ }
+ }
+ return $vs;
+}
+
+function get_lb_summary() {
+ $relayctl = array();
+ exec('/usr/local/sbin/relayctl show summary 2>&1', $relayctl);
+ $relay_hosts=Array();
+ foreach ((array) $relayctl as $line) {
+ $t = explode("\t", $line);
+ switch (trim($t[1])) {
+ case "table":
+ $curpool=trim($t[2]);
+ break;
+ case "host":
+ $curhost=trim($t[2]);
+ $relay_hosts[$curpool][$curhost]['avail']=trim($t[3]);
+ $relay_hosts[$curpool][$curhost]['state']=trim($t[4]);
+ break;
+ }
+ }
+ return $relay_hosts;
+}
+
+/* Get a list of all relayd virtual server anchors */
+function get_lb_anchors() {
+ /* NOTE: These names come back prepended with "relayd/" e.g. "relayd/MyVSName" */
+ return explode("\n", trim(`/sbin/pfctl -sA -a relayd | /usr/bin/awk '{print $1;}'`));
+}
+
+/* Remove NAT rules from a relayd anchor that is no longer in use.
+ $anchorname can either be * to clear all anchors or a specific anchor name.*/
+function cleanup_lb_anchor($anchorname = "*") {
+ $lbanchors = get_lb_anchors();
+ foreach ($lbanchors as $lba) {
+ if (($anchorname == "*") || ($lba == "relayd/{$anchorname}")) {
+ /* Flush both the NAT and the Table for the anchor, so it will be completely removed by pf. */
+ mwexec("/sbin/pfctl -a " . escapeshellarg($lba) . " -F nat");
+ mwexec("/sbin/pfctl -a " . escapeshellarg($lba) . " -F Tables");
+ }
+ }
+}
+
+/* Mark an anchor for later cleanup. This will allow us to remove an old VS name */
+function cleanup_lb_mark_anchor($name) {
+ global $g;
+ /* Nothing to do! */
+ if (empty($name)) {
+ return;
+ }
+ $filename = "{$g['tmp_path']}/relayd_anchors_remove";
+ $cleanup_anchors = array();
+ /* Read in any currently unapplied name changes */
+ if (file_exists($filename)) {
+ $cleanup_anchors = explode("\n", file_get_contents($filename));
+ }
+ /* Only add the anchor to the list if it's not already there. */
+ if (!in_array($name, $cleanup_anchors)) {
+ $cleanup_anchors[] = $name;
+ }
+ file_put_contents($filename, implode("\n", $cleanup_anchors));
+}
+
+/* Cleanup relayd anchors that have been marked for cleanup. */
+function cleanup_lb_marked() {
+ global $g, $config;
+ $filename = "{$g['tmp_path']}/relayd_anchors_remove";
+ $cleanup_anchors = array();
+ /* Nothing to do! */
+ if (!file_exists($filename)) {
+ return;
+ } else {
+ $cleanup_anchors = explode("\n", file_get_contents($filename));
+ /* Nothing to do! */
+ if (empty($cleanup_anchors)) {
+ return;
+ }
+ }
+
+ /* Load current names so we can make sure we don't remove an anchor that is still in use. */
+ $vs_a = $config['load_balancer']['virtual_server'];
+ $active_vsnames = array();
+ if (is_array($vs_a)) {
+ foreach ($vs_a as $vs) {
+ $active_vsnames[] = $vs['name'];
+ }
+ }
+
+ foreach ($cleanup_anchors as $anchor) {
+ /* Only cleanup an anchor if it is not still active. */
+ if (!in_array($anchor, $active_vsnames)) {
+ cleanup_lb_anchor($anchor);
+ }
+ }
+ unlink_if_exists($filename);
+}
+
+?>
OpenPOWER on IntegriCloud