Project

General

Profile

Download (18.9 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	vslb.inc
4

    
5
	part of pfSense (https://www.pfsense.org)
6
	Copyright (C) 2005-2008 Bill Marquette
7
	Copyright (c) 2005-2016 Electric Sheep Fencing, LLC.
8
	All rights reserved.
9

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

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

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

    
21
	3. All advertising materials mentioning features or use of this software
22
	   must display the following acknowledgment:
23
	   "This product includes software developed by the pfSense Project
24
	   for use in the pfSense® software distribution. (http://www.pfsense.org/).
25

    
26
	4. The names "pfSense" and "pfSense Project" must not be used to
27
	   endorse or promote products derived from this software without
28
	   prior written permission. For written permission, please contact
29
	   coreteam@pfsense.org.
30

    
31
	5. Products derived from this software may not be called "pfSense"
32
	   nor may "pfSense" appear in their names without prior written
33
	   permission of the Electric Sheep Fencing, LLC.
34

    
35
	6. Redistributions of any form whatsoever must retain the following
36
	   acknowledgment:
37

    
38
	"This product includes software developed by the pfSense Project
39
	for use in the pfSense software distribution (http://www.pfsense.org/).
40

    
41
	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
42
	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
44
	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
45
	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46
	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
47
	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
48
	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49
	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
50
	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
51
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
52
	OF THE POSSIBILITY OF SUCH DAMAGE.
53
 */
54

    
55
/* include all configuration functions */
56

    
57
class Monitor {
58
	private $conf = array();
59
	function __construct($config) {
60
		$this->conf = $config;
61
	}
62

    
63
	public function p() {
64
		return "check {$this->get('proto')}";
65
	}
66
	private function get($var) {
67
		return isset($this->$var) ? $this->$var : "";
68
	}
69
	protected function config($element) {
70
		return isset($this->conf[$element]) ? $this->conf[$element] : "";
71
	}
72
}
73

    
74
class TCPMonitor extends Monitor {
75
	protected $proto = 'tcp';
76
}
77

    
78
class SSLMonitor extends Monitor {
79
	protected $proto = 'ssl';
80
}
81

    
82
class ICMPMonitor extends Monitor {
83
	protected $proto = 'icmp';
84
}
85

    
86
class HTTPMonitor extends Monitor {
87
	protected $proto = 'http';
88
	function __construct($config) {
89
		parent::__construct($config);
90
	}
91
	public function p() {
92
		$method = ($this->code() != "") ? $this->code() : $this->digest();
93
		return "check {$this->proto} {$this->path()} {$this->host()} {$method}";
94
	}
95

    
96
	private function path() {
97
		return $this->config('path') != "" ? "'{$this->config('path')}'" : "";
98
	}
99

    
100
	private function host() {
101
		return $this->config('host') != "" ? "host {$this->config('host')}" : "";
102
	}
103

    
104
	private function code() {
105
		return $this->config('code') != "" ? "code {$this->config('code')}" : "";
106
	}
107

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

    
113
class HTTPSMonitor extends HTTPMonitor {
114
	protected $proto = 'https';
115
}
116

    
117
class SendMonitor extends Monitor {
118
	private $proto = 'send';
119
	function __construct($config) {
120
		parent::__construct($config);
121
	}
122
	public function p() {
123
		return "check {$this->proto} {$this->data()} expect {$this->pattern()} {$this->ssl()}";
124
	}
125

    
126

    
127
	private function data() {
128
		return $this->config('send') != "" ? "\"{$this->config('send')}\"" : "\"\"";
129
	}
130

    
131
	private function pattern() {
132
		return $this->config('expect') != "" ? "\"{$this->config('expect')}\"" : "\"\"";
133
	}
134

    
135
	private function ssl() {
136
		return $this->config('ssl') == true ? "ssl" : "";
137
	}
138
}
139

    
140
function echo_lbaction($action) {
141
	global $config;
142

    
143
	// Index actions by name
144
	$actions_a = array();
145
	for ($i = 0; isset($config['load_balancer']['lbaction'][$i]); $i++) {
146
		$actions_a[$config['load_balancer']['lbaction'][$i]['name']] = $config['load_balancer']['lbaction'][$i];
147
	}
148

    
149
	$ret = "";
150
	$ret .= "{$actions_a[$action]['direction']} {$actions_a[$action]['type']} {$actions_a[$action]['action']}";
151
	switch ($actions_a[$action]['action']) {
152
		case 'append':
153
			$ret .= " \"{$actions_a[$action]['options']['value']}\" to \"{$actions_a[$action]['options']['akey']}\"";
154
			break;
155
		case 'change':
156
			$ret .= " \"{$actions_a[$action]['options']['akey']}\" to \"{$actions_a[$action]['options']['value']}\"";
157
			break;
158
		case 'expect':
159
			$ret .= " \"{$actions_a[$action]['options']['value']}\" from \"{$actions_a[$action]['options']['akey']}\"";
160
			break;
161
		case 'filter':
162
			$ret .= " \"{$actions_a[$action]['options']['value']}\" from \"{$actions_a[$action]['options']['akey']}\"";
163
			break;
164
		case 'hash':
165
			$ret .= " \"{$actions_a[$action]['options']['akey']}\"";
166
			break;
167
		case 'log':
168
			$ret .= " \"{$actions_a[$action]['options']['akey']}\"";
169
			break;
170
	}
171
	return $ret;
172
}
173

    
174
function relayd_configure($kill_first=false) {
175
	global $config, $g;
176

    
177
	// have to do this until every call to filter.inc is
178
	// require_once() instead of require().
179
	if (!function_exists('filter_expand_alias_array')) {
180
		require_once("filter.inc");
181
	}
182

    
183
	$vs_a = $config['load_balancer']['virtual_server'];
184
	$pool_a = $config['load_balancer']['lbpool'];
185
	$protocol_a = $config['load_balancer']['lbprotocol'];
186
	$setting = $config['load_balancer']['setting'];
187

    
188
	$check_a = array();
189

    
190
	foreach ((array)$config['load_balancer']['monitor_type'] as $type) {
191
		switch ($type['type']) {
192
			case 'icmp':
193
				$mon = new ICMPMonitor($type['options']);
194
				break;
195
			case 'tcp':
196
				$mon = new TCPMonitor($type['options']);
197
				break;
198
			case 'http':
199
				$mon = new HTTPMonitor($type['options']);
200
				break;
201
			case 'https':
202
				$mon = new HTTPSMonitor($type['options']);
203
				break;
204
			case 'send':
205
				$mon = new SendMonitor($type['options']);
206
				break;
207
		}
208
		if ($mon) {
209
			$check_a[$type['name']] = $mon->p();
210
		}
211
	}
212

    
213

    
214
	$fd = fopen("{$g['varetc_path']}/relayd.conf", "w");
215
	$conf .= "log updates \n";
216

    
217
	/* Global timeout, interval and prefork settings
218
	   if not specified by the user:
219
	   - use a 1000 ms timeout value as in pfsense 2.0.1 and above
220
	   - leave interval and prefork empty, relayd will use its default values */
221

    
222
	if (isset($setting['timeout']) && !empty($setting['timeout'])) {
223
		$conf .= "timeout ".$setting['timeout']." \n";
224
	} else {
225
		$conf .= "timeout 1000 \n";
226
	}
227

    
228
	if (isset($setting['interval']) && !empty($setting['interval'])) {
229
		$conf .= "interval ".$setting['interval']." \n";
230
	}
231

    
232
	if (isset($setting['prefork']) && !empty($setting['prefork'])) {
233
		$conf .= "prefork ".$setting['prefork']." \n";
234
	}
235

    
236
	/* reindex pools by name as we loop through the pools array */
237
	$pools = array();
238
	/* Virtual server pools */
239
	if (is_array($pool_a)) {
240
		for ($i = 0; isset($pool_a[$i]); $i++) {
241
			if (is_array($pool_a[$i]['servers'])) {
242
				if (!empty($pool_a[$i]['retry'])) {
243
					$retrytext = " retry {$pool_a[$i]['retry']}";
244
				} else {
245
					$retrytext = "";
246
				}
247
				$conf .= "table <{$pool_a[$i]['name']}> {\n";
248
				foreach ($pool_a[$i]['servers'] as $server) {
249
					if (is_subnetv4($server)) {
250
						foreach (subnetv4_expand($server) as $ip) {
251
							$conf .= "\t{$ip}{$retrytext}\n";
252
						}
253
					} else {
254
						$conf .= "\t{$server}{$retrytext}\n";
255
					}
256
				}
257
				$conf .= "}\n";
258
				/* Index by name for easier fetching when we loop through the virtual servers */
259
				$pools[$pool_a[$i]['name']] = $pool_a[$i];
260
			}
261
		}
262
	}
263
//  if (is_array($protocol_a)) {
264
//    for ($i = 0; isset($protocol_a[$i]); $i++) {
265
//      $proto = "{$protocol_a[$i]['type']} protocol \"{$protocol_a[$i]['name']}\" {\n";
266
//      if (is_array($protocol_a[$i]['lbaction'])) {
267
//        if ($protocol_a[$i]['lbaction'][0] == "") {
268
//          continue;
269
//        }
270
//        for ($a = 0; isset($protocol_a[$i]['lbaction'][$a]); $a++) {
271
//          $proto .= "  " . echo_lbaction($protocol_a[$i]['lbaction'][$a]) . "\n";
272
//        }
273
//      }
274
//      $proto .= "}\n";
275
//      $conf .= $proto;
276
//    }
277
//  }
278

    
279
	$conf .= "dns protocol \"dnsproto\" {\n";
280
	$conf .= "\t" . "tcp { nodelay, sack, socket buffer 1024, backlog 1000 }\n";
281
	$conf .= "}\n";
282

    
283
	if (is_array($vs_a)) {
284
		for ($i = 0; isset($vs_a[$i]); $i++) {
285

    
286
			$append_port_to_name = false;
287
			if (is_alias($pools[$vs_a[$i]['poolname']]['port'])) {
288
				$dest_port_array = filter_expand_alias_array($pools[$vs_a[$i]['poolname']]['port']);
289
				$append_port_to_name = true;
290
			} else {
291
				$dest_port_array = array($pools[$vs_a[$i]['poolname']]['port']);
292
			}
293
			if (is_alias($vs_a[$i]['port'])) {
294
				$src_port_array = filter_expand_alias_array($vs_a[$i]['port']);
295
				$append_port_to_name = true;
296
			} else if ($vs_a[$i]['port']) {
297
				$src_port_array = array($vs_a[$i]['port']);
298
			} else {
299
				$src_port_array = $dest_port_array;
300
			}
301

    
302
			$append_ip_to_name = false;
303
			if (is_alias($vs_a[$i]['ipaddr'])) {
304
				$ip_list = array();
305
				foreach (filter_expand_alias_array($vs_a[$i]['ipaddr']) as $item) {
306
					log_error("item is $item");
307
					if (is_subnetv4($item)) {
308
						$ip_list = array_merge($ip_list, subnetv4_expand($item));
309
					} else {
310
						$ip_list[] = $item;
311
					}
312
				}
313
				$append_ip_to_name = true;
314
			} else if (is_subnetv4($vs_a[$i]['ipaddr'])) {
315
				$ip_list = subnetv4_expand($vs_a[$i]['ipaddr']);
316
				$append_ip_to_name = true;
317
			} else {
318
				$ip_list = array($vs_a[$i]['ipaddr']);
319
			}
320

    
321
			for ($j = 0; $j < count($ip_list); $j += 1) {
322
				$ip = $ip_list[$j];
323
				for ($k = 0; $k < count($src_port_array) && $k < count($dest_port_array); $k += 1) {
324
					$src_port = $src_port_array[$k];
325
					$dest_port = $dest_port_array[$k];
326
					if (is_portrange($dest_port)) {
327
						$dest_ports = explode(':', $dest_port);
328
						$dest_port = $dest_ports[0];
329
					}
330

    
331
					$name = $vs_a[$i]['name'];
332
					if ($append_ip_to_name) {
333
						$name .= "_" . $j;
334
					}
335
					if ($append_port_to_name) {
336
						$name .= "_" . str_replace(":", "_", $src_port);
337
					}
338

    
339
					if (($vs_a[$i]['mode'] == 'relay') || ($vs_a[$i]['relay_protocol'] == 'dns')) {
340
						$conf .= "relay \"{$name}\" {\n";
341
						$conf .= "  listen on {$ip} port {$src_port}\n";
342

    
343
						if ($vs_a[$i]['relay_protocol'] == "dns") {
344
							$conf .= "  protocol \"dnsproto\"\n";
345
						} else {
346
							$conf .= "  protocol \"{$vs_a[$i]['relay_protocol']}\"\n";
347
						}
348
						$lbmode = "";
349
						if ($pools[$vs_a[$i]['poolname']]['mode'] == "loadbalance") {
350
							$lbmode = "mode loadbalance";
351
						}
352

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

    
355
						if (isset($vs_a[$i]['sitedown']) && strlen($vs_a[$i]['sitedown']) > 0 && ($vs_a[$i]['relay_protocol'] != 'dns')) {
356
							$conf .= "  forward to <{$vs_a[$i]['sitedown']}> port {$dest_port} {$lbmode} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n";
357
						}
358
						$conf .= "}\n";
359
					} else {
360
						$conf .= "redirect \"{$name}\" {\n";
361
						$conf .= "  listen on {$ip} port {$src_port}\n";
362
						$conf .= "  forward to <{$vs_a[$i]['poolname']}> port {$dest_port} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n";
363

    
364
						if (isset($config['system']['lb_use_sticky'])) {
365
							$conf .= "  sticky-address\n";
366
						}
367

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

    
373
						$conf .= "}\n";
374
					}
375
				}
376
			}
377
		}
378
	}
379
	fwrite($fd, $conf);
380
	fclose($fd);
381

    
382
	if (is_process_running('relayd')) {
383
		if (!empty($vs_a)) {
384
			if ($kill_first) {
385
				mwexec('pkill relayd');
386
				/* Remove all active relayd anchors now that relayd is no longer running. */
387
				cleanup_lb_anchor("*");
388
				mwexec("/usr/local/sbin/relayd -f {$g['varetc_path']}/relayd.conf");
389
			} else {
390
				// it's running and there is a config, just reload
391
				mwexec("/usr/local/sbin/relayctl reload");
392
			}
393
		} else {
394
			/*
395
			 * XXX: Something breaks our control connection with relayd
396
			 * and makes 'relayctl stop' not work
397
			 * rule reloads are the current suspect
398
			 * mwexec('/usr/local/sbin/relayctl stop');
399
			 *  returns "command failed"
400
			 */
401
			mwexec('pkill relayd');
402
			/* Remove all active relayd anchors now that relayd is no longer running. */
403
			cleanup_lb_anchor("*");
404
		}
405
	} else {
406
		if (!empty($vs_a)) {
407
			// not running and there is a config, start it
408
			/* Remove all active relayd anchors so it can start fresh. */
409
			cleanup_lb_anchor("*");
410
			mwexec("/usr/local/sbin/relayd -f {$g['varetc_path']}/relayd.conf");
411
		}
412
	}
413
}
414

    
415
function get_lb_redirects() {
416
/*
417
# relayctl show summary
418
Id   Type      Name                      Avlblty Status
419
1    redirect  testvs2                           active
420
5    table     test2:80                          active (3 hosts up)
421
11   host      192.168.1.2               91.55%  up
422
10   host      192.168.1.3               100.00% up
423
9    host      192.168.1.4               88.73%  up
424
3    table     test:80                           active (1 hosts up)
425
7    host      192.168.1.2               66.20%  down
426
6    host      192.168.1.3               97.18%  up
427
0    redirect  testvs                            active
428
3    table     test:80                           active (1 hosts up)
429
7    host      192.168.1.2               66.20%  down
430
6    host      192.168.1.3               97.18%  up
431
4    table     testvs-sitedown:80                active (1 hosts up)
432
8    host      192.168.1.4               84.51%  up
433
# relayctl show redirects
434
Id   Type      Name                      Avlblty Status
435
1    redirect  testvs2                           active
436
0    redirect  testvs                            active
437
# relayctl show redirects
438
Id   Type      Name                      Avlblty Status
439
1    redirect  testvs2                           active
440
		   total: 2 sessions
441
		   last: 2/60s 2/h 2/d sessions
442
		   average: 1/60s 0/h 0/d sessions
443
0    redirect  testvs                            active
444
*/
445
	$rdr_a = array();
446
	exec('/usr/local/sbin/relayctl show redirects 2>&1', $rdr_a);
447
	$relay_a = array();
448
	exec('/usr/local/sbin/relayctl show relays 2>&1', $relay_a);
449
	$vs = array();
450
	$cur_entry = "";
451
	for ($i = 0; isset($rdr_a[$i]); $i++) {
452
		$line = $rdr_a[$i];
453
		if (preg_match("/^[0-9]+/", $line)) {
454
			$regs = array();
455
			if ($x = preg_match("/^[0-9]+\s+redirect\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
	$cur_entry = "";
469
	for ($i = 0; isset($relay_a[$i]); $i++) {
470
		$line = $relay_a[$i];
471
		if (preg_match("/^[0-9]+/", $line)) {
472
			$regs = array();
473
			if ($x = preg_match("/^[0-9]+\s+relay\s+([^\s]+)\s+([^\s]+)/", $line, $regs)) {
474
				$cur_entry = trim($regs[1]);
475
				$vs[trim($regs[1])] = array();
476
				$vs[trim($regs[1])]['status'] = trim($regs[2]);
477
			}
478
		} elseif (($x = preg_match("/^\s+total:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
479
			$vs[$cur_entry]['total'] = trim($regs[1]);
480
		} elseif (($x = preg_match("/^\s+last:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
481
			$vs[$cur_entry]['last'] = trim($regs[1]);
482
		} elseif (($x = preg_match("/^\s+average:(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
483
			$vs[$cur_entry]['average'] = trim($regs[1]);
484
		}
485
	}
486
	return $vs;
487
}
488

    
489
function get_lb_summary() {
490
	$relayctl = array();
491
	exec('/usr/local/sbin/relayctl show summary 2>&1', $relayctl);
492
	$relay_hosts=Array();
493
	foreach ((array) $relayctl as $line) {
494
		$t = explode("\t", $line);
495
		switch (trim($t[1])) {
496
			case "table":
497
				$curpool=trim($t[2]);
498
				break;
499
			case "host":
500
				$curhost=trim($t[2]);
501
				$relay_hosts[$curpool][$curhost]['avail']=trim($t[3]);
502
				$relay_hosts[$curpool][$curhost]['state']=trim($t[4]);
503
				break;
504
		}
505
	}
506
	return $relay_hosts;
507
}
508

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

    
515
/* Remove NAT rules from a relayd anchor that is no longer in use.
516
	$anchorname can either be * to clear all anchors or a specific anchor name.*/
517
function cleanup_lb_anchor($anchorname = "*") {
518
	$lbanchors = get_lb_anchors();
519
	foreach ($lbanchors as $lba) {
520
		if (($anchorname == "*") || ($lba == "relayd/{$anchorname}")) {
521
			/* Flush both the NAT and the Table for the anchor, so it will be completely removed by pf. */
522
			mwexec("/sbin/pfctl -a " . escapeshellarg($lba) . " -F nat");
523
			mwexec("/sbin/pfctl -a " . escapeshellarg($lba) . " -F Tables");
524
		}
525
	}
526
}
527

    
528
/* Mark an anchor for later cleanup. This will allow us to remove an old VS name */
529
function cleanup_lb_mark_anchor($name) {
530
	global $g;
531
	/* Nothing to do! */
532
	if (empty($name)) {
533
		return;
534
	}
535
	$filename = "{$g['tmp_path']}/relayd_anchors_remove";
536
	$cleanup_anchors = array();
537
	/* Read in any currently unapplied name changes */
538
	if (file_exists($filename)) {
539
		$cleanup_anchors = explode("\n", file_get_contents($filename));
540
	}
541
	/* Only add the anchor to the list if it's not already there. */
542
	if (!in_array($name, $cleanup_anchors)) {
543
		$cleanup_anchors[] = $name;
544
	}
545
	file_put_contents($filename, implode("\n", $cleanup_anchors));
546
}
547

    
548
/* Cleanup relayd anchors that have been marked for cleanup. */
549
function cleanup_lb_marked() {
550
	global $g, $config;
551
	$filename = "{$g['tmp_path']}/relayd_anchors_remove";
552
	$cleanup_anchors = array();
553
	/* Nothing to do! */
554
	if (!file_exists($filename)) {
555
		return;
556
	} else {
557
		$cleanup_anchors = explode("\n", file_get_contents($filename));
558
		/* Nothing to do! */
559
		if (empty($cleanup_anchors)) {
560
			return;
561
		}
562
	}
563

    
564
	/* Load current names so we can make sure we don't remove an anchor that is still in use. */
565
	$vs_a = $config['load_balancer']['virtual_server'];
566
	$active_vsnames = array();
567
	if (is_array($vs_a)) {
568
		foreach ($vs_a as $vs) {
569
			$active_vsnames[] = $vs['name'];
570
		}
571
	}
572

    
573
	foreach ($cleanup_anchors as $anchor) {
574
		/* Only cleanup an anchor if it is not still active. */
575
		if (!in_array($anchor, $active_vsnames)) {
576
			cleanup_lb_anchor($anchor);
577
		}
578
	}
579
	unlink_if_exists($filename);
580
}
581

    
582
?>
(58-58/65)