Projet

Général

Profil

Télécharger (17,8 ko) Statistiques
| Branche: | Tag: | Révision:

univnautes / etc / inc / vslb.inc @ 34bb5eb0

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 && ($vs_a[$i]['relay_protocol'] != 'dns'))
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 && ($vs_a[$i]['relay_protocol'] != 'dns'))
353
							$conf .= "  forward to <{$vs_a[$i]['sitedown']}> port {$dest_port} {$check_a[$pools[$vs_a[$i]['sitedown']]['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
				/* Remove all active relayd anchors now that relayd is no longer running. */
369
				cleanup_lb_anchor("*");
370
				mwexec("/usr/local/sbin/relayd -f {$g['varetc_path']}/relayd.conf");
371
			} else {
372
				// it's running and there is a config, just reload
373
				mwexec("/usr/local/sbin/relayctl reload");
374
			}
375
		} else {
376
			/*
377
			 * XXX: Something breaks our control connection with relayd
378
			 * and makes 'relayctl stop' not work
379
			 * rule reloads are the current suspect
380
			 * mwexec('/usr/local/sbin/relayctl stop');
381
			 *  returns "command failed"
382
			 */
383
			mwexec('pkill relayd');
384
			/* Remove all active relayd anchors now that relayd is no longer running. */
385
			cleanup_lb_anchor("*");
386
		}
387
	} else {
388
		if (! empty($vs_a)) {
389
			// not running and there is a config, start it
390
			/* Remove all active relayd anchors so it can start fresh. */
391
			cleanup_lb_anchor("*");
392
			mwexec("/usr/local/sbin/relayd -f {$g['varetc_path']}/relayd.conf");
393
		}
394
	}
395
}
396

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

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

    
491
/* Get a list of all relayd virtual server anchors */
492
function get_lb_anchors() {
493
	/* NOTE: These names come back prepended with "relayd/" e.g. "relayd/MyVSName" */
494
	return explode("\n", trim(`/sbin/pfctl -sA -a relayd | /usr/bin/awk '{print $1;}'`));
495
}
496

    
497
/* Remove NAT rules from a relayd anchor that is no longer in use.
498
	$anchorname can either be * to clear all anchors or a specific anchor name.*/
499
function cleanup_lb_anchor($anchorname = "*") {
500
	$lbanchors = get_lb_anchors();
501
	foreach ($lbanchors as $lba) {
502
		if (($anchorname == "*") || ($lba == "relayd/{$anchorname}")) {
503
			/* Flush both the NAT and the Table for the anchor, so it will be completely removed by pf. */
504
			mwexec("/sbin/pfctl -a " . escapeshellarg($lba) . " -F nat");
505
			mwexec("/sbin/pfctl -a " . escapeshellarg($lba) . " -F Tables");
506
		}
507
	}
508
}
509

    
510
/* Mark an anchor for later cleanup. This will allow us to remove an old VS name */
511
function cleanup_lb_mark_anchor($name) {
512
	global $g;
513
	/* Nothing to do! */
514
	if (empty($name))
515
		return;
516
	$filename = "{$g['tmp_path']}/relayd_anchors_remove";
517
	$cleanup_anchors = array();
518
	/* Read in any currently unapplied name changes */
519
	if (file_exists($filename))
520
		$cleanup_anchors = explode("\n", file_get_contents($filename));
521
	/* Only add the anchor to the list if it's not already there. */
522
	if (!in_array($name, $cleanup_anchors))
523
		$cleanup_anchors[] = $name;
524
	file_put_contents($filename, implode("\n", $cleanup_anchors));
525
}
526

    
527
/* Cleanup relayd anchors that have been marked for cleanup. */
528
function cleanup_lb_marked() {
529
	global $g, $config;
530
	$filename = "{$g['tmp_path']}/relayd_anchors_remove";
531
	$cleanup_anchors = array();
532
	/* Nothing to do! */
533
	if (!file_exists($filename)) {
534
		return;
535
	} else {
536
		$cleanup_anchors = explode("\n", file_get_contents($filename));
537
		/* Nothing to do! */
538
		if (empty($cleanup_anchors))
539
			return;
540
	}
541

    
542
	/* Load current names so we can make sure we don't remove an anchor that is still in use. */
543
	$vs_a = $config['load_balancer']['virtual_server'];
544
	$active_vsnames = array();
545
	if(is_array($vs_a)) {
546
		foreach ($vs_a as $vs) {
547
			$active_vsnames[] = $vs['name'];
548
		}
549
	}
550

    
551
	foreach ($cleanup_anchors as $anchor) {
552
		/* Only cleanup an anchor if it is not still active. */
553
		if (!in_array($anchor, $active_vsnames)) {
554
			cleanup_lb_anchor($anchor);
555
		}
556
	}
557
	unlink_if_exists($filename);
558
}
559

    
560
?>
(59-59/67)