Projet

Général

Profil

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

univnautes / etc / inc / vslb.inc @ 340ce958

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

    
37
/* include all configuration functions */
38

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

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

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

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

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

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

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

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

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

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

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

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

    
108

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

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

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

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

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

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

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

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

    
169
	$check_a = array();
170

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
559
?>
(60-60/68)