Project

General

Profile

Download (37.5 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
  Copyright (C) 2008 Bill Marquette, Seth Mos
4
  Copyright (C) 2010 Ermal Luçi
5
  All rights reserved.
6

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

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

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

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

    
28
	pfSense_BUILDER_BINARIES:	/sbin/route	/usr/local/sbin/apinger
29
	pfSense_MODULE:	routing
30

    
31
 */
32
require_once("config.inc");
33
require_once("rrd.inc");
34

    
35
/* Returns an array of default values used for apinger.conf */
36
function return_apinger_defaults() {
37
	return array(
38
		"latencylow" => "200",
39
		"latencyhigh" => "500",
40
		"losslow" => "10",
41
		"losshigh" => "20",
42
		"interval" => "1",
43
		"down" => "10",
44
		"avg_delay_samples" => "10",
45
		"avg_loss_samples" => "50",
46
		"avg_loss_delay_samples" => "20");
47
}
48

    
49
/*
50
 * Creates monitoring configuration file and
51
 * adds appropriate static routes.
52
 */
53
function setup_gateways_monitor() {
54
	global $config, $g;
55

    
56
	$gateways_arr = return_gateways_array();
57
	if (!is_array($gateways_arr)) {
58
		log_error("No gateways to monitor. Apinger will not be run.");
59
		killbypid("{$g['varrun_path']}/apinger.pid");
60
		@unlink("{$g['varrun_path']}/apinger.status");
61
		return;
62
	}
63

    
64
	$apinger_debug = "";
65
	if (isset($config['system']['apinger_debug']))
66
		$apinger_debug = "debug on";
67

    
68
	$apinger_default = return_apinger_defaults();
69
	$apingerconfig = <<<EOD
70

    
71
# pfSense apinger configuration file. Automatically Generated!
72

    
73
{$apinger_debug}
74

    
75
## User and group the pinger should run as
76
user "root"
77
group "wheel"
78

    
79
## Mailer to use (default: "/usr/lib/sendmail -t")
80
#mailer "/var/qmail/bin/qmail-inject"
81

    
82
## Location of the pid-file (default: "/var/run/apinger.pid")
83
pid_file "{$g['varrun_path']}/apinger.pid"
84

    
85
## Format of timestamp (%s macro) (default: "%b %d %H:%M:%S")
86
#timestamp_format "%Y%m%d%H%M%S"
87

    
88
status {
89
	## File where the status information should be written to
90
	file "{$g['varrun_path']}/apinger.status"
91
	## Interval between file updates
92
	## when 0 or not set, file is written only when SIGUSR1 is received
93
	interval 5s
94
}
95

    
96
########################################
97
# RRDTool status gathering configuration
98
# Interval between RRD updates
99
rrd interval 60s;
100

    
101
## These parameters can be overridden in a specific alarm configuration
102
alarm default {
103
	command on "/usr/local/sbin/pfSctl -c 'service reload dyndns %T' -c 'service reload ipsecdns' -c 'service reload openvpn %T' -c 'filter reload' "
104
	command off "/usr/local/sbin/pfSctl -c 'service reload dyndns %T' -c 'service reload ipsecdns' -c 'service reload openvpn %T' -c 'filter reload' "
105
	combine 10s
106
}
107

    
108
## "Down" alarm definition.
109
## This alarm will be fired when target doesn't respond for 30 seconds.
110
alarm down "down" {
111
	time {$apinger_default['down']}s
112
}
113

    
114
## "Delay" alarm definition.
115
## This alarm will be fired when responses are delayed more than 200ms
116
## it will be canceled, when the delay drops below 100ms
117
alarm delay "delay" {
118
	delay_low {$apinger_default['latencylow']}ms
119
	delay_high {$apinger_default['latencyhigh']}ms
120
}
121

    
122
## "Loss" alarm definition.
123
## This alarm will be fired when packet loss goes over 20%
124
## it will be canceled, when the loss drops below 10%
125
alarm loss "loss" {
126
	percent_low {$apinger_default['losslow']}
127
	percent_high {$apinger_default['losshigh']}
128
}
129

    
130
target default {
131
	## How often the probe should be sent
132
	interval {$apinger_default['interval']}s
133

    
134
	## How many replies should be used to compute average delay
135
	## for controlling "delay" alarms
136
	avg_delay_samples {$apinger_default['avg_delay_samples']}
137

    
138
	## How many probes should be used to compute average loss
139
	avg_loss_samples {$apinger_default['avg_loss_samples']}
140

    
141
	## The delay (in samples) after which loss is computed
142
	## without this delays larger than interval would be treated as loss
143
	avg_loss_delay_samples {$apinger_default['avg_loss_delay_samples']}
144

    
145
	## Names of the alarms that may be generated for the target
146
	alarms "down","delay","loss"
147

    
148
	## Location of the RRD
149
	#rrd file "{$g['vardb_path']}/rrd/apinger-%t.rrd"
150
}
151

    
152
EOD;
153

    
154
	$monitor_ips = array();
155
	foreach($gateways_arr as $name => $gateway) {
156
		/* Do not monitor if such was requested */
157
		if (isset($gateway['monitor_disable']))
158
			continue;
159
		if (empty($gateway['monitor']) || !is_ipaddr($gateway['monitor'])) {
160
			if (is_ipaddr($gateway['gateway']))
161
				$gateway['monitor'] = $gateway['gateway'];
162
			else /* No chance to get an ip to monitor skip target. */
163
				continue;
164
		}
165

    
166
		/* if the monitor address is already used before, skip */
167
		if(in_array($gateway['monitor'], $monitor_ips))
168
			continue;
169

    
170
		/* Interface ip is needed since apinger will bind a socket to it. 
171
		 * However the config GUI should already have checked this and when 
172
		 * PPoE is used the IP address is set to "dynamic". So using is_ipaddrv4
173
		 * or is_ipaddrv6 to identify packet type would be wrong, especially as
174
		 * further checks (that can cope with the "dynamic" case) are present inside
175
		 * the if block. So using $gateway['ipprotocol'] is the better option.
176
		 */
177
		if ($gateway['ipprotocol'] == "inet") { // This is an IPv4 gateway...
178
			$gwifip = find_interface_ip($gateway['interface'], true);
179
			if (!is_ipaddrv4($gwifip))
180
				continue; //Skip this target
181

    
182
			/*
183
			 * If the gateway is the same as the monitor we do not add a
184
			 * route as this will break the routing table.
185
			 * Add static routes for each gateway with their monitor IP
186
			 * not strictly necessary but is a added level of protection.
187
			 */
188
			if (is_ipaddrv4($gateway['gateway']) && $gateway['monitor'] != $gateway['gateway']) {
189
				log_error("Removing static route for monitor {$gateway['monitor']} and adding a new route through {$gateway['gateway']}");
190
				mwexec("/sbin/route change -host " . escapeshellarg($gateway['monitor']) .
191
					" " . escapeshellarg($gateway['gateway']), true);
192
			}
193
		} else if ($gateway['ipprotocol'] == "inet6") { // This is an IPv6 gateway...
194
			if ($gateway['monitor'] == $gateway['gateway']) {
195
				/* link locals really need a different src ip */
196
				if (is_linklocal($gateway['gateway'])) {
197
					if (!strpos($gateway['gateway'], '%'))
198
						$gateway['gateway'] .= '%' . $gateway['interface'];
199
					$gwifip = find_interface_ipv6_ll($gateway['interface'], true);
200
				} else {
201
					$gwifip = find_interface_ipv6($gateway['interface'], true);
202
				}
203
			} else {
204
				/* 'monitor' has been set, so makes sure it has precedence over
205
				 * 'gateway' in defining the source IP. Otherwise if 'gateway'
206
				 * is a local link and 'monitor' is global routable then the
207
				 * ICMP6 response would not find its way back home...
208
				 */
209
				$gwifip = find_interface_ipv6($gateway['interface'], true);
210
			}
211

    
212
			/* Make sure srcip and target have scope defined when they are ll */
213
			if (is_linklocal($gwifip) && !strpos($gwifip, '%'))
214
				$gwifip .= '%' . $gateway['interface'];
215
			if (is_linklocal($gateway['monitor']) && !strpos($gateway['monitor'], '%'))
216
				$gateway['monitor'] .= "%{$gateway['interface']}";
217

    
218
			if (!is_ipaddrv6($gwifip))
219
				continue; //Skip this target
220

    
221
			/*
222
			 * If the gateway is the same as the monitor we do not add a
223
			 * route as this will break the routing table.
224
			 * Add static routes for each gateway with their monitor IP
225
			 * not strictly necessary but is a added level of protection.
226
			 */
227
			if ($gateway['gateway'] != $gateway['monitor']) {
228
				log_error("Removing static route for monitor {$gateway['monitor']} and adding a new route through {$gateway['gateway']}");
229
				mwexec("/sbin/route change -host -inet6 " . escapeshellarg($gateway['monitor']) .
230
					" " . escapeshellarg($gateway['gateway']), true);
231
			}
232
		} else
233
			continue;
234

    
235
		$monitor_ips[] = $gateway['monitor'];
236
		$apingercfg = "target \"{$gateway['monitor']}\" {\n";
237
		$apingercfg .= "	description \"{$name}\"\n";
238
		$apingercfg .= "	srcip \"{$gwifip}\"\n";
239

    
240
		## How often the probe should be sent
241
		if (!empty($gateway['interval']) &&  is_numeric($gateway['interval'])) {
242
			$interval = intval($gateway['interval']);	# Restrict to Integer
243
			if ($interval <  1) $interval =  1;	# Minimum
244
			if ($interval != $apinger_default['interval'])	# If not default value
245
				$apingercfg .= "	interval " . $interval . "s\n";
246
		}
247

    
248
		## How many replies should be used to compute average delay 
249
		## for controlling "delay" alarms
250
		if (!empty($gateway['avg_delay_samples']) && is_numeric($gateway['avg_delay_samples'])) {
251
			$avg_delay_samples = intval($gateway['avg_delay_samples']);	# Restrict to Integer
252
			if ($avg_delay_samples <  1) $avg_delay_samples =  1;	# Minimum
253
			if ($avg_delay_samples != $apinger_default['avg_delay_samples'])	# If not default value
254
				$apingercfg .= "	avg_delay_samples " . $avg_delay_samples . "\n";
255
		}
256

    
257
		## How many probes should be used to compute average loss
258
		if (!empty($gateway['avg_loss_samples']) && is_numeric($gateway['avg_loss_samples'])) {
259
			$avg_loss_samples = intval($gateway['avg_loss_samples']);	# Restrict to Integer
260
			if ($avg_loss_samples < 1) $avg_loss_samples = 1;	# Minimum
261
			if ($avg_loss_samples != $apinger_default['avg_loss_samples'])	# If not default value
262
				$apingercfg .= "	avg_loss_samples " . $avg_loss_samples . "\n";
263
		}
264

    
265
		## The delay (in samples) after which loss is computed
266
		## without this delays larger than interval would be treated as loss
267
		if (!empty($gateway['avg_loss_delay_samples']) && is_numeric($gateway['avg_loss_delay_samples'])) {
268
			$avg_loss_delay_samples = intval($gateway['avg_loss_delay_samples']);	# Restrict to Integer
269
			if ($avg_loss_delay_samples < 1) $avg_loss_delay_samples = 1;	# Minimum
270
			if ($avg_loss_delay_samples != $apinger_default['avg_loss_delay_samples'])	# If not default value
271
				$apingercfg .= "	avg_loss_delay_samples " . $avg_loss_delay_samples . "\n";
272
		}
273

    
274
		$alarms = "";
275
		$alarmscfg = "";
276
		$override = false;
277
		if (!empty($gateway['losslow'])) {
278
			$alarmscfg .= "alarm loss \"{$name}loss\" {\n";
279
			$alarmscfg .= "\tpercent_low {$gateway['losslow']}\n";
280
			$alarmscfg .= "\tpercent_high {$gateway['losshigh']}\n";
281
			$alarmscfg .= "}\n";
282
			$alarms .= "\"{$name}loss\"";
283
			$override = true;
284
		} else {
285
			if ($override == true)
286
				$alarms .= ",";
287
			$alarms .= "\"loss\"";
288
			$override = true;
289
		}
290
		if (!empty($gateway['latencylow'])) {
291
			$alarmscfg .= "alarm delay \"{$name}delay\" {\n";
292
			$alarmscfg .= "\tdelay_low {$gateway['latencylow']}ms\n";
293
			$alarmscfg .= "\tdelay_high {$gateway['latencyhigh']}ms\n";
294
			$alarmscfg .= "}\n";
295
			if ($override == true)
296
				$alarms .= ",";
297
			$alarms .= "\"{$name}delay\"";
298
			$override = true;
299
		} else {
300
			if ($override == true)
301
				$alarms .= ",";
302
			$alarms .= "\"delay\"";
303
			$override = true;
304
		}
305
		if (!empty($gateway['down'])) {
306
			$alarmscfg .= "alarm down \"{$name}down\" {\n";
307
			$alarmscfg .= "\ttime {$gateway['down']}s\n";
308
			$alarmscfg .= "}\n";
309
			if ($override == true)
310
				$alarms .= ",";
311
			$alarms .= "\"{$name}down\"";
312
			$override = true;
313
		} else {
314
			if ($override == true)
315
				$alarms .= ",";
316
			$alarms .= "\"down\"";
317
			$override = true;
318
		}
319
		if ($override == true)
320
			$apingercfg .= "\talarms override {$alarms};\n";
321

    
322
		if (isset($gateway['force_down']))
323
			$apingercfg .= "\tforce_down on\n";
324

    
325
		$apingercfg .= "	rrd file \"{$g['vardb_path']}/rrd/{$gateway['name']}-quality.rrd\"\n";
326
		$apingercfg .= "}\n";
327
		$apingercfg .= "\n";
328

    
329
		$apingerconfig .= $alarmscfg;
330
		$apingerconfig .= $apingercfg;
331

    
332
		# Create gateway quality RRD with settings more suitable for pfSense graph set, 
333
		# since apinger uses default step (300; 5 minutes) and other settings that don't 
334
		# match the pfSense gateway quality graph set.
335
		create_gateway_quality_rrd("{$g['vardb_path']}/rrd/{$gateway['name']}-quality.rrd");
336
	}
337
	@file_put_contents("{$g['varetc_path']}/apinger.conf", $apingerconfig);
338
	unset($apingerconfig);
339

    
340
	if (is_dir("{$g['tmp_path']}"))
341
		chmod("{$g['tmp_path']}", 01777);
342
	if (!is_dir("{$g['vardb_path']}/rrd"))
343
		mkdir("{$g['vardb_path']}/rrd", 0775);
344

    
345
	@chown("{$g['vardb_path']}/rrd", "nobody");
346

    
347
 	/* Restart apinger process */
348
	if (isvalidpid("{$g['varrun_path']}/apinger.pid"))
349
		sigkillbypid("{$g['varrun_path']}/apinger.pid", "HUP");
350
	else {
351
		/* start a new apinger process */
352
		@unlink("{$g['varrun_path']}/apinger.status");
353
		sleep(1);
354
		mwexec_bg("/usr/local/sbin/apinger -c {$g['varetc_path']}/apinger.conf");
355
		sleep(1);
356
		sigkillbypid("{$g['varrun_path']}/apinger.pid", "USR1");
357
	}
358

    
359
	return 0;
360
}
361

    
362
/* return the status of the apinger targets as a array */
363
function return_gateways_status($byname = false) {
364
	global $config, $g;
365

    
366
	$apingerstatus = array();
367
	/* Always get the latest status from apinger */
368
	if (file_exists("{$g['varrun_path']}/apinger.pid"))
369
                sigkillbypid("{$g['varrun_path']}/apinger.pid", "USR1");
370
	if (file_exists("{$g['varrun_path']}/apinger.status")) {
371
		$apingerstatus = file("{$g['varrun_path']}/apinger.status");
372
	} else
373
		$apingerstatus = array();
374

    
375
	$status = array();
376
	foreach($apingerstatus as $line) {
377
		$info = explode("|", $line);
378
		if ($byname == false)
379
			$target = $info[0];
380
		else
381
			$target = $info[2];
382

    
383
		$status[$target] = array();
384
		$status[$target]['monitorip'] = $info[0];
385
		$status[$target]['srcip'] = $info[1];
386
		$status[$target]['name'] = $info[2];
387
		$status[$target]['lastcheck'] = $info[5] ? date('r', $info[5]) : date('r');
388
		$status[$target]['delay'] = empty($info[6]) ? "0ms" : round($info[6], 1) ."ms" ;
389
		$status[$target]['loss'] = empty($info[7]) ? "0.0%" : round($info[7], 1) . "%";
390
		$status[$target]['status'] = trim($info[8]);
391
	}
392

    
393
	/* tack on any gateways that have monitoring disabled
394
	 * or are down, which could cause gateway groups to fail */
395
	$gateways_arr = return_gateways_array();
396
	foreach($gateways_arr as $gwitem) {
397
		if(!isset($gwitem['monitor_disable']))
398
			continue;
399
		if(!is_ipaddr($gwitem['monitorip'])) {
400
			$realif = $gwitem['interface'];
401
			$tgtip = get_interface_gateway($realif);
402
			if (!is_ipaddr($tgtip))
403
				$tgtip = "none";
404
			$srcip = find_interface_ip($realif);
405
		} else {
406
			$tgtip = $gwitem['monitorip'];
407
			$srcip = find_interface_ip($realif);
408
		}
409
		if($byname == true)
410
			$target = $gwitem['name'];
411
		else
412
			$target = $tgtip;
413

    
414
		/* failsafe for down interfaces */
415
		if($target == "none") {
416
			$target = $gwitem['name'];
417
			$status[$target]['name'] = $gwitem['name'];
418
			$status[$target]['lastcheck'] = date('r');
419
			$status[$target]['delay'] = "0.0ms";
420
			$status[$target]['loss'] = "100.0%";
421
			$status[$target]['status'] = "down";
422
		} else {
423
			$status[$target]['monitorip'] = $tgtip;
424
			$status[$target]['srcip'] = $srcip;
425
			$status[$target]['name'] = $gwitem['name'];
426
			$status[$target]['lastcheck'] = date('r');
427
			$status[$target]['delay'] = "0.0ms";
428
			$status[$target]['loss'] = "0.0%";
429
			$status[$target]['status'] = "none";
430
		}
431
	}
432
	return($status);
433
}
434

    
435
/* Return all configured gateways on the system */
436
function return_gateways_array($disabled = false, $localhost = false, $inactive = false) {
437
	global $config, $g;
438

    
439
	$gateways_arr = array();
440

    
441
	$found_defaultv4 = 0;
442
	$found_defaultv6 = 0;
443

    
444
	// Ensure the interface cache is up to date first
445
	$interfaces = get_interface_arr(true);
446
	$interfaces_v4 = array();
447
	$interfaces_v6 = array();
448

    
449
	$i = -1;
450
	/* Process/add all the configured gateways. */
451
	if (is_array($config['gateways']['gateway_item'])) {
452
		foreach ($config['gateways']['gateway_item'] as $gateway) {
453
			/* Increment it here to do not skip items */
454
			$i++;
455

    
456
			if (empty($config['interfaces'][$gateway['interface']])) {
457
				if ($inactive === false)
458
					continue;
459
				else
460
					$gateway['inactive'] = true;
461
			}
462
			$wancfg = $config['interfaces'][$gateway['interface']];
463

    
464
			/* skip disabled interfaces */
465
			if ($disabled === false && (!isset($wancfg['enable']) || isset($gateway['disabled'])))
466
				continue;
467

    
468
			/* if the gateway is dynamic and we can find the IPv4, Great! */
469
			if (empty($gateway['gateway']) || $gateway['gateway'] == "dynamic") {
470
				if ($gateway['ipprotocol'] == "inet") {
471
					/* we know which interfaces is dynamic, this should be made a function */
472
					$gateway['gateway'] = get_interface_gateway($gateway['interface']);
473
					/* no IP address found, set to dynamic */
474
					if (!is_ipaddrv4($gateway['gateway']))
475
						$gateway['gateway'] = "dynamic";
476
					$gateway['dynamic'] = true;
477
				}
478

    
479
				/* if the gateway is dynamic and we can find the IPv6, Great! */
480
				else if ($gateway['ipprotocol'] == "inet6") {
481
					/* we know which interfaces is dynamic, this should be made a function, and for v6 too */
482
					$gateway['gateway'] = get_interface_gateway_v6($gateway['interface']);
483
					/* no IPv6 address found, set to dynamic */
484
					if (!is_ipaddrv6($gateway['gateway']))
485
						$gateway['gateway'] = "dynamic";
486
					$gateway['dynamic'] = true;
487
				}
488
			} else {
489
				/* getting this detection right is hard at this point because we still don't
490
				 * store the address family in the gateway item */
491
				if (is_ipaddrv4($gateway['gateway']))
492
					$gateway['ipprotocol'] = "inet";
493
				else if(is_ipaddrv6($gateway['gateway']))
494
					$gateway['ipprotocol'] = "inet6";
495
			}
496

    
497
			if (isset($gateway['monitor_disable']))
498
				$gateway['monitor_disable'] = true;
499
			else if (empty($gateway['monitor']))
500
				$gateway['monitor'] = $gateway['gateway'];
501

    
502
			$gateway['friendlyiface'] = $gateway['interface'];
503

    
504
			/* special treatment for tunnel interfaces */
505
			if ($gateway['ipprotocol'] == "inet6") {
506
				$gateway['interface'] = get_real_interface($gateway['interface'], "inet6", false, false);
507
				$interfaces_v6[$gateway['friendlyiface']] = $gateway['friendlyiface'];
508
			} else {
509
				$gateway['interface'] = get_real_interface($gateway['interface'], "all", false, false);
510
				$interfaces_v4[$gateway['friendlyiface']] = $gateway['friendlyiface'];
511
			}
512

    
513
			/* entry has a default flag, use it */
514
			if (isset($gateway['defaultgw'])) {
515
				if ($gateway['ipprotocol'] == "inet") {
516
					$gateway['defaultgw'] = true;
517
					$found_defaultv4 = 1;
518
				} else if ($gateway['ipprotocol'] == "inet6") {
519
					$gateway['defaultgw'] = true;
520
					$found_defaultv6 = 1;
521
				}
522
			}
523
			/* include the gateway index as the attribute */
524
			$gateway['attribute'] = $i;
525

    
526
			$gateways_arr[$gateway['name']] = $gateway;
527
		}
528
	}
529
	unset($gateway);
530

    
531
	/* Loop through all interfaces with a gateway and add it to a array */
532
	if ($disabled == false)
533
		$iflist = get_configured_interface_with_descr();
534
	else
535
		$iflist = get_configured_interface_with_descr(false, true);
536

    
537
	/* Process/add dynamic v4 gateways. */
538
	foreach($iflist as $ifname => $friendly ) {
539
		if(! interface_has_gateway($ifname))
540
			continue;
541

    
542
		if (empty($config['interfaces'][$ifname]))
543
			continue;
544

    
545
		$ifcfg = &$config['interfaces'][$ifname];
546
		if(!isset($ifcfg['enable']))
547
			continue;
548

    
549
		if(!empty($ifcfg['ipaddr']) && is_ipaddrv4($ifcfg['ipaddr']))
550
			continue;
551

    
552
		if (isset($interfaces_v4[$ifname]))
553
			continue;
554

    
555
		$ctype = "";
556
		switch($ifcfg['ipaddr']) {
557
			case "dhcp":
558
			case "pppoe":
559
			case "pptp":
560
			case "ppp":
561
				$ctype = strtoupper($ifcfg['ipaddr']);
562
				break;
563
			default:
564
				$tunnelif = substr($ifcfg['if'], 0, 3);
565
				if (substr($ifcfg['if'], 0, 4) ==  "ovpn") {
566
					// if current iface is an ovpn server endpoint then check its type, skip tap only
567
					if (substr($ifcfg['if'], 4, 1) == 's') {
568
						$ovpnid = substr($ifcfg['if'], 5);
569
						if (is_array($config['openvpn']['openvpn-server'])) {
570
							foreach ($config['openvpn']['openvpn-server'] as & $ovpnserverconf) {
571
								if ($ovpnserverconf['vpnid'] == $ovpnid) {
572
									if ($ovpnserverconf['dev_mode'] == "tap")
573
										continue 3;
574
								}
575
							}
576
						}
577
					}
578
					$ctype = "VPNv4";
579
				} else if ($tunnelif == "gif" || $tunnelif == "gre")
580
					$ctype = "TUNNELv4";
581
				break;
582
		}
583
		$ctype = "_". strtoupper($ctype);
584

    
585
		$gateway = array();
586
		$gateway['dynamic'] = false;
587
		$gateway['ipprotocol'] = "inet";
588
		$gateway['gateway'] = get_interface_gateway($ifname, $gateway['dynamic']);
589
		$gateway['interface'] = get_real_interface($ifname);
590
		$gateway['friendlyiface'] = $ifname;
591
		$gateway['name'] = "{$friendly}{$ctype}";
592
		$gateway['attribute'] = "system";
593

    
594
		if (($gateway['dynamic'] === "default") && ($found_defaultv4 == 0)) {
595
			$gateway['defaultgw'] = true;
596
			$gateway['dynamic'] = true;
597
			$found_defaultv4 = 1;
598
		}
599
		/* Loopback dummy for dynamic interfaces without a IP */
600
		if (!is_ipaddrv4($gateway['gateway']) && $gateway['dynamic'] == true)
601
			$gateway['gateway'] = "dynamic";
602

    
603
		/* automatically skip known static and dynamic gateways we have a array entry for */
604
		foreach($gateways_arr as $gateway_item) {
605
			if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name'])&& ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) ||
606
				($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))
607
					continue 2;
608
		}
609

    
610
		if (is_ipaddrv4($gateway['gateway']))
611
			$gateway['monitor'] = $gateway['gateway'];
612

    
613
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
614
		$gateways_arr[$gateway['name']] = $gateway;
615
	}
616
	unset($gateway);
617

    
618
	/* Process/add dynamic v6 gateways. */
619
	foreach($iflist as $ifname => $friendly ) {
620
		/* If the user has disabled IPv6, they probably don't want any IPv6 gateways. */
621
		if (!isset($config['system']['ipv6allow']))
622
			break;
623

    
624
		if(! interface_has_gatewayv6($ifname))
625
			continue;
626

    
627
		if (empty($config['interfaces'][$ifname]))
628
			continue;
629

    
630
		$ifcfg = &$config['interfaces'][$ifname];
631
		if(!isset($ifcfg['enable']))
632
			continue;
633

    
634
		if(!empty($ifcfg['ipaddrv6']) && is_ipaddrv6($ifcfg['ipaddrv6']))
635
			continue;
636

    
637
		if(isset($interfaces_v6[$ifname]))
638
			continue;
639

    
640
		$ctype = "";
641
		switch($ifcfg['ipaddrv6']) {
642
			case "slaac":
643
			case "dhcp6":
644
			case "6to4":
645
			case "6rd":
646
				$ctype = strtoupper($ifcfg['ipaddrv6']);
647
				break;
648
			default:
649
				$tunnelif = substr($ifcfg['if'], 0, 3);
650
				if (substr($ifcfg['if'], 0, 4) ==  "ovpn") {
651
					// if current iface is an ovpn server endpoint then check its type, skip tap only
652
					if (substr($ifcfg['if'], 4, 1) == 's') {
653
						$ovpnid = substr($ifcfg['if'], 5);
654
						if (is_array($config['openvpn']['openvpn-server'])) {
655
							foreach ($config['openvpn']['openvpn-server'] as & $ovpnserverconf) {
656
								if ($ovpnserverconf['vpnid'] == $ovpnid) {
657
									if ($ovpnserverconf['dev_mode'] == "tap")
658
										continue 3;
659
								}
660
							}
661
						}
662
					}
663
					$ctype = "VPNv6";
664
				} else if ($tunnelif == "gif" || $tunnelif == "gre")
665
					$ctype = "TUNNELv6";
666
				break;
667
		}
668
		$ctype = "_". strtoupper($ctype);
669

    
670
		$gateway = array();
671
		$gateway['dynamic'] = false;
672
		$gateway['ipprotocol'] = "inet6";
673
		$gateway['gateway'] = get_interface_gateway_v6($ifname, $gateway['dynamic']);
674
		$gateway['interface'] = get_real_interface($ifname, "inet6");
675
		switch($ifcfg['ipaddrv6']) {
676
			case "6rd":
677
			case "6to4":
678
				$gateway['dynamic'] = "default";
679
				break;
680
		}
681
		$gateway['friendlyiface'] = $ifname;
682
		$gateway['name'] = "{$friendly}{$ctype}";
683
		$gateway['attribute'] = "system";
684

    
685
		if (($gateway['dynamic'] === "default")  && ($found_defaultv6 == 0)) {
686
			$gateway['defaultgw'] = true;
687
			$gateway['dynamic'] = true;
688
			$found_defaultv6 = 1;
689
		}
690

    
691
		/* Loopback dummy for dynamic interfaces without a IP */
692
		if (!is_ipaddrv6($gateway['gateway']) && $gateway['dynamic'] == true)
693
			$gateway['gateway'] = "dynamic";
694

    
695
		/* automatically skip known static and dynamic gateways we have a array entry for */
696
		foreach($gateways_arr as $gateway_item) {
697
			if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name']) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) ||
698
				($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))
699
					continue 2;
700
		}
701

    
702
		if (is_ipaddrv6($gateway['gateway']))
703
			$gateway['monitor'] = $gateway['gateway'];
704

    
705
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
706
		$gateways_arr[$gateway['name']] = $gateway;
707
	}
708
	unset($gateway);
709

    
710
	/* FIXME: Should this be enabled.
711
	 * Some interface like wan might be default but have no info recorded
712
	 * the config. */
713
	/* this is a fallback if all else fails and we want to get packets out @smos */
714
	if ($found_defaultv4 == 0 || $found_defaultv6 == 0) {
715
		foreach ($gateways_arr as &$gateway) {
716
			if (($gateway['friendlyiface'] == "wan") && ($found_defaultv4 == 0) && (!isset($gateway['ipprotocol']) || ($gateway['ipprotocol'] == "inet"))) {
717
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgw")) {
718
					$gateway['defaultgw'] = true;
719
					$found_defaultv4 = 1;
720
				}
721
			}
722
			else if (($gateway['friendlyiface'] == "wan") && ($found_defaultv6 == 0) && ($gateway['ipprotocol'] == "inet6")) {
723
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgwv6")) {
724
					$gateway['defaultgw'] = true;
725
					$found_defaultv6 = 1;
726
				}
727
			}
728
		}
729
	}
730

    
731
	if($localhost === true) {
732
		/* attach localhost for Null routes */
733
		$gwlo4 = array();
734
		$gwlo4['name'] = "Null4";
735
		$gwlo4['interface'] = "lo0";
736
		$gwlo4['ipprotocol'] = "inet";
737
		$gwlo4['gateway'] = "127.0.0.1";
738
		$gwlo6 = array();
739
		$gwlo6['name'] = "Null6";
740
		$gwlo6['interface'] = "lo0";
741
		$gwlo6['ipprotocol'] = "inet6";
742
		$gwlo6['gateway'] = "::1";
743
		$gateways_arr['Null4'] = $gwlo4;
744
		$gateways_arr['Null6'] = $gwlo6;
745
	}
746
	return($gateways_arr);
747
}
748

    
749
function fixup_default_gateway($ipprotocol, $gateways_status, $gateways_arr) {
750
	global $config, $g;
751
	/*
752
	 * NOTE: The code below is meant to replace the default gateway when it goes down.
753
	 *	This facilitates services running on pfSense itself and are not handled by a PBR to continue working.
754
	 */
755
	$upgw = '';
756
	$dfltgwname = '';
757
	$dfltgwdown = false;
758
	$dfltgwfound = false;
759
	foreach ($gateways_arr as $gwname => $gwsttng) {
760
		if (($gwsttng['ipprotocol'] == $ipprotocol) && isset($gwsttng['defaultgw'])) {
761
			$dfltgwfound = true;
762
			$dfltgwname = $gwname;
763
			if (!isset($gwsttng['monitor_disable']) && stristr($gateways_status[$gwname]['status'], "down"))
764
				$dfltgwdown = true;
765
		}
766
		/* Keep a record of the last up gateway */
767
		/* XXX: Blacklist lan for now since it might cause issues to those who have a gateway set for it */
768
		if (empty($upgw) && ($gwsttng['ipprotocol'] == $ipprotocol) && (isset($gwsttng['monitor_disable']) || !stristr($gateways_status[$gwname]['status'], "down")) && $gwsttng[$gwname]['friendlyiface'] != "lan")
769
			$upgw = $gwname;
770
		if ($dfltgwdown == true && !empty($upgw))
771
			break;
772
	}
773
	if ($dfltgwfound == false) {
774
		$gwname = convert_friendly_interface_to_friendly_descr("wan");
775
		if (!empty($gateways_status[$gwname]) && stristr($gateways_status[$gwname]['status'], "down"))
776
			$dfltgwdown = true;
777
	}
778
	if ($dfltgwdown == true && !empty($upgw)) {
779
		if ($gateways_arr[$upgw]['gateway'] == "dynamic")
780
			$gateways_arr[$upgw]['gateway'] = get_interface_gateway($gateways_arr[$upgw]['friendlyiface']);
781
		if (is_ipaddr($gateways_arr[$upgw]['gateway'])) {
782
			log_error("Default gateway down setting {$upgw} as default!");
783
			if(is_ipaddrv6($gateways_arr[$upgw]['gateway'])) {
784
				$inetfamily = "-inet6";
785
			} else {
786
				$inetfamily = "-inet";
787
			}
788
			mwexec("/sbin/route change {$inetfamily} default {$gateways_arr[$upgw]['gateway']}");
789
		}
790
	} else if (!empty($dftgwname)) {
791
		$defaultgw = trim(exec("/sbin/route -n get -{$ipprotocol} default | /usr/bin/awk '/gateway:/ {print $2}'"), " \n");
792
		if ($ipprotocol == 'inet6' && !is_ipaddrv6($gateways_arr[$dfltgwname]['gateway']))
793
			return;
794
		if ($ipprotocol == 'inet' && !is_ipaddrv4($gateways_arr[$dfltgwname]['gateway']))
795
			return;
796
		if ($defaultgw != $gateways_arr[$dfltgwname]['gateway'])
797
			mwexec("/sbin/route change -{$ipprotocol} default {$gateways_arr[$dfltgwname]['gateway']}");
798
	}
799
}
800

    
801
/*
802
 * Return an array with all gateway groups with name as key
803
 * All gateway groups will be processed before returning the array.
804
 */
805
function return_gateway_groups_array() {
806
	global $config, $g;
807

    
808
	/* fetch the current gateways status */
809
	$gateways_status = return_gateways_status(true);
810
	$gateways_arr = return_gateways_array();
811
	$gateway_groups_array = array();
812

    
813
	if (isset($config['system']['gw_switch_default'])) {
814
		fixup_default_gateway("inet", $gateways_status, $gateways_arr);
815
		fixup_default_gateway("inet6", $gateways_status, $gateways_arr);
816
	}
817
	if (is_array($config['gateways']['gateway_group'])) {
818
		$carplist = get_configured_carp_interface_list();
819
		foreach ($config['gateways']['gateway_group'] as $group) {
820
			/* create array with group gateways members separated by tier */
821
			$tiers = array();
822
			$backupplan = array();
823
			$gwvip_arr = array();
824
			foreach ($group['item'] as $item) {
825
				list($gwname, $tier, $vipname) = explode("|", $item);
826

    
827
				if (is_ipaddr($carplist[$vipname])) {
828
					if (!is_array($gwvip_arr[$group['name']]))
829
						$gwvip_arr[$group['name']] = array();
830
					$gwvip_arr[$group['name']][$gwname] = $vipname;
831
				}
832

    
833
				/* Do it here rather than reiterating again the group in case no member is up. */
834
				if (!is_array($backupplan[$tier]))
835
					$backupplan[$tier] = array();
836
				$backupplan[$tier][] = $gwname;
837

    
838
				/* check if the gateway is available before adding it to the array */
839
				if (is_array($gateways_status[$gwname])) {
840
					$status = $gateways_status[$gwname];
841
					$gwdown = false;
842
					if (stristr($status['status'], "down")) {
843
						$msg = sprintf(gettext("MONITOR: %s is down, omitting from routing group {$group['name']}"), $gwname);
844
						$gwdown = true;
845
					} else if (stristr($status['status'], "loss") && strstr($group['trigger'], "loss")) {
846
						/* packet loss */
847
						$msg = sprintf(gettext("MONITOR: %s has packet loss, omitting from routing group {$group['name']}"), $gwname);
848
						$gwdown = true;
849
					} else if (stristr($status['status'], "delay") && strstr($group['trigger'] , "latency")) {
850
						/* high latency */
851
						$msg = sprintf(gettext("MONITOR: %s has high latency, omitting from routing group {$group['name']}"), $gwname);
852
						$gwdown = true;
853
					}
854
					if ($gwdown == true) {
855
						log_error($msg);
856
						notify_via_growl($msg);
857
						notify_via_smtp($msg);
858
					} else {
859
						/* Online add member */
860
						if (!is_array($tiers[$tier]))
861
							$tiers[$tier] = array();
862
						$tiers[$tier][] = $gwname;
863
					}
864
				} else if (isset($gateways_arr[$gwname]['monitor_disable']))
865
					$tiers[$tier][] = $gwname;
866
			}
867
			$tiers_count = count($tiers);
868
			if ($tiers_count == 0) {
869
				/* Oh dear, we have no members! Engage Plan B */
870
				if (!platform_booting()) {
871
					$msg = gettext("Gateways status could not be determined, considering all as up/active. (Group: {$group['name']})");
872
					log_error($msg);
873
					notify_via_growl($msg);
874
					//notify_via_smtp($msg);
875
				}
876
				$tiers = $backupplan;
877
			}
878
			/* sort the tiers array by the tier key */
879
			ksort($tiers);
880

    
881
			/* we do not really foreach the tiers as we stop after the first tier */
882
			foreach ($tiers as $tieridx => $tier) {
883
				/* process all gateways in this tier */
884
				foreach ($tier as $member) {
885
					/* determine interface gateway */
886
					if (isset($gateways_arr[$member])) {
887
						$gateway = $gateways_arr[$member];
888
						$int = $gateway['interface'];
889
						$gatewayip = "";
890
						if(is_ipaddr($gateway['gateway']))
891
							$gatewayip = $gateway['gateway'];
892
						else if (!empty($int))
893
							$gatewayip = get_interface_gateway($gateway['friendlyiface']);
894

    
895
						if (!empty($int)) {
896
							$gateway_groups_array[$group['name']]['ipprotocol'] = $gateway['ipprotocol'];
897
							if (is_ipaddr($gatewayip)) {
898
								$groupmember = array();
899
								$groupmember['int']  = $int;
900
								$groupmember['gwip']  = $gatewayip;
901
								$groupmember['weight']  = isset($gateway['weight']) ? $gateway['weight'] : 1;
902
								if (is_array($gwvip_arr[$group['name']])&& !empty($gwvip_arr[$group['name']][$member]))
903
									$groupmember['vip'] = $gwvip_arr[$group['name']][$member];
904
								$gateway_groups_array[$group['name']][] = $groupmember;
905
							}
906
						}
907
					}
908
				}
909
				/* we should have the 1st available tier now, exit stage left */
910
				if (count($gateway_groups_array[$group['name']]) > 0)
911
					break;
912
				else
913
					log_error("GATEWAYS: Group {$group['name']} did not have any gateways up on tier {$tieridx}!");
914
			}
915
		}
916
	}
917

    
918
	return ($gateway_groups_array);
919
}
920

    
921
/* Update DHCP WAN Interface ip address in gateway group item */
922
function dhclient_update_gateway_groups_defaultroute($interface = "wan") {
923
	global $config, $g;
924
	foreach($config['gateways']['gateway_item'] as & $gw) {
925
		if($gw['interface'] == $interface) {
926
			$current_gw = get_interface_gateway($interface);
927
			if($gw['gateway'] <> $current_gw) {
928
				$gw['gateway'] = $current_gw;
929
				$changed = true;
930
			}
931
		}
932
	}
933
	if($changed && $current_gw)
934
		write_config(sprintf(gettext('Updating gateway group gateway for %1$s - new gateway is %2$s'), $interfac, $current_gw));
935
}
936

    
937
function lookup_gateway_ip_by_name($name) {
938

    
939
	$gateways_arr = return_gateways_array(false, true);
940
	foreach ($gateways_arr as $gname => $gw) {
941
		if ($gw['name'] === $name || $gname === $name)
942
			return $gw['gateway'];
943
	}
944

    
945
	return false;
946
}
947

    
948
function lookup_gateway_monitor_ip_by_name($name) {
949

    
950
	$gateways_arr = return_gateways_array(false, true);
951
	if (!empty($gateways_arr[$name])) {
952
		$gateway = $gateways_arr[$name];
953
		if(!is_ipaddr($gateway['monitor']))
954
			return $gateway['gateway'];
955

    
956
		return $gateway['monitor'];
957
	}
958

    
959
	return (false);
960
}
961

    
962
function lookup_gateway_interface_by_name($name) {
963

    
964
	$gateways_arr = return_gateways_array(false, true);
965
	if (!empty($gateways_arr[$name])) {
966
		$interfacegw = $gateways_arr[$name]['friendlyiface'];
967
		return ($interfacegw);
968
	}
969

    
970
	return (false);
971
}
972

    
973
function get_interface_gateway($interface, &$dynamic = false) {
974
	global $config, $g;
975

    
976
	$gw = NULL;
977

    
978
	$gwcfg = $config['interfaces'][$interface];
979
	if (!empty($gwcfg['gateway']) && is_array($config['gateways']['gateway_item'])) {
980
		foreach($config['gateways']['gateway_item'] as $gateway) {
981
			if(($gateway['name'] == $gwcfg['gateway']) && (is_ipaddrv4($gateway['gateway']))) {
982
				$gw = $gateway['gateway'];
983
				break;
984
			}
985
		}
986
	}
987

    
988
	// for dynamic interfaces we handle them through the $interface_router file.
989
	if (($gw == NULL || !is_ipaddrv4($gw)) && !is_ipaddrv4($gwcfg['ipaddr'])) {
990
		$realif = get_real_interface($interface);
991
		if (file_exists("{$g['tmp_path']}/{$realif}_router")) {
992
				$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_router"), " \n");
993
			$dynamic = true;
994
		}
995
		if (file_exists("{$g['tmp_path']}/{$realif}_defaultgw"))
996
			$dynamic = "default";
997

    
998
	}
999

    
1000
	/* return gateway */
1001
	return ($gw);
1002
}
1003

    
1004
function get_interface_gateway_v6($interface, &$dynamic = false) {
1005
	global $config, $g;
1006

    
1007
	$gw = NULL;
1008
	$gwcfg = $config['interfaces'][$interface];
1009
	if (!empty($gwcfg['gatewayv6']) && is_array($config['gateways']['gateway_item'])) {
1010
		foreach($config['gateways']['gateway_item'] as $gateway) {
1011
			if(($gateway['name'] == $gwcfg['gatewayv6']) && (is_ipaddrv6($gateway['gateway']))) {
1012
				$gw = $gateway['gateway'];
1013
				break;
1014
			}
1015
		}
1016
	}
1017

    
1018
	// for dynamic interfaces we handle them through the $interface_router file.
1019
	if (($gw == NULL || !is_ipaddrv6($gw)) && !is_ipaddrv6($gwcfg['ipaddrv6'])) {
1020
			$realif = get_real_interface($interface);
1021
			if (file_exists("{$g['tmp_path']}/{$realif}_routerv6")) {
1022
				$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_routerv6"), " \n");
1023
				$dynamic = true;
1024
			}
1025
			if (file_exists("{$g['tmp_path']}/{$realif}_defaultgwv6"))
1026
				$dynamic = "default";
1027

    
1028
	}
1029
	/* return gateway */
1030
	return ($gw);
1031
}
1032

    
1033
/* Check a IP address against a gateway IP or name
1034
 * to verify it's address family */
1035
function validate_address_family($ipaddr, $gwname) {
1036
	$v4ip = false;
1037
	$v6ip = false;
1038
	$v4gw = false;
1039
	$v6gw = false;
1040

    
1041
	if(is_ipaddrv4($ipaddr))
1042
		$v4ip = true;
1043
	if(is_ipaddrv6($ipaddr))
1044
		$v6ip = true;
1045
	if(is_ipaddrv4($gwname))
1046
		$v4gw = true;
1047
	if(is_ipaddrv6($gwname))
1048
		$v6gw = true;
1049

    
1050
	if($v4ip && $v4gw)
1051
		return true;
1052
	if($v6ip && $v6gw)
1053
		return true;
1054

    
1055
	/* still no match, carry on, lookup gateways */
1056
	if(is_ipaddrv4(lookup_gateway_ip_by_name($gwname)))
1057
		$v4gw = true;
1058
	if(is_ipaddrv6(lookup_gateway_ip_by_name($gwname)))
1059
		$v6gw = true;
1060

    
1061
	$gw_array = return_gateways_array();
1062
	if(is_array($gw_array[$gwname])) {
1063
		switch($gw_array[$gwname]['ipprotocol']) {
1064
			case "inet":
1065
				$v4gw = true;
1066
				break;
1067
			case "inet6":
1068
				$v6gw = true;
1069
				break;
1070
		}
1071
	}
1072

    
1073
	if($v4ip && $v4gw)
1074
		return true;
1075
	if($v6ip && $v6gw)
1076
		return true;
1077

    
1078
	return false;
1079
}
1080

    
1081
/* check if a interface is part of a gateway group */
1082
function interface_gateway_group_member($interface) {
1083
	global $config;
1084

    
1085
	if (is_array($config['gateways']['gateway_group']))
1086
		$groups = $config['gateways']['gateway_group'];
1087
	else
1088
		return false;
1089

    
1090
	$gateways_arr = return_gateways_array(false, true);
1091
	foreach($groups as $group) {
1092
		if(is_array($group['item'])) {
1093
			foreach($group['item'] as $item) {
1094
				$elements = explode("|", $item);
1095
				$gwname = $elements[0];
1096
				if ($interface == $gateways_arr[$gwname]['interface']) {
1097
					unset($gateways_arr);
1098
					return true;
1099
				}
1100
			}
1101
		}
1102
	}
1103
	unset($gateways_arr);
1104

    
1105
	return false;
1106
}
1107

    
1108
function gateway_is_gwgroup_member($name) {
1109
	global $config;
1110

    
1111
	if (is_array($config['gateways']['gateway_group']))
1112
		$groups = $config['gateways']['gateway_group'];
1113
	else
1114
		return false;
1115

    
1116
	$members = array();
1117
	foreach($groups as $group) {
1118
		if (is_array($group['item'])) {
1119
			foreach($group['item'] as $item) {
1120
				$elements = explode("|", $item);
1121
				$gwname = $elements[0];
1122
				if ($name == $elements[0])
1123
					$members[] = $group['name'];
1124
			}
1125
		}
1126
	}
1127

    
1128
	return $members;
1129
}
1130
?>
(25-25/68)