Project

General

Profile

Download (15.1 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/* $Id$ */
3
/*
4
  vslb.inc
5
  Copyright (C) 2005-2008 Bill Marquette
6
  All rights reserved.
7

    
8
  Redistribution and use in source and binary forms, with or without
9
  modification, are permitted provided that the following conditions are met:
10

    
11
  1. Redistributions of source code must retain the above copyright notice,
12
  this list of conditions and the following disclaimer.
13

    
14
  2. Redistributions in binary form must reproduce the above copyright
15
  notice, this list of conditions and the following disclaimer in the
16
  documentation and/or other materials provided with the distribution.
17

    
18
  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
19
  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20
  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21
  AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22
  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
  POSSIBILITY OF SUCH DAMAGE.
28

    
29
 */
30

    
31
/*
32
	pfSense_BUILDER_BINARIES:	/usr/local/sbin/relayd
33
	pfSense_MODULE:	routing
34
*/
35

    
36
/* DISABLE_PHP_LINT_CHECKING */
37

    
38
/* include all configuration functions */
39

    
40
class Monitor {
41
	private $conf = array();
42
	function __construct($config) {
43
		$this->conf = $config;
44
	}
45

    
46
	public function p() {
47
		return "check {$this->get('proto')}";
48
	}
49
	private function get($var) {
50
		return isset($this->$var) ? $this->$var : "";
51
	}
52
	protected function config($element) {
53
		return isset($this->conf[$element]) ? $this->conf[$element] : "";
54
	}
55
}
56

    
57
class TCPMonitor extends Monitor {
58
	protected $proto = 'tcp';
59
}
60

    
61
class SSLMonitor extends Monitor {
62
	protected $proto = 'ssl';
63
}
64

    
65
class ICMPMonitor extends Monitor {
66
	protected $proto = 'icmp';
67
}
68

    
69
class HTTPMonitor extends Monitor {
70
	protected $proto = 'http';
71
	function __construct($config) {
72
		parent::__construct($config);
73
	}
74
	public function p() {
75
		$method = ($this->code() != "") ? $this->code() : $this->digest();
76
		return "check {$this->proto} {$this->path()} {$this->host()} {$method}";
77
	}
78

    
79
	private function path() {
80
		return $this->config('path') != "" ? "'{$this->config('path')}'" : "";
81
	}
82

    
83
	private function host() {
84
		return $this->config('host') != "" ? "host {$this->config('host')}" : "";
85
	}
86

    
87
	private function code() {
88
		return $this->config('code') != "" ? "code {$this->config('code')}" : "";
89
	}
90

    
91
	private function digest() {
92
		return $this->config('digest') != "" ? "digest {$this->config('digest')}" : "";
93
	}
94
}
95

    
96
class HTTPSMonitor extends HTTPMonitor {
97
	protected $proto = 'https';
98
}
99

    
100
class SendMonitor extends Monitor {
101
	private $proto = 'send';
102
	function __construct($config) {
103
		parent::__construct($config);
104
	}
105
	public function p() {
106
		return "check {$this->proto} {$this->data()} expect {$this->pattern()} {$this->ssl()}";
107
	}
108

    
109

    
110
	private function data() {
111
		return $this->config('send') != "" ? "\"{$this->config('send')}\"" : "\"\"";
112
	}
113

    
114
	private function pattern() {
115
		return $this->config('expect') != "" ? "\"{$this->config('expect')}\"" : "\"\"";
116
	}
117

    
118
	private function ssl() {
119
		return $this->config('ssl') == true ? "ssl" : "";
120
	}
121
}
122

    
123
function echo_lbaction($action) {
124
	global $config;
125
  
126
	// Index actions by name
127
	$actions_a = array();
128
	for ($i=0; isset($config['load_balancer']['lbaction'][$i]); $i++)
129
		$actions_a[$config['load_balancer']['lbaction'][$i]['name']] = $config['load_balancer']['lbaction'][$i];
130

    
131
	$ret = "";
132
	$ret .=  "{$actions_a[$action]['direction']} {$actions_a[$action]['type']} {$actions_a[$action]['action']}";
133
	switch($actions_a[$action]['action']) {
134
	case 'append':
135
		$ret .= " \"{$actions_a[$action]['options']['value']}\" to \"{$actions_a[$action]['options']['akey']}\"";
136
		break;
137
	case 'change':
138
		$ret .= " \"{$actions_a[$action]['options']['akey']}\" to \"{$actions_a[$action]['options']['value']}\"";
139
		break;
140
	case 'expect':
141
		$ret .= " \"{$actions_a[$action]['options']['value']}\" from \"{$actions_a[$action]['options']['akey']}\"";
142
		break;
143
	case 'filter':
144
		$ret .= " \"{$actions_a[$action]['options']['value']}\" from \"{$actions_a[$action]['options']['akey']}\"";
145
		break;
146
	case 'hash':
147
		$ret .= " \"{$actions_a[$action]['options']['akey']}\"";
148
		break;
149
	case 'log':
150
		$ret .= " \"{$actions_a[$action]['options']['akey']}\"";
151
		break;
152
	}
153
	return $ret;
154
}
155

    
156
function relayd_configure($kill_first=false) {
157
	global $config, $g;
158

    
159
	// have to do this until every call to filter.inc is
160
	// require_once() instead of require().
161
	if (!function_exists('filter_expand_alias_array')) {
162
		require_once("filter.inc");
163
	}
164

    
165
	$vs_a = $config['load_balancer']['virtual_server'];
166
	$pool_a = $config['load_balancer']['lbpool'];
167
	$protocol_a = $config['load_balancer']['lbprotocol'];
168
	$setting = $config['load_balancer']['setting'];
169

    
170
	$check_a = array();
171

    
172
	foreach ((array)$config['load_balancer']['monitor_type'] as $type) {
173
		switch($type['type']) {
174
		case 'icmp':
175
			$mon = new ICMPMonitor($type['options']);
176
			break;
177
		case 'tcp':
178
			$mon = new TCPMonitor($type['options']);
179
			break;
180
		case 'http':
181
			$mon = new HTTPMonitor($type['options']);
182
			break;
183
		case 'https':
184
			$mon = new HTTPSMonitor($type['options']);
185
			break;
186
		case 'send':
187
			$mon = new SendMonitor($type['options']);
188
			break;
189
		}
190
		if($mon) {
191
			$check_a[$type['name']] = $mon->p();
192
		}
193
	}
194
	
195
  
196
	$fd = fopen("{$g['varetc_path']}/relayd.conf", "w");
197
	$conf .= "log updates \n";
198

    
199
	/* Global timeout, interval and prefork settings
200
	   if not specified by the user:
201
	   - use a 1000 ms timeout value as in pfsense 2.0.1 and above
202
	   - leave interval and prefork empty, relayd will use its default values */
203
	
204
	if (isset($setting['timeout']) && !empty($setting['timeout'])) {
205
		$conf .= "timeout ".$setting['timeout']." \n";
206
	} else {
207
		$conf .= "timeout 1000 \n";
208
	}
209
	
210
	if (isset($setting['interval']) && !empty($setting['interval'])) {
211
		$conf .= "interval ".$setting['interval']." \n";
212
	}
213
	
214
	if (isset($setting['prefork']) && !empty($setting['prefork'])) {
215
		$conf .= "prefork ".$setting['prefork']." \n";
216
	}
217
	
218
	/* reindex pools by name as we loop through the pools array */
219
	$pools = array();
220
	/* Virtual server pools */
221
	if(is_array($pool_a)) {
222
		for ($i = 0; isset($pool_a[$i]); $i++) {
223
			if(is_array($pool_a[$i]['servers'])) {
224
				if (!empty($pool_a[$i]['retry'])) {
225
					$retrytext = " retry {$pool_a[$i]['retry']}";
226
				} else {
227
					$retrytext = "";
228
				}
229
				$conf .= "table <{$pool_a[$i]['name']}> {\n";
230
				foreach ($pool_a[$i]['servers'] as $server) {
231
					if (is_subnetv4($server)) {
232
						foreach (subnetv4_expand($server) as $ip) {
233
							$conf .= "\t{$ip}{$retrytext}\n";
234
						}
235
					}
236
					else {
237
						$conf .= "\t{$server}{$retrytext}\n";
238
					}
239
				}
240
				$conf .= "}\n";
241
				/* Index by name for easier fetching when we loop through the virtual servers */
242
				$pools[$pool_a[$i]['name']] = $pool_a[$i];
243
			}
244
		}
245
	}
246
//  if(is_array($protocol_a)) {
247
//    for ($i = 0; isset($protocol_a[$i]); $i++) {
248
//      $proto = "{$protocol_a[$i]['type']} protocol \"{$protocol_a[$i]['name']}\" {\n";
249
//      if(is_array($protocol_a[$i]['lbaction'])) {
250
//        if($protocol_a[$i]['lbaction'][0] == "") {
251
//          continue;
252
//        }
253
//        for ($a = 0; isset($protocol_a[$i]['lbaction'][$a]); $a++) {
254
//          $proto .= "  " . echo_lbaction($protocol_a[$i]['lbaction'][$a]) . "\n";
255
//        }
256
//      }
257
//      $proto .= "}\n";
258
//      $conf .= $proto;
259
//    }
260
//  }
261

    
262
	$conf .= "dns protocol \"dnsproto\" {\n";
263
	$conf .= "\t" . "tcp { nodelay, sack, socket buffer 1024, backlog 1000 }\n";
264
	$conf .= "}\n";
265

    
266
	if(is_array($vs_a)) {
267
		for ($i = 0; isset($vs_a[$i]); $i++) {
268
			
269
			$append_port_to_name = false;
270
			if (is_alias($pools[$vs_a[$i]['poolname']]['port'])) {
271
				$dest_port_array = filter_expand_alias_array($pools[$vs_a[$i]['poolname']]['port']);
272
				$append_port_to_name = true;
273
			}
274
			else {
275
				$dest_port_array = array($pools[$vs_a[$i]['poolname']]['port']);
276
			}
277
			if (is_alias($vs_a[$i]['port'])) {
278
				$src_port_array = filter_expand_alias_array($vs_a[$i]['port']);
279
				$append_port_to_name = true;
280
			}
281
			else if ($vs_a[$i]['port']) {
282
				$src_port_array = array($vs_a[$i]['port']);
283
			}
284
			else {
285
				$src_port_array = $dest_port_array;
286
			}
287

    
288
			$append_ip_to_name = false;
289
			if (is_alias($vs_a[$i]['ipaddr'])) {
290
				$ip_list = array();
291
				foreach (filter_expand_alias_array($vs_a[$i]['ipaddr']) as $item) {
292
					log_error("item is $item");
293
					if (is_subnetv4($item)) {
294
						$ip_list = array_merge($ip_list, subnetv4_expand($item));
295
					}
296
					else {
297
						$ip_list[] = $item;
298
					}
299
				}
300
				$append_ip_to_name = true;
301
			}
302
			else if (is_subnetv4($vs_a[$i]['ipaddr'])) {
303
				$ip_list = subnetv4_expand($vs_a[$i]['ipaddr']);
304
				$append_ip_to_name = true;
305
			}
306
			else {
307
				$ip_list = array($vs_a[$i]['ipaddr']);
308
			}
309

    
310
			for ($j = 0; $j < count($ip_list); $j += 1) {
311
				$ip = $ip_list[$j];
312
				for ($k = 0; $k < count($src_port_array) && $k < count($dest_port_array); $k += 1) {
313
					$src_port  = $src_port_array[$k];
314
					$dest_port = $dest_port_array[$k];
315

    
316
					$name = $vs_a[$i]['name'];
317
					if ($append_ip_to_name) {
318
						$name .= "_" . $j;
319
					}
320
					if ($append_port_to_name) {
321
						$name .= "_" . $src_port;
322
					}
323

    
324
					if (($vs_a[$i]['mode'] == 'relay') || ($vs_a[$i]['relay_protocol'] == 'dns')) {
325
						$conf .= "relay \"{$name}\" {\n";
326
						$conf .= "  listen on {$ip} port {$src_port}\n";
327

    
328
						if ($vs_a[$i]['relay_protocol'] == "dns") {
329
							$conf .= "  protocol \"dnsproto\"\n";
330
						} else {
331
							$conf .= "  protocol \"{$vs_a[$i]['relay_protocol']}\"\n";
332
						}
333
						$lbmode = "";
334
						if ( $pools[$vs_a[$i]['poolname']]['mode'] == "loadbalance" ) {
335
							$lbmode = "mode loadbalance";
336
						}
337

    
338
						$conf .= "  forward to <{$vs_a[$i]['poolname']}> port {$dest_port} {$lbmode} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n";
339

    
340
						if (isset($vs_a[$i]['sitedown']) &&  strlen($vs_a[$i]['sitedown']) > 0)
341
							$conf .= "  forward to <{$vs_a[$i]['sitedown']}> port {$dest_port} {$lbmode} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n";
342
						$conf .= "}\n";
343
					} else  {
344
						$conf .= "redirect \"{$name}\" {\n";
345
						$conf .= "  listen on {$ip} port {$src_port}\n";
346
						$conf .= "  forward to <{$vs_a[$i]['poolname']}> port {$dest_port} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n";
347

    
348
						if (isset($config['system']['lb_use_sticky']))
349
							$conf .= "  sticky-address\n";
350

    
351
						/* sitedown MUST use the same port as the primary pool - sucks, but it's a relayd thing */
352
						if (isset($vs_a[$i]['sitedown']) && strlen($vs_a[$i]['sitedown']) > 0)
353
							$conf .= "  forward to <{$vs_a[$i]['sitedown']}> port {$dest_port} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n";
354

    
355
						$conf .= "}\n";
356
					}
357
				}
358
			}
359
		}
360
	}
361
	fwrite($fd, $conf);
362
	fclose($fd);
363

    
364
	if (is_process_running('relayd')) {
365
		if (! empty($vs_a)) {
366
			if ($kill_first) {
367
				mwexec('pkill relayd');
368
				mwexec("/usr/local/sbin/relayd -f {$g['varetc_path']}/relayd.conf");
369
			} else {
370
				// it's running and there is a config, just reload
371
				mwexec("/usr/local/sbin/relayctl reload");
372
			}
373
		} else {
374
			/*
375
			 * XXX: Something breaks our control connection with relayd
376
			 * and makes 'relayctl stop' not work
377
			 * rule reloads are the current suspect
378
			 * mwexec('/usr/local/sbin/relayctl stop');
379
			 *  returns "command failed"
380
			 */
381
			mwexec('pkill relayd');
382
		}
383
	} else {
384
		if (! empty($vs_a)) {
385
			// not running and there is a config, start it
386
			mwexec("/usr/local/sbin/relayd -f {$g['varetc_path']}/relayd.conf");
387
		}
388
	}
389
}
390

    
391
function get_lb_redirects() {
392
/*
393
# relayctl show summary
394
Id   Type      Name                      Avlblty Status
395
1    redirect  testvs2                           active
396
5    table     test2:80                          active (3 hosts up)
397
11   host      192.168.1.2               91.55%  up
398
10   host      192.168.1.3               100.00% up
399
9    host      192.168.1.4               88.73%  up
400
3    table     test:80                           active (1 hosts up)
401
7    host      192.168.1.2               66.20%  down
402
6    host      192.168.1.3               97.18%  up
403
0    redirect  testvs                            active
404
3    table     test:80                           active (1 hosts up)
405
7    host      192.168.1.2               66.20%  down
406
6    host      192.168.1.3               97.18%  up
407
4    table     testvs-sitedown:80                active (1 hosts up)
408
8    host      192.168.1.4               84.51%  up
409
# relayctl show redirects
410
Id   Type      Name                      Avlblty Status
411
1    redirect  testvs2                           active
412
0    redirect  testvs                            active
413
# relayctl show redirects
414
Id   Type      Name                      Avlblty Status
415
1    redirect  testvs2                           active
416
           total: 2 sessions
417
           last: 2/60s 2/h 2/d sessions
418
           average: 1/60s 0/h 0/d sessions
419
0    redirect  testvs                            active
420
*/
421
	$rdr_a = array();
422
	exec('/usr/local/sbin/relayctl show redirects 2>&1', $rdr_a);
423
	$relay_a = array();
424
	exec('/usr/local/sbin/relayctl show relays 2>&1', $relay_a);
425
	$vs = array();
426
	$cur_entry = "";
427
	for ($i = 0; isset($rdr_a[$i]); $i++) {
428
		$line = $rdr_a[$i];
429
		if (preg_match("/^[0-9]+/", $line)) {
430
			$regs = array();
431
			if($x = preg_match("/^[0-9]+\s+redirect\s+([^\s]+)\s+([^\s]+)/", $line, $regs)) {
432
				$cur_entry = trim($regs[1]);
433
				$vs[trim($regs[1])] = array();
434
				$vs[trim($regs[1])]['status'] = trim($regs[2]);
435
			}
436
		} elseif (($x = preg_match("/^\s+total:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
437
			$vs[$cur_entry]['total'] = trim($regs[1]);
438
		} elseif (($x = preg_match("/^\s+last:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
439
			$vs[$cur_entry]['last'] = trim($regs[1]);
440
		} elseif (($x = preg_match("/^\s+average:(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
441
			$vs[$cur_entry]['average'] = trim($regs[1]);
442
		}
443
	}
444
	$cur_entry = "";
445
	for ($i = 0; isset($relay_a[$i]); $i++) {
446
		$line = $relay_a[$i];
447
		if (preg_match("/^[0-9]+/", $line)) {
448
			$regs = array();
449
			if($x = preg_match("/^[0-9]+\s+relay\s+([^\s]+)\s+([^\s]+)/", $line, $regs)) {
450
				$cur_entry = trim($regs[1]);
451
				$vs[trim($regs[1])] = array();
452
				$vs[trim($regs[1])]['status'] = trim($regs[2]);
453
			}
454
		} elseif (($x = preg_match("/^\s+total:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
455
			$vs[$cur_entry]['total'] = trim($regs[1]);
456
		} elseif (($x = preg_match("/^\s+last:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
457
			$vs[$cur_entry]['last'] = trim($regs[1]);
458
		} elseif (($x = preg_match("/^\s+average:(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
459
			$vs[$cur_entry]['average'] = trim($regs[1]);
460
		}
461
	}
462
	return $vs;
463
}
464

    
465
function get_lb_summary() {
466
	$relayctl = array();
467
	exec('/usr/local/sbin/relayctl show summary 2>&1', $relayctl);
468
	$relay_hosts=Array();
469
	foreach( (array) $relayctl as $line) {
470
		$t = explode("\t", $line);
471
		switch (trim($t[1])) {
472
			case "table":
473
				$curpool=trim($t[2]);
474
				break;
475
			case "host":
476
				$curhost=trim($t[2]);
477
				$relay_hosts[$curpool][$curhost]['avail']=trim($t[3]);
478
				$relay_hosts[$curpool][$curhost]['state']=trim($t[4]);
479
				break;
480
		}
481
	}
482
	return $relay_hosts;
483
}
484

    
485
?>
(58-58/66)