Project

General

Profile

Download (60.8 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	util.inc
4
*/
5
/* ====================================================================
6
 *	Copyright (c)  2004-2015  Electric Sheep Fencing, LLC. All rights reserved.
7
 *
8
 *  Some or all of this file is based on the m0n0wall project which is
9
 *  Copyright (c)  2004 Manuel Kasper (BSD 2 clause)
10
 *
11
 *	Redistribution and use in source and binary forms, with or without modification,
12
 *	are permitted provided that the following conditions are met:
13
 *
14
 *	1. Redistributions of source code must retain the above copyright notice,
15
 *		this list of conditions and the following disclaimer.
16
 *
17
 *	2. Redistributions in binary form must reproduce the above copyright
18
 *		notice, this list of conditions and the following disclaimer in
19
 *		the documentation and/or other materials provided with the
20
 *		distribution.
21
 *
22
 *	3. All advertising materials mentioning features or use of this software
23
 *		must display the following acknowledgment:
24
 *		"This product includes software developed by the pfSense Project
25
 *		 for use in the pfSense software distribution. (http://www.pfsense.org/).
26
 *
27
 *	4. The names "pfSense" and "pfSense Project" must not be used to
28
 *		 endorse or promote products derived from this software without
29
 *		 prior written permission. For written permission, please contact
30
 *		 coreteam@pfsense.org.
31
 *
32
 *	5. Products derived from this software may not be called "pfSense"
33
 *		nor may "pfSense" appear in their names without prior written
34
 *		permission of the Electric Sheep Fencing, LLC.
35
 *
36
 *	6. Redistributions of any form whatsoever must retain the following
37
 *		acknowledgment:
38
 *
39
 *	"This product includes software developed by the pfSense Project
40
 *	for use in the pfSense software distribution (http://www.pfsense.org/).
41
 *
42
 *	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
43
 *	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44
 *	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
45
 *	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
46
 *	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
47
 *	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
48
 *	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
49
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50
 *	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
51
 *	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
52
 *	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
53
 *	OF THE POSSIBILITY OF SUCH DAMAGE.
54
 *
55
 *	====================================================================
56
 *
57
 */
58

    
59
/* kill a process by pid file */
60
function killbypid($pidfile) {
61
	return sigkillbypid($pidfile, "TERM");
62
}
63

    
64
function isvalidpid($pidfile) {
65
	$output = "";
66
	if (file_exists($pidfile)) {
67
		exec("/bin/pgrep -nF {$pidfile}", $output, $retval);
68
		return (intval($retval) == 0);
69
	}
70
	return false;
71
}
72

    
73
function is_process_running($process) {
74
	$output = "";
75
	exec("/bin/pgrep -anx " . escapeshellarg($process), $output, $retval);
76

    
77
	return (intval($retval) == 0);
78
}
79

    
80
function isvalidproc($proc) {
81
	return is_process_running($proc);
82
}
83

    
84
/* sigkill a process by pid file */
85
/* return 1 for success and 0 for a failure */
86
function sigkillbypid($pidfile, $sig) {
87
	if (file_exists($pidfile)) {
88
		return mwexec("/bin/pkill " . escapeshellarg("-{$sig}") . " -F {$pidfile}", true);
89
	}
90

    
91
	return 0;
92
}
93

    
94
/* kill a process by name */
95
function sigkillbyname($procname, $sig) {
96
	if (isvalidproc($procname)) {
97
		return mwexec("/usr/bin/killall " . escapeshellarg("-{$sig}") . " " . escapeshellarg($procname), true);
98
	}
99
}
100

    
101
/* kill a process by name */
102
function killbyname($procname) {
103
	if (isvalidproc($procname)) {
104
		mwexec("/usr/bin/killall " . escapeshellarg($procname));
105
	}
106
}
107

    
108
function is_subsystem_dirty($subsystem = "") {
109
	global $g;
110

    
111
	if ($subsystem == "") {
112
		return false;
113
	}
114

    
115
	if (file_exists("{$g['varrun_path']}/{$subsystem}.dirty")) {
116
		return true;
117
	}
118

    
119
	return false;
120
}
121

    
122
function mark_subsystem_dirty($subsystem = "") {
123
	global $g;
124

    
125
	if (!file_put_contents("{$g['varrun_path']}/{$subsystem}.dirty", "DIRTY")) {
126
		log_error(sprintf(gettext("WARNING: Could not mark subsystem: %s dirty"), $subsystem));
127
	}
128
}
129

    
130
function clear_subsystem_dirty($subsystem = "") {
131
	global $g;
132

    
133
	@unlink("{$g['varrun_path']}/{$subsystem}.dirty");
134
}
135

    
136
function config_lock() {
137
	return;
138
}
139
function config_unlock() {
140
	return;
141
}
142

    
143
/* lock configuration file */
144
function lock($lock, $op = LOCK_SH) {
145
	global $g, $cfglckkeyconsumers;
146
	if (!$lock) {
147
		die(gettext("WARNING: You must give a name as parameter to lock() function."));
148
	}
149
	if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) {
150
		@touch("{$g['tmp_path']}/{$lock}.lock");
151
		@chmod("{$g['tmp_path']}/{$lock}.lock", 0666);
152
	}
153
	$cfglckkeyconsumers++;
154
	if ($fp = fopen("{$g['tmp_path']}/{$lock}.lock", "w")) {
155
		if (flock($fp, $op)) {
156
			return $fp;
157
		} else {
158
			fclose($fp);
159
		}
160
	}
161
}
162

    
163
function try_lock($lock, $timeout = 5) {
164
	global $g, $cfglckkeyconsumers;
165
	if (!$lock) {
166
		die(gettext("WARNING: You must give a name as parameter to try_lock() function."));
167
	}
168
	if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) {
169
		@touch("{$g['tmp_path']}/{$lock}.lock");
170
		@chmod("{$g['tmp_path']}/{$lock}.lock", 0666);
171
	}
172
	$cfglckkeyconsumers++;
173
	if ($fp = fopen("{$g['tmp_path']}/{$lock}.lock", "w")) {
174
		$trycounter = 0;
175
		while (!flock($fp, LOCK_EX | LOCK_NB)) {
176
			if ($trycounter >= $timeout) {
177
				fclose($fp);
178
				return NULL;
179
			}
180
			sleep(1);
181
			$trycounter++;
182
		}
183

    
184
		return $fp;
185
	}
186

    
187
	return NULL;
188
}
189

    
190
/* unlock configuration file */
191
function unlock($cfglckkey = 0) {
192
	global $g, $cfglckkeyconsumers;
193
	flock($cfglckkey, LOCK_UN);
194
	fclose($cfglckkey);
195
	return;
196
}
197

    
198
/* unlock forcefully configuration file */
199
function unlock_force($lock) {
200
	global $g;
201

    
202
	@unlink("{$g['tmp_path']}/{$lock}.lock");
203
}
204

    
205
function send_event($cmd) {
206
	global $g;
207

    
208
	if (!isset($g['event_address'])) {
209
		$g['event_address'] = "unix:///var/run/check_reload_status";
210
	}
211

    
212
	$try = 0;
213
	while ($try < 3) {
214
		$fd = @fsockopen($g['event_address']);
215
		if ($fd) {
216
			fwrite($fd, $cmd);
217
			$resp = fread($fd, 4096);
218
			if ($resp != "OK\n") {
219
				log_error("send_event: sent {$cmd} got {$resp}");
220
			}
221
			fclose($fd);
222
			$try = 3;
223
		} else if (!is_process_running("check_reload_status")) {
224
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
225
		}
226
		$try++;
227
	}
228
}
229

    
230
function send_multiple_events($cmds) {
231
	global $g;
232

    
233
	if (!isset($g['event_address'])) {
234
		$g['event_address'] = "unix:///var/run/check_reload_status";
235
	}
236

    
237
	if (!is_array($cmds)) {
238
		return;
239
	}
240

    
241
	while ($try < 3) {
242
		$fd = @fsockopen($g['event_address']);
243
		if ($fd) {
244
			foreach ($cmds as $cmd) {
245
				fwrite($fd, $cmd);
246
				$resp = fread($fd, 4096);
247
				if ($resp != "OK\n") {
248
					log_error("send_event: sent {$cmd} got {$resp}");
249
				}
250
			}
251
			fclose($fd);
252
			$try = 3;
253
		} else if (!is_process_running("check_reload_status")) {
254
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
255
		}
256
		$try++;
257
	}
258
}
259

    
260
function refcount_init($reference) {
261
	$shmid = @shmop_open($reference, "c", 0644, 10);
262
	@shmop_write($shmid, str_pad("0", 10, "\x0", STR_PAD_RIGHT), 0);
263
	@shmop_close($shmid);
264
}
265

    
266
function refcount_reference($reference) {
267
	/* Take out a lock across the shared memory read, increment, write sequence to make it atomic. */
268
	$shm_lck = lock("shm{$reference}", LOCK_EX);
269
	try {
270
		/* NOTE: A warning is generated when shared memory does not exist */
271
		$shmid = @shmop_open($reference, "w", 0, 0);
272
		if (!$shmid) {
273
			refcount_init($reference);
274
			$shmid = @shmop_open($reference, "w", 0, 0);
275
			if (!$shmid) {
276
				log_error(gettext("Could not open shared memory {$reference}"));
277
				unlock($shm_lck);
278
				return;
279
			}
280
		}
281
		$shm_data = @shmop_read($shmid, 0, 10);
282
		$shm_data = intval($shm_data) + 1;
283
		@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
284
		@shmop_close($shmid);
285
		unlock($shm_lck);
286
	} catch (Exception $e) {
287
		log_error($e->getMessage());
288
		unlock($shm_lck);
289
	}
290

    
291
	return $shm_data;
292
}
293

    
294
function refcount_unreference($reference) {
295
	/* Take out a lock across the shared memory read, decrement, write sequence to make it atomic. */
296
	$shm_lck = lock("shm{$reference}", LOCK_EX);
297
	try {
298
		$shmid = @shmop_open($reference, "w", 0, 0);
299
		if (!$shmid) {
300
			refcount_init($reference);
301
			log_error(gettext("Could not open shared memory {$reference}"));
302
			unlock($shm_lck);
303
			return;
304
		}
305
		$shm_data = @shmop_read($shmid, 0, 10);
306
		$shm_data = intval($shm_data) - 1;
307
		if ($shm_data < 0) {
308
			//debug_backtrace();
309
			log_error(sprintf(gettext("Reference %s is going negative, not doing unreference."), $reference));
310
		} else {
311
			@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
312
		}
313
		@shmop_close($shmid);
314
		unlock($shm_lck);
315
	} catch (Exception $e) {
316
		log_error($e->getMessage());
317
		unlock($shm_lck);
318
	}
319

    
320
	return $shm_data;
321
}
322

    
323
function refcount_read($reference) {
324
	/* This function just reads the current value of the refcount for information. */
325
	/* There is no need for locking. */
326
	$shmid = @shmop_open($reference, "a", 0, 0);
327
	if (!$shmid) {
328
		log_error(gettext("Could not open shared memory for read {$reference}"));
329
		return -1;
330
	}
331
	$shm_data = @shmop_read($shmid, 0, 10);
332
	@shmop_close($shmid);
333
	return $shm_data;
334
}
335

    
336
function is_module_loaded($module_name) {
337
	$module_name = str_replace(".ko", "", $module_name);
338
	$running = 0;
339
	$_gb = exec("/sbin/kldstat -qn {$module_name} 2>&1", $_gb, $running);
340
	if (intval($running) == 0) {
341
		return true;
342
	} else {
343
		return false;
344
	}
345
}
346

    
347
/* validate non-negative numeric string, or equivalent numeric variable */
348
function is_numericint($arg) {
349
	return (((is_int($arg) && $arg >= 0) || (is_string($arg) && strlen($arg) > 0 && ctype_digit($arg))) ? true : false);
350
}
351

    
352
/* Generate the (human readable) ipv4 or ipv6 subnet address (i.e., netmask, or subnet start IP)
353
   given an (human readable) ipv4 or ipv6 host address and subnet bit count */
354
function gen_subnet($ipaddr, $bits) {
355
	if (($sn = gen_subnetv6($ipaddr, $bits)) == '') {
356
		$sn = gen_subnetv4($ipaddr, $bits);  // try to avoid rechecking IPv4/v6
357
	}
358
	return $sn;
359
}
360

    
361
/* same as gen_subnet() but accepts IPv4 only */
362
function gen_subnetv4($ipaddr, $bits) {
363
	if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) {
364
		if ($bits == 0) {
365
			return '0.0.0.0';  // avoids <<32
366
		}
367
		return long2ip(ip2long($ipaddr) & ((0xFFFFFFFF << (32 - $bits)) & 0xFFFFFFFF));
368
	}
369
	return "";
370
}
371

    
372
/* same as gen_subnet() but accepts IPv6 only */
373
function gen_subnetv6($ipaddr, $bits) {
374
	if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) {
375
		return Net_IPv6::compress(Net_IPv6::getNetmask($ipaddr, $bits));
376
	}
377
	return "";
378
}
379

    
380
/* Generate the (human readable) ipv4 or ipv6 subnet end address (i.e., highest address, end IP, or IPv4 broadcast address)
381
   given an (human readable) ipv4 or ipv6 host address and subnet bit count. */
382
function gen_subnet_max($ipaddr, $bits) {
383
	if (($sn = gen_subnetv6_max($ipaddr, $bits)) == '') {
384
		$sn = gen_subnetv4_max($ipaddr, $bits);  // try to avoid rechecking IPv4/v6
385
	}
386
	return $sn;
387
}
388

    
389
/* same as gen_subnet_max() but validates IPv4 only */
390
function gen_subnetv4_max($ipaddr, $bits) {
391
	if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) {
392
		if ($bits == 32) {
393
			return $ipaddr;
394
		}
395
		return long2ip32(ip2long($ipaddr) | (~gen_subnet_mask_long($bits) & 0xFFFFFFFF));
396
	}
397
	return "";
398
}
399

    
400
/* same as gen_subnet_max() but validates IPv6 only */
401
function gen_subnetv6_max($ipaddr, $bits) {
402
	if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) {
403
		$endip_bin = substr(Net_IPv6::_ip2Bin($ipaddr), 0, $bits) . str_repeat('1', 128 - $bits);
404
		return Net_IPv6::compress(Net_IPv6::_bin2Ip($endip_bin));
405
	}
406
	return "";
407
}
408

    
409
/* returns a subnet mask (long given a bit count) */
410
function gen_subnet_mask_long($bits) {
411
	$sm = 0;
412
	for ($i = 0; $i < $bits; $i++) {
413
		$sm >>= 1;
414
		$sm |= 0x80000000;
415
	}
416
	return $sm;
417
}
418

    
419
/* same as above but returns a string */
420
function gen_subnet_mask($bits) {
421
	return long2ip(gen_subnet_mask_long($bits));
422
}
423

    
424
/* Convert long int to IPv4 address
425
   Returns '' if not valid IPv4 (including if any bits >32 are non-zero) */
426
function long2ip32($ip) {
427
	return long2ip($ip & 0xFFFFFFFF);
428
}
429

    
430
/* Convert IPv4 address to long int, truncated to 32-bits to avoid sign extension on 64-bit platforms.
431
   Returns '' if not valid IPv4. */
432
function ip2long32($ip) {
433
	return (ip2long($ip) & 0xFFFFFFFF);
434
}
435

    
436
/* Convert IPv4 address to unsigned long int.
437
   Returns '' if not valid IPv4. */
438
function ip2ulong($ip) {
439
	return sprintf("%u", ip2long32($ip));
440
}
441

    
442
/* Find out how many IPs are contained within a given IP range
443
 *  e.g. 192.168.0.0 to 192.168.0.255 returns 256
444
 */
445
function ip_range_size_v4($startip, $endip) {
446
	if (is_ipaddrv4($startip) && is_ipaddrv4($endip)) {
447
		// Operate as unsigned long because otherwise it wouldn't work
448
		//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
449
		return abs(ip2ulong($startip) - ip2ulong($endip)) + 1;
450
	}
451
	return -1;
452
}
453

    
454
/* Find the smallest possible subnet mask which can contain a given number of IPs
455
 *  e.g. 512 IPs can fit in a /23, but 513 IPs need a /22
456
 */
457
function find_smallest_cidr_v4($number) {
458
	$smallest = 1;
459
	for ($b=32; $b > 0; $b--) {
460
		$smallest = ($number <= pow(2, $b)) ? $b : $smallest;
461
	}
462
	return (32-$smallest);
463
}
464

    
465
/* Return the previous IP address before the given address */
466
function ip_before($ip, $offset = 1) {
467
	return long2ip32(ip2long($ip) - $offset);
468
}
469

    
470
/* Return the next IP address after the given address */
471
function ip_after($ip, $offset = 1) {
472
	return long2ip32(ip2long($ip) + $offset);
473
}
474

    
475
/* Return true if the first IP is 'before' the second */
476
function ip_less_than($ip1, $ip2) {
477
	// Compare as unsigned long because otherwise it wouldn't work when
478
	//   crossing over from 127.255.255.255 / 128.0.0.0 barrier
479
	return ip2ulong($ip1) < ip2ulong($ip2);
480
}
481

    
482
/* Return true if the first IP is 'after' the second */
483
function ip_greater_than($ip1, $ip2) {
484
	// Compare as unsigned long because otherwise it wouldn't work
485
	//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
486
	return ip2ulong($ip1) > ip2ulong($ip2);
487
}
488

    
489
/* compare two IP addresses */
490
function ipcmp($a, $b) {
491
	if (ip_less_than($a, $b)) {
492
		return -1;
493
	} else if (ip_greater_than($a, $b)) {
494
		return 1;
495
	} else {
496
		return 0;
497
	}
498
}
499

    
500
/* Convert a range of IPv4 addresses to an array of individual addresses. */
501
/* Note: IPv6 ranges are not yet supported here. */
502
function ip_range_to_address_array($startip, $endip, $max_size = 5000) {
503
	if (!is_ipaddrv4($startip) || !is_ipaddrv4($endip)) {
504
		return false;
505
	}
506

    
507
	if (ip_greater_than($startip, $endip)) {
508
		// Swap start and end so we can process sensibly.
509
		$temp = $startip;
510
		$startip = $endip;
511
		$endip = $temp;
512
	}
513

    
514
	if (ip_range_size_v4($startip, $endip) > $max_size) {
515
		return false;
516
	}
517

    
518
	// Container for IP addresses within this range.
519
	$rangeaddresses = array();
520
	$end_int = ip2ulong($endip);
521
	for ($ip_int = ip2ulong($startip); $ip_int <= $end_int; $ip_int++) {
522
		$rangeaddresses[] = long2ip($ip_int);
523
	}
524

    
525
	return $rangeaddresses;
526
}
527

    
528
/* 	Convert an IPv4 or IPv6 IP range to an array of subnets which can contain the range.
529
	Algorithm and embodying code PD'ed by Stilez - enjoy as you like :-)
530

    
531
	Documented on pfsense dev list 19-20 May 2013. Summary:
532

    
533
	The algorithm looks at patterns of 0's and 1's in the least significant bit(s), whether IPv4 or IPv6.
534
	These are all that needs checking to identify a _guaranteed_ correct, minimal and optimal subnet array.
535

    
536
	As a result, string/binary pattern matching of the binary IP is very efficient. It uses just 2 pattern-matching rules
537
	to chop off increasingly larger subnets at both ends that can't be part of larger subnets, until nothing's left.
538

    
539
	(a) If any range has EITHER low bit 1 (in startip) or 0 (in endip), that end-point is _always guaranteed_ to be optimally
540
	represented by its own 'single IP' CIDR; the remaining range then shrinks by one IP up or down, causing the new end-point's
541
	low bit to change from 1->0 (startip) or 0->1 (endip). Only one edge case needs checking: if a range contains exactly 2
542
	adjacent IPs of this format, then the two IPs themselves are required to span it, and we're done.
543
	Once this rule is applied, the remaining range is _guaranteed_ to end in 0's and 1's so rule (b) can now be used, and its
544
	low bits can now be ignored.
545

    
546
	(b) If any range has BOTH startip and endip ending in some number of 0's and 1's respectively, these low bits can
547
	*always* be ignored and "bit-shifted" for subnet spanning. So provided we remember the bits we've place-shifted, we can
548
	_always_ right-shift and chop off those bits, leaving a smaller range that has EITHER startip ending in 1 or endip ending
549
	in 0 (ie can now apply (a) again) or the entire range has vanished and we're done.
550
	We then loop to redo (a) again on the remaining (place shifted) range until after a few loops, the remaining (place shifted)
551
	range 'vanishes' by meeting the exit criteria of (a) or (b), and we're done.
552
*/
553

    
554
function ip_range_to_subnet_array($ip1, $ip2) {
555

    
556
	if (is_ipaddrv4($ip1) && is_ipaddrv4($ip2)) {
557
		$proto = 'ipv4';  // for clarity
558
		$bits = 32;
559
		$ip1bin = decbin(ip2long32($ip1));
560
		$ip2bin = decbin(ip2long32($ip2));
561
	} elseif (is_ipaddrv6($ip1) && is_ipaddrv6($ip2)) {
562
		$proto = 'ipv6';
563
		$bits = 128;
564
		$ip1bin = Net_IPv6::_ip2Bin($ip1);
565
		$ip2bin = Net_IPv6::_ip2Bin($ip2);
566
	} else {
567
		return array();
568
	}
569

    
570
	// it's *crucial* that binary strings are guaranteed the expected length;  do this for certainty even though for IPv6 it's redundant
571
	$ip1bin = str_pad($ip1bin, $bits, '0', STR_PAD_LEFT);
572
	$ip2bin = str_pad($ip2bin, $bits, '0', STR_PAD_LEFT);
573

    
574
	if ($ip1bin == $ip2bin) {
575
		return array($ip1 . '/' . $bits); // exit if ip1=ip2 (trivial case)
576
	}
577

    
578
	if ($ip1bin > $ip2bin) {
579
		list ($ip1bin, $ip2bin) = array($ip2bin, $ip1bin);  // swap if needed (ensures ip1 < ip2)
580
	}
581

    
582
	$rangesubnets = array();
583
	$netsize = 0;
584

    
585
	do {
586
		// at loop start, $ip1 is guaranteed strictly less than $ip2 (important for edge case trapping and preventing accidental binary wrapround)
587
		// which means the assignments $ip1 += 1 and $ip2 -= 1 will always be "binary-wrapround-safe"
588

    
589
		// step #1 if start ip (as shifted) ends in any '1's, then it must have a single cidr to itself (any cidr would include the '0' below it)
590

    
591
		if (substr($ip1bin, -1, 1) == '1') {
592
			// the start ip must be in a separate one-IP cidr range
593
			$new_subnet_ip = substr($ip1bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize);
594
			$rangesubnets[$new_subnet_ip] = $bits - $netsize;
595
			$n = strrpos($ip1bin, '0');  //can't be all 1's
596
			$ip1bin = ($n == 0 ? '' : substr($ip1bin, 0, $n)) . '1' . str_repeat('0', $bits - $n - 1);  // BINARY VERSION OF $ip1 += 1
597
		}
598

    
599
		// step #2, if end ip (as shifted) ends in any zeros then that must have a cidr to itself (as cidr cant span the 1->0 gap)
600

    
601
		if (substr($ip2bin, -1, 1) == '0') {
602
			// the end ip must be in a separate one-IP cidr range
603
			$new_subnet_ip = substr($ip2bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize);
604
			$rangesubnets[$new_subnet_ip] = $bits - $netsize;
605
			$n = strrpos($ip2bin, '1');  //can't be all 0's
606
			$ip2bin = ($n == 0 ? '' : substr($ip2bin, 0, $n)) . '0' . str_repeat('1', $bits - $n - 1);  // BINARY VERSION OF $ip2 -= 1
607
			// already checked for the edge case where end = start+1 and start ends in 0x1, above, so it's safe
608
		}
609

    
610
		// this is the only edge case arising from increment/decrement.
611
		// it happens if the range at start of loop is exactly 2 adjacent ips, that spanned the 1->0 gap. (we will have enumerated both by now)
612

    
613
		if ($ip2bin < $ip1bin) {
614
			continue;
615
		}
616

    
617
		// step #3 the start and end ip MUST now end in '0's and '1's respectively
618
		// so we have a non-trivial range AND the last N bits are no longer important for CIDR purposes.
619

    
620
		$shift = $bits - max(strrpos($ip1bin, '0'), strrpos($ip2bin, '1'));  // num of low bits which are '0' in ip1 and '1' in ip2
621
		$ip1bin = str_repeat('0', $shift) . substr($ip1bin, 0, $bits - $shift);
622
		$ip2bin = str_repeat('0', $shift) . substr($ip2bin, 0, $bits - $shift);
623
		$netsize += $shift;
624
		if ($ip1bin == $ip2bin) {
625
			// we're done.
626
			$new_subnet_ip = substr($ip1bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize);
627
			$rangesubnets[$new_subnet_ip] = $bits - $netsize;
628
			continue;
629
		}
630

    
631
		// at this point there's still a remaining range, and either startip ends with '1', or endip ends with '0'. So repeat cycle.
632
	} while ($ip1bin < $ip2bin);
633

    
634
	// subnets are ordered by bit size. Re sort by IP ("naturally") and convert back to IPv4/IPv6
635

    
636
	ksort($rangesubnets, SORT_STRING);
637
	$out = array();
638

    
639
	foreach ($rangesubnets as $ip => $netmask) {
640
		if ($proto == 'ipv4') {
641
			$i = str_split($ip, 8);
642
			$out[] = implode('.', array(bindec($i[0]), bindec($i[1]), bindec($i[2]), bindec($i[3]))) . '/' . $netmask;
643
		} else {
644
			$out[] = Net_IPv6::compress(Net_IPv6::_bin2Ip($ip)) . '/' . $netmask;
645
		}
646
	}
647

    
648
	return $out;
649
}
650

    
651
/* returns true if $range is a valid pair of IPv4 or IPv6 addresses separated by a "-"
652
	false - if not a valid pair
653
	true (numeric 4 or 6) - if valid, gives type of addresses */
654
function is_iprange($range) {
655
	if (substr_count($range, '-') != 1) {
656
		return false;
657
	}
658
	list($ip1, $ip2) = explode ('-', $range);
659
	if (is_ipaddrv4($ip1) && is_ipaddrv4($ip2)) {
660
		return 4;
661
	}
662
	if (is_ipaddrv6($ip1) && is_ipaddrv6($ip2)) {
663
		return 6;
664
	}
665
	return false;
666
}
667

    
668
/* returns true if $ipaddr is a valid dotted IPv4 address or a IPv6
669
	false - not valid
670
	true (numeric 4 or 6) - if valid, gives type of address */
671
function is_ipaddr($ipaddr) {
672
	if (is_ipaddrv4($ipaddr)) {
673
		return 4;
674
	}
675
	if (is_ipaddrv6($ipaddr)) {
676
		return 6;
677
	}
678
	return false;
679
}
680

    
681
/* returns true if $ipaddr is a valid IPv6 address */
682
function is_ipaddrv6($ipaddr) {
683
	if (!is_string($ipaddr) || empty($ipaddr)) {
684
		return false;
685
	}
686
	if (strstr($ipaddr, "%") && is_linklocal($ipaddr)) {
687
		$tmpip = explode("%", $ipaddr);
688
		$ipaddr = $tmpip[0];
689
	}
690
	return Net_IPv6::checkIPv6($ipaddr);
691
}
692

    
693
/* returns true if $ipaddr is a valid dotted IPv4 address */
694
function is_ipaddrv4($ipaddr) {
695
	if (!is_string($ipaddr) || empty($ipaddr) || ip2long($ipaddr) === FALSE) {
696
		return false;
697
	}
698
	return true;
699
}
700

    
701
/* returns true if $ipaddr is a valid IPv6 linklocal address */
702
function is_linklocal($ipaddr) {
703
	return (strtolower(substr($ipaddr, 0, 5)) == "fe80:");
704
}
705

    
706
/* returns scope of a linklocal address */
707
function get_ll_scope($addr) {
708
	if (!is_linklocal($addr) || !strstr($addr, "%")) {
709
		return "";
710
	}
711
	list ($ll, $scope) = explode("%", $addr);
712
	return $scope;
713
}
714

    
715
/* returns true if $ipaddr is a valid literal IPv6 address */
716
function is_literalipaddrv6($ipaddr) {
717
	if (preg_match("/\[([0-9a-f:]+)\]/i", $ipaddr, $match)) {
718
		$ipaddr = $match[1];
719
	} else {
720
		return false;
721
	}
722

    
723
	return is_ipaddrv6($ipaddr);
724
}
725

    
726
/* returns true if $iport is a valid IPv4/IPv6 address + port
727
	false - not valid
728
	true (numeric 4 or 6) - if valid, gives type of address */
729
function is_ipaddrwithport($ipport) {
730
	$c = strrpos($ipport, ":");
731
	if ($c === false) {
732
		return false;  // can't split at final colon if no colon exists
733
	}
734

    
735
	if (!is_port(substr($ipport, $c + 1))) {
736
		return false;  // no valid port after last colon
737
	}
738

    
739
	$ip = substr($ipport, 0, $c);  // else is text before last colon a valid IP
740
	if (is_literalipaddrv6($ip)) {
741
		return 6;
742
	} elseif (is_ipaddrv4($ip)) {
743
		return 4;
744
	} else {
745
		return false;
746
	}
747
}
748

    
749
function is_hostnamewithport($hostport) {
750
	$parts = explode(":", $hostport);
751
	$port = array_pop($parts);
752
	if (count($parts) == 1) {
753
		return is_hostname($parts[0]) && is_port($port);
754
	} else {
755
		return false;
756
	}
757
}
758

    
759
/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */
760
function is_ipaddroralias($ipaddr) {
761
	global $config;
762

    
763
	if (is_alias($ipaddr)) {
764
		if (is_array($config['aliases']['alias'])) {
765
			foreach ($config['aliases']['alias'] as $alias) {
766
				if ($alias['name'] == $ipaddr && !preg_match("/port/i", $alias['type'])) {
767
					return true;
768
				}
769
			}
770
		}
771
		return false;
772
	} else {
773
		return is_ipaddr($ipaddr);
774
	}
775

    
776
}
777

    
778
/* returns true if $subnet is a valid IPv4 or IPv6 subnet in CIDR format
779
	false - if not a valid subnet
780
	true (numeric 4 or 6) - if valid, gives type of subnet */
781
function is_subnet($subnet) {
782
	if (is_string($subnet) && preg_match('/^(?:([0-9.]{7,15})|([0-9a-f:]{2,39}))\/(\d{1,3})$/i', $subnet, $parts)) {
783
		if (is_ipaddrv4($parts[1]) && $parts[3] <= 32) {
784
			return 4;
785
		}
786
		if (is_ipaddrv6($parts[2]) && $parts[3] <= 128) {
787
			return 6;
788
		}
789
	}
790
	return false;
791
}
792

    
793
/* same as is_subnet() but accepts IPv4 only */
794
function is_subnetv4($subnet) {
795
	return (is_subnet($subnet) == 4);
796
}
797

    
798
/* same as is_subnet() but accepts IPv6 only */
799
function is_subnetv6($subnet) {
800
	return (is_subnet($subnet) == 6);
801
}
802

    
803
/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */
804
function is_subnetoralias($subnet) {
805
	global $aliastable;
806

    
807
	if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet])) {
808
		return true;
809
	} else {
810
		return is_subnet($subnet);
811
	}
812
}
813

    
814
function subnet_size($subnet) {
815
	if (is_subnetv4($subnet)) {
816
		list ($ip, $bits) = explode("/", $subnet);
817
		return round(exp(log(2) * (32 - $bits)));
818
	} else if (is_subnetv6($subnet)) {
819
		list ($ip, $bits) = explode("/", $subnet);
820
		return round(exp(log(2) * (128 - $bits)));
821
	} else {
822
		return 0;
823
	}
824
}
825

    
826

    
827
function subnet_expand($subnet) {
828
	if (is_subnetv4($subnet)) {
829
		return subnetv4_expand($subnet);
830
	} else if (is_subnetv6($subnet)) {
831
		return subnetv6_expand($subnet);
832
	} else {
833
		return $subnet;
834
	}
835
}
836

    
837
function subnetv4_expand($subnet) {
838
	$result = array();
839
	list ($ip, $bits) = explode("/", $subnet);
840
	$net = ip2long($ip);
841
	$mask = (0xffffffff << (32 - $bits));
842
	$net &= $mask;
843
	$size = round(exp(log(2) * (32 - $bits)));
844
	for ($i = 0; $i < $size; $i += 1) {
845
		$result[] = long2ip($net | $i);
846
	}
847
	return $result;
848
}
849

    
850
/* find out whether two subnets overlap */
851
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
852

    
853
	if (!is_numeric($bits1)) {
854
		$bits1 = 32;
855
	}
856
	if (!is_numeric($bits2)) {
857
		$bits2 = 32;
858
	}
859

    
860
	if ($bits1 < $bits2) {
861
		$relbits = $bits1;
862
	} else {
863
		$relbits = $bits2;
864
	}
865

    
866
	$sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1);
867
	$sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2);
868

    
869
	return ($sn1 == $sn2);
870
}
871

    
872
/* find out whether two IPv6 subnets overlap */
873
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
874
	$sub1_min = gen_subnetv6($subnet1, $bits1);
875
	$sub1_max = gen_subnetv6_max($subnet1, $bits1);
876
	$sub2_min = gen_subnetv6($subnet2, $bits2);
877
	$sub2_max = gen_subnetv6_max($subnet2, $bits2);
878

    
879
	return (is_inrange_v6($sub1_min, $sub2_min, $sub2_max) || is_inrange_v6($sub1_max, $sub2_min, $sub2_max) || is_inrange_v6($sub2_min, $sub1_min, $sub1_max));
880
}
881

    
882
/* return true if $addr is in $subnet, false if not */
883
function ip_in_subnet($addr, $subnet) {
884
	if (is_ipaddrv6($addr) && is_subnetv6($subnet)) {
885
		return (Net_IPv6::isInNetmask($addr, $subnet));
886
	} else if (is_ipaddrv4($addr) && is_subnetv4($subnet)) {
887
		list($ip, $mask) = explode('/', $subnet);
888
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
889
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
890
	}
891
	return false;
892
}
893

    
894
/* returns true if $hostname is just a valid hostname (top part without any of the domain part) */
895
function is_unqualified_hostname($hostname) {
896
	if (!is_string($hostname)) {
897
		return false;
898
	}
899

    
900
	if (preg_match('/^(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])$/i', $hostname)) {
901
		return true;
902
	} else {
903
		return false;
904
	}
905
}
906

    
907
/* returns true if $hostname is a valid hostname, with or without being a fully-qualified domain name. */
908
function is_hostname($hostname, $allow_wildcard=false) {
909
	if (!is_string($hostname)) {
910
		return false;
911
	}
912

    
913
	if (is_domain($hostname, $allow_wildcard)) {
914
		if ((substr_count($hostname, ".") == 1) && ($hostname[strlen($hostname)-1] == ".")) {
915
			/* Only a single dot at the end like "test." - hosts cannot be directly in the root domain. */
916
			return false;
917
		} else {
918
			return true;
919
		}
920
	} else {
921
		return false;
922
	}
923
}
924

    
925
/* returns true if $domain is a valid domain name */
926
function is_domain($domain, $allow_wildcard=false) {
927
	if (!is_string($domain)) {
928
		return false;
929
	}
930
	if ($allow_wildcard) {
931
		$domain_regex = '/^(?:(?:[a-z_0-9\*]|[a-z_0-9][a-z_0-9\-]*[a-z_0-9])\.)*(?:[a-z_0-9]|[a-z_0-9][a-z_0-9\-]*[a-z_0-9\.])$/i';
932
	} else {
933
		$domain_regex = '/^(?:(?:[a-z_0-9]|[a-z_0-9][a-z_0-9\-]*[a-z_0-9])\.)*(?:[a-z_0-9]|[a-z_0-9][a-z_0-9\-]*[a-z_0-9\.])$/i';
934
	}
935

    
936
	if (preg_match($domain_regex, $domain)) {
937
		return true;
938
	} else {
939
		return false;
940
	}
941
}
942

    
943
/* returns true if $macaddr is a valid MAC address */
944
function is_macaddr($macaddr, $partial=false) {
945
	$repeat = ($partial) ? '1,5' : '5';
946
	return preg_match('/^[0-9A-F]{2}(?:[:][0-9A-F]{2}){'.$repeat.'}$/i', $macaddr) == 1 ? true : false;
947
}
948

    
949
/* returns true if $name is a valid name for an alias
950
   returns NULL if a reserved word is used
951
   returns FALSE for bad chars in the name - this allows calling code to determine what the problem was.
952
   aliases cannot be:
953
	bad chars: anything except a-z 0-9 and underscore
954
	bad names: empty string, pure numeric, pure underscore
955
	reserved words: pre-defined service/protocol/port names which should not be ambiguous, and the words "port" and  "pass" */
956

    
957
function is_validaliasname($name) {
958
	/* Array of reserved words */
959
	$reserved = array("port", "pass");
960

    
961
	if (!is_string($name) || strlen($name) >= 32 || preg_match('/(^_*$|^\d*$|[^a-z0-9_])/i', $name)) {
962
		return false;
963
	}
964
	if (in_array($name, $reserved, true) || getservbyname($name, "tcp") || getservbyname($name, "udp") || getprotobyname($name)) {
965
		return; /* return NULL */
966
	}
967
	return true;
968
}
969

    
970
/* returns true if $port is a valid TCP/UDP port */
971
function is_port($port) {
972
	if (ctype_digit($port) && ((intval($port) >= 1) && (intval($port) <= 65535))) {
973
		return true;
974
	}
975
	if (getservbyname($port, "tcp") || getservbyname($port, "udp")) {
976
		return true;
977
	}
978
	return false;
979
}
980

    
981
/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */
982
function is_portrange($portrange) {
983
	$ports = explode(":", $portrange);
984

    
985
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
986
}
987

    
988
/* returns true if $port is a valid port number or an alias thereof */
989
function is_portoralias($port) {
990
	global $config;
991

    
992
	if (is_alias($port)) {
993
		if (is_array($config['aliases']['alias'])) {
994
			foreach ($config['aliases']['alias'] as $alias) {
995
				if ($alias['name'] == $port && preg_match("/port/i", $alias['type'])) {
996
					return true;
997
				}
998
			}
999
		}
1000
		return false;
1001
	} else {
1002
		return is_port($port);
1003
	}
1004
}
1005

    
1006
/* create ranges of sequential port numbers (200:215) and remove duplicates */
1007
function group_ports($ports) {
1008
	if (!is_array($ports) || empty($ports)) {
1009
		return;
1010
	}
1011

    
1012
	$uniq = array();
1013
	foreach ($ports as $port) {
1014
		if (is_portrange($port)) {
1015
			list($begin, $end) = explode(":", $port);
1016
			if ($begin > $end) {
1017
				$aux = $begin;
1018
				$begin = $end;
1019
				$end = $aux;
1020
			}
1021
			for ($i = $begin; $i <= $end; $i++) {
1022
				if (!in_array($i, $uniq)) {
1023
					$uniq[] = $i;
1024
				}
1025
			}
1026
		} else if (is_port($port)) {
1027
			if (!in_array($port, $uniq)) {
1028
				$uniq[] = $port;
1029
			}
1030
		}
1031
	}
1032
	sort($uniq, SORT_NUMERIC);
1033

    
1034
	$result = array();
1035
	foreach ($uniq as $idx => $port) {
1036
		if ($idx == 0) {
1037
			$result[] = $port;
1038
			continue;
1039
		}
1040

    
1041
		$last = end($result);
1042
		if (is_portrange($last)) {
1043
			list($begin, $end) = explode(":", $last);
1044
		} else {
1045
			$begin = $end = $last;
1046
		}
1047

    
1048
		if ($port == ($end+1)) {
1049
			$end++;
1050
			$result[count($result)-1] = "{$begin}:{$end}";
1051
		} else {
1052
			$result[] = $port;
1053
		}
1054
	}
1055

    
1056
	return $result;
1057
}
1058

    
1059
/* returns true if $val is a valid shaper bandwidth value */
1060
function is_valid_shaperbw($val) {
1061
	return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val));
1062
}
1063

    
1064
/* returns true if $test is in the range between $start and $end */
1065
function is_inrange_v4($test, $start, $end) {
1066
	if ((ip2ulong($test) <= ip2ulong($end)) && (ip2ulong($test) >= ip2ulong($start))) {
1067
		return true;
1068
	} else {
1069
		return false;
1070
	}
1071
}
1072

    
1073
/* returns true if $test is in the range between $start and $end */
1074
function is_inrange_v6($test, $start, $end) {
1075
	if ((inet_pton($test) <= inet_pton($end)) && (inet_pton($test) >= inet_pton($start))) {
1076
		return true;
1077
	} else {
1078
		return false;
1079
	}
1080
}
1081

    
1082
/* returns true if $test is in the range between $start and $end */
1083
function is_inrange($test, $start, $end) {
1084
	return is_ipaddrv6($test) ? is_inrange_v6($test, $start, $end) : is_inrange_v4($test, $start, $end);
1085
}
1086

    
1087
/* XXX: return the configured carp interface list */
1088
function get_configured_carp_interface_list($carpinterface = '', $family = 'inet', $what = 'ip') {
1089
	global $config;
1090

    
1091
	$iflist = array();
1092

    
1093
	if (!is_array($config['virtualip']['vip']) || empty($config['virtualip']['vip'])) {
1094
		return $iflist;
1095
	}
1096

    
1097
	$viparr = &$config['virtualip']['vip'];
1098
	foreach ($viparr as $vip) {
1099
		if ($vip['mode'] != "carp") {
1100
			continue;
1101
		}
1102

    
1103
		if (empty($carpinterface)) {
1104
			$iflist["_vip{$vip['uniqid']}"] = $vip['subnet'];
1105
			continue;
1106
		}
1107

    
1108
		if ($carpinterface != "_vip{$vip['uniqid']}") {
1109
			continue;
1110
		}
1111

    
1112
		switch ($what) {
1113
			case 'subnet':
1114
				if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1115
					return $vip['subnet_bits'];
1116
				} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1117
					return $vip['subnet_bits'];
1118
				}
1119
				break;
1120
			case 'iface':
1121
				if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1122
					return $vip['interface'];
1123
				} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1124
					return $vip['interface'];
1125
				}
1126
				break;
1127
			case 'vip':
1128
				if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1129
					return $vip;
1130
				} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1131
					return $vip;
1132
				}
1133
				break;
1134
			case 'ip':
1135
			default:
1136
				if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1137
					return $vip['subnet'];
1138
				} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1139
					return $vip['subnet'];
1140
				}
1141
				break;
1142
		}
1143
		break;
1144
	}
1145

    
1146
	return $iflist;
1147
}
1148

    
1149
/* return the configured IP aliases list */
1150
function get_configured_ip_aliases_list($returnfullentry = false) {
1151
	global $config;
1152

    
1153
	$alias_list = array();
1154

    
1155
	if (is_array($config['virtualip']['vip'])) {
1156
		$viparr = &$config['virtualip']['vip'];
1157
		foreach ($viparr as $vip) {
1158
			if ($vip['mode'] == "ipalias") {
1159
				if ($returnfullentry) {
1160
					$alias_list[$vip['subnet']] = $vip;
1161
				} else {
1162
					$alias_list[$vip['subnet']] = $vip['interface'];
1163
				}
1164
			}
1165
		}
1166
	}
1167

    
1168
	return $alias_list;
1169
}
1170

    
1171
/* return all configured aliases list (IP, carp, proxyarp and other) */
1172
function get_configured_vips_list() {
1173
	global $config;
1174

    
1175
	$alias_list = array();
1176

    
1177
	if (is_array($config['virtualip']['vip'])) {
1178
		$viparr = &$config['virtualip']['vip'];
1179
		foreach ($viparr as $vip) {
1180
			if ($vip['mode'] == "carp") {
1181
				$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => "_vip{$vip['uniqid']}");
1182
			} else {
1183
				$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => $vip['interface']);
1184
			}
1185
		}
1186
	}
1187

    
1188
	return $alias_list;
1189
}
1190

    
1191
/* comparison function for sorting by the order in which interfaces are normally created */
1192
function compare_interface_friendly_names($a, $b) {
1193
	if ($a == $b) {
1194
		return 0;
1195
	} else if ($a == 'wan') {
1196
		return -1;
1197
	} else if ($b == 'wan') {
1198
		return 1;
1199
	} else if ($a == 'lan') {
1200
		return -1;
1201
	} else if ($b == 'lan') {
1202
		return 1;
1203
	}
1204

    
1205
	return strnatcmp($a, $b);
1206
}
1207

    
1208
/* return the configured interfaces list. */
1209
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
1210
	global $config;
1211

    
1212
	$iflist = array();
1213

    
1214
	/* if list */
1215
	foreach ($config['interfaces'] as $if => $ifdetail) {
1216
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1217
			continue;
1218
		}
1219
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1220
			$iflist[$if] = $if;
1221
		}
1222
	}
1223

    
1224
	return $iflist;
1225
}
1226

    
1227
/* return the configured interfaces list. */
1228
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
1229
	global $config;
1230

    
1231
	$iflist = array();
1232

    
1233
	/* if list */
1234
	foreach ($config['interfaces'] as $if => $ifdetail) {
1235
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1236
			continue;
1237
		}
1238
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1239
			$tmpif = get_real_interface($if);
1240
			if (!empty($tmpif)) {
1241
				$iflist[$tmpif] = $if;
1242
			}
1243
		}
1244
	}
1245

    
1246
	return $iflist;
1247
}
1248

    
1249
/* return the configured interfaces list with their description. */
1250
function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) {
1251
	global $config;
1252

    
1253
	$iflist = array();
1254

    
1255
	/* if list */
1256
	foreach ($config['interfaces'] as $if => $ifdetail) {
1257
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1258
			continue;
1259
		}
1260
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1261
			if (empty($ifdetail['descr'])) {
1262
				$iflist[$if] = strtoupper($if);
1263
			} else {
1264
				$iflist[$if] = strtoupper($ifdetail['descr']);
1265
			}
1266
		}
1267
	}
1268

    
1269
	return $iflist;
1270
}
1271

    
1272
/*
1273
 *   get_configured_ip_addresses() - Return a list of all configured
1274
 *   interfaces IP Addresses
1275
 *
1276
 */
1277
function get_configured_ip_addresses() {
1278
	global $config;
1279

    
1280
	if (!function_exists('get_interface_ip')) {
1281
		require_once("interfaces.inc");
1282
	}
1283
	$ip_array = array();
1284
	$interfaces = get_configured_interface_list();
1285
	if (is_array($interfaces)) {
1286
		foreach ($interfaces as $int) {
1287
			$ipaddr = get_interface_ip($int);
1288
			$ip_array[$int] = $ipaddr;
1289
		}
1290
	}
1291
	$interfaces = get_configured_carp_interface_list();
1292
	if (is_array($interfaces)) {
1293
		foreach ($interfaces as $int => $ipaddr) {
1294
			$ip_array[$int] = $ipaddr;
1295
		}
1296
	}
1297

    
1298
	/* pppoe server */
1299
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
1300
		foreach ($config['pppoes']['pppoe'] as $pppoe) {
1301
			if ($pppoe['mode'] == "server") {
1302
				if (is_ipaddr($pppoe['localip'])) {
1303
					$int = "pppoes". $pppoe['pppoeid'];
1304
					$ip_array[$int] = $pppoe['localip'];
1305
				}
1306
			}
1307
		}
1308
	}
1309

    
1310
	return $ip_array;
1311
}
1312

    
1313
/*
1314
 *   get_configured_ipv6_addresses() - Return a list of all configured
1315
 *   interfaces IPv6 Addresses
1316
 *
1317
 */
1318
function get_configured_ipv6_addresses() {
1319
	require_once("interfaces.inc");
1320
	$ipv6_array = array();
1321
	$interfaces = get_configured_interface_list();
1322
	if (is_array($interfaces)) {
1323
		foreach ($interfaces as $int) {
1324
			$ipaddrv6 = get_interface_ipv6($int);
1325
			$ipv6_array[$int] = $ipaddrv6;
1326
		}
1327
	}
1328
	$interfaces = get_configured_carp_interface_list();
1329
	if (is_array($interfaces)) {
1330
		foreach ($interfaces as $int => $ipaddrv6) {
1331
			$ipv6_array[$int] = $ipaddrv6;
1332
		}
1333
	}
1334
	return $ipv6_array;
1335
}
1336

    
1337
/*
1338
 *   get_interface_list() - Return a list of all physical interfaces
1339
 *   along with MAC and status.
1340
 *
1341
 *   $mode = "active" - use ifconfig -lu
1342
 *           "media"  - use ifconfig to check physical connection
1343
 *			status (much slower)
1344
 */
1345
function get_interface_list($mode = "active", $keyby = "physical", $vfaces = "") {
1346
	global $config;
1347
	$upints = array();
1348
	/* get a list of virtual interface types */
1349
	if (!$vfaces) {
1350
		$vfaces = array(
1351
				'bridge',
1352
				'ppp',
1353
				'pppoe',
1354
				'pptp',
1355
				'l2tp',
1356
				'sl',
1357
				'gif',
1358
				'gre',
1359
				'faith',
1360
				'lo',
1361
				'ng',
1362
				'_vlan',
1363
				'_wlan',
1364
				'pflog',
1365
				'plip',
1366
				'pfsync',
1367
				'enc',
1368
				'tun',
1369
				'carp',
1370
				'lagg',
1371
				'vip',
1372
				'ipfw'
1373
		);
1374
	}
1375
	switch ($mode) {
1376
		case "active":
1377
			$upints = pfSense_interface_listget(IFF_UP);
1378
			break;
1379
		case "media":
1380
			$intlist = pfSense_interface_listget();
1381
			$ifconfig = "";
1382
			exec("/sbin/ifconfig -a", $ifconfig);
1383
			$regexp = '/(' . implode('|', $intlist) . '):\s/';
1384
			$ifstatus = preg_grep('/status:/', $ifconfig);
1385
			foreach ($ifstatus as $status) {
1386
				$int = array_shift($intlist);
1387
				if (stristr($status, "active")) {
1388
					$upints[] = $int;
1389
				}
1390
			}
1391
			break;
1392
		default:
1393
			$upints = pfSense_interface_listget();
1394
			break;
1395
	}
1396
	/* build interface list with netstat */
1397
	$linkinfo = "";
1398
	exec("/usr/bin/netstat -inW -f link | awk '{ print $1, $4 }'", $linkinfo);
1399
	array_shift($linkinfo);
1400
	/* build ip address list with netstat */
1401
	$ipinfo = "";
1402
	exec("/usr/bin/netstat -inW -f inet | awk '{ print $1, $4 }'", $ipinfo);
1403
	array_shift($ipinfo);
1404
	foreach ($linkinfo as $link) {
1405
		$friendly = "";
1406
		$alink = explode(" ", $link);
1407
		$ifname = rtrim(trim($alink[0]), '*');
1408
		/* trim out all numbers before checking for vfaces */
1409
		if (!in_array(array_shift(preg_split('/\d/', $ifname)), $vfaces) &&
1410
		    !stristr($ifname, "_vlan") && !stristr($ifname, "_wlan")) {
1411
			$toput = array(
1412
					"mac" => trim($alink[1]),
1413
					"up" => in_array($ifname, $upints)
1414
				);
1415
			foreach ($ipinfo as $ip) {
1416
				$aip = explode(" ", $ip);
1417
				if ($aip[0] == $ifname) {
1418
					$toput['ipaddr'] = $aip[1];
1419
				}
1420
			}
1421
			if (is_array($config['interfaces'])) {
1422
				foreach ($config['interfaces'] as $name => $int) {
1423
					if ($int['if'] == $ifname) {
1424
						$friendly = $name;
1425
					}
1426
				}
1427
			}
1428
			switch ($keyby) {
1429
			case "physical":
1430
				if ($friendly != "") {
1431
					$toput['friendly'] = $friendly;
1432
				}
1433
				$dmesg_arr = array();
1434
				exec("/sbin/dmesg |grep $ifname | head -n1", $dmesg_arr);
1435
				preg_match_all("/<(.*?)>/i", $dmesg_arr[0], $dmesg);
1436
				$toput['dmesg'] = $dmesg[1][0];
1437
				$iflist[$ifname] = $toput;
1438
				break;
1439
			case "ppp":
1440

    
1441
			case "friendly":
1442
				if ($friendly != "") {
1443
					$toput['if'] = $ifname;
1444
					$iflist[$friendly] = $toput;
1445
				}
1446
				break;
1447
			}
1448
		}
1449
	}
1450
	return $iflist;
1451
}
1452

    
1453
/****f* util/log_error
1454
* NAME
1455
*   log_error  - Sends a string to syslog.
1456
* INPUTS
1457
*   $error     - string containing the syslog message.
1458
* RESULT
1459
*   null
1460
******/
1461
function log_error($error) {
1462
	global $g;
1463
	$page = $_SERVER['SCRIPT_NAME'];
1464
	if (empty($page)) {
1465
		$files = get_included_files();
1466
		$page = basename($files[0]);
1467
	}
1468
	syslog(LOG_ERR, "$page: $error");
1469
	if ($g['debug']) {
1470
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1471
	}
1472
	return;
1473
}
1474

    
1475
/****f* util/log_auth
1476
* NAME
1477
*   log_auth   - Sends a string to syslog as LOG_AUTH facility
1478
* INPUTS
1479
*   $error     - string containing the syslog message.
1480
* RESULT
1481
*   null
1482
******/
1483
function log_auth($error) {
1484
	global $g;
1485
	$page = $_SERVER['SCRIPT_NAME'];
1486
	syslog(LOG_AUTH, "$page: $error");
1487
	if ($g['debug']) {
1488
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1489
	}
1490
	return;
1491
}
1492

    
1493
/****f* util/exec_command
1494
 * NAME
1495
 *   exec_command - Execute a command and return a string of the result.
1496
 * INPUTS
1497
 *   $command   - String of the command to be executed.
1498
 * RESULT
1499
 *   String containing the command's result.
1500
 * NOTES
1501
 *   This function returns the command's stdout and stderr.
1502
 ******/
1503
function exec_command($command) {
1504
	$output = array();
1505
	exec($command . ' 2>&1', $output);
1506
	return(implode("\n", $output));
1507
}
1508

    
1509
/* wrapper for exec()
1510
   Executes in background or foreground.
1511
   For background execution, returns PID of background process to allow calling code control */
1512
function mwexec($command, $nologentry = false, $clearsigmask = false, $background = false) {
1513
	global $g;
1514
	$retval = 0;
1515

    
1516
	if ($g['debug']) {
1517
		if (!$_SERVER['REMOTE_ADDR']) {
1518
			echo "mwexec(): $command" . ($background ? " [BG]":"") . "\n";
1519
		}
1520
	}
1521
	if ($clearsigmask) {
1522
		$oldset = array();
1523
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1524
	}
1525

    
1526
	if ($background) {
1527
		// start background process and return PID
1528
		$retval = exec("/usr/bin/nohup $command > /dev/null 2>&1 & echo $!");
1529
	} else {
1530
		// run in foreground, and (optionally) log if nonzero return
1531
		$outputarray = array();
1532
		exec("$command 2>&1", $outputarray, $retval);
1533
		if (($retval <> 0) && (!$nologentry || isset($config['system']['developerspew']))) {
1534
			log_error(sprintf(gettext("The command '%1\$s' returned exit code '%2\$d', the output was '%3\$s' "), $command, $retval, implode(" ", $outputarray)));
1535
		}
1536
	}
1537

    
1538
	if ($clearsigmask) {
1539
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1540
	}
1541

    
1542
	return $retval;
1543
}
1544

    
1545
/* wrapper for exec() in background */
1546
function mwexec_bg($command, $clearsigmask = false) {
1547
	return mwexec($command, false, $clearsigmask, true);
1548
}
1549

    
1550
/* unlink a file, or pattern-match of a file, if it exists
1551
   if the file/path contains glob() compatible wildcards, all matching files will be unlinked
1552
   if no matches, no error occurs */
1553
function unlink_if_exists($fn) {
1554
	$to_do = glob($fn);
1555
	if (is_array($to_do) && count($to_do) > 0) {
1556
		@array_map("unlink", $to_do);
1557
	} else {
1558
		@unlink($fn);
1559
	}
1560
}
1561
/* make a global alias table (for faster lookups) */
1562
function alias_make_table($config) {
1563
	global $aliastable;
1564

    
1565
	$aliastable = array();
1566

    
1567
	if (is_array($config['aliases']['alias'])) {
1568
		foreach ($config['aliases']['alias'] as $alias) {
1569
			if ($alias['name']) {
1570
				$aliastable[$alias['name']] = $alias['address'];
1571
			}
1572
		}
1573
	}
1574
}
1575

    
1576
/* check if an alias exists */
1577
function is_alias($name) {
1578
	global $aliastable;
1579

    
1580
	return isset($aliastable[$name]);
1581
}
1582

    
1583
function alias_get_type($name) {
1584
	global $config;
1585

    
1586
	if (is_array($config['aliases']['alias'])) {
1587
		foreach ($config['aliases']['alias'] as $alias) {
1588
			if ($name == $alias['name']) {
1589
				return $alias['type'];
1590
			}
1591
		}
1592
	}
1593

    
1594
	return "";
1595
}
1596

    
1597
/* expand a host or network alias, if necessary */
1598
function alias_expand($name) {
1599
	global $aliastable;
1600

    
1601
	if (isset($aliastable[$name])) {
1602
		// alias names cannot be strictly numeric. redmine #4289
1603
		if (is_numericint($name)) {
1604
			return null;
1605
		}
1606
		return "\${$name}";
1607
	} else if (is_ipaddr($name) || is_subnet($name) || is_port($name) || is_portrange($name)) {
1608
		return "{$name}";
1609
	} else {
1610
		return null;
1611
	}
1612
}
1613

    
1614
function alias_expand_urltable($name) {
1615
	global $config;
1616
	$urltable_prefix = "/var/db/aliastables/";
1617
	$urltable_filename = $urltable_prefix . $name . ".txt";
1618

    
1619
	if (is_array($config['aliases']['alias'])) {
1620
		foreach ($config['aliases']['alias'] as $alias) {
1621
			if (preg_match("/urltable/i", $alias['type']) && ($alias['name'] == $name)) {
1622
				if (is_URL($alias["url"]) && file_exists($urltable_filename) && filesize($urltable_filename)) {
1623
					return $urltable_filename;
1624
				} else {
1625
					send_event("service sync alias {$name}");
1626
					break;
1627
				}
1628
			}
1629
		}
1630
	}
1631
	return null;
1632
}
1633

    
1634
/* obtain MAC address given an IP address by looking at the ARP table */
1635
function arp_get_mac_by_ip($ip) {
1636
	mwexec("/sbin/ping -c 1 -t 1 " . escapeshellarg($ip), true);
1637
	$arpoutput = "";
1638
	exec("/usr/sbin/arp -n " . escapeshellarg($ip), $arpoutput);
1639

    
1640
	if ($arpoutput[0]) {
1641
		$arpi = explode(" ", $arpoutput[0]);
1642
		$macaddr = $arpi[3];
1643
		if (is_macaddr($macaddr)) {
1644
			return $macaddr;
1645
		} else {
1646
			return false;
1647
		}
1648
	}
1649

    
1650
	return false;
1651
}
1652

    
1653
/* return a fieldname that is safe for xml usage */
1654
function xml_safe_fieldname($fieldname) {
1655
	$replace = array('/', '-', ' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
1656
			 '_', '+', '=', '{', '}', '[', ']', '|', '/', '<', '>', '?',
1657
			 ':', ',', '.', '\'', '\\'
1658
		);
1659
	return strtolower(str_replace($replace, "", $fieldname));
1660
}
1661

    
1662
function mac_format($clientmac) {
1663
	global $config, $cpzone;
1664

    
1665
	$mac = explode(":", $clientmac);
1666
	$mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1667

    
1668
	switch ($mac_format) {
1669
		case 'singledash':
1670
			return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1671

    
1672
		case 'ietf':
1673
			return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1674

    
1675
		case 'cisco':
1676
			return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1677

    
1678
		case 'unformatted':
1679
			return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1680

    
1681
		default:
1682
			return $clientmac;
1683
	}
1684
}
1685

    
1686
function resolve_retry($hostname, $retries = 5) {
1687

    
1688
	if (is_ipaddr($hostname)) {
1689
		return $hostname;
1690
	}
1691

    
1692
	for ($i = 0; $i < $retries; $i++) {
1693
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1694
		$ip = gethostbyname($hostname);
1695

    
1696
		if ($ip && $ip != $hostname) {
1697
			/* success */
1698
			return $ip;
1699
		}
1700

    
1701
		sleep(1);
1702
	}
1703

    
1704
	return false;
1705
}
1706

    
1707
function format_bytes($bytes) {
1708
	if ($bytes >= 1073741824) {
1709
		return sprintf("%.2f GB", $bytes/1073741824);
1710
	} else if ($bytes >= 1048576) {
1711
		return sprintf("%.2f MB", $bytes/1048576);
1712
	} else if ($bytes >= 1024) {
1713
		return sprintf("%.0f KB", $bytes/1024);
1714
	} else {
1715
		return sprintf("%d bytes", $bytes);
1716
	}
1717
}
1718

    
1719
function update_filter_reload_status($text) {
1720
	global $g;
1721

    
1722
	file_put_contents("{$g['varrun_path']}/filter_reload_status", $text);
1723
}
1724

    
1725
/****** util/return_dir_as_array
1726
 * NAME
1727
 *   return_dir_as_array - Return a directory's contents as an array.
1728
 * INPUTS
1729
 *   $dir          - string containing the path to the desired directory.
1730
 *   $filter_regex - string containing a regular expression to filter file names. Default empty.
1731
 * RESULT
1732
 *   $dir_array - array containing the directory's contents. This array will be empty if the path specified is invalid.
1733
 ******/
1734
function return_dir_as_array($dir, $filter_regex = '') {
1735
	$dir_array = array();
1736
	if (is_dir($dir)) {
1737
		if ($dh = opendir($dir)) {
1738
			while (($file = readdir($dh)) !== false) {
1739
				if (($file == ".") || ($file == "..")) {
1740
					continue;
1741
				}
1742

    
1743
				if (empty($filter_regex) || preg_match($filter_regex, $file)) {
1744
					array_push($dir_array, $file);
1745
				}
1746
			}
1747
			closedir($dh);
1748
		}
1749
	}
1750
	return $dir_array;
1751
}
1752

    
1753
function run_plugins($directory) {
1754
	global $config, $g;
1755

    
1756
	/* process packager manager custom rules */
1757
	$files = return_dir_as_array($directory);
1758
	if (is_array($files)) {
1759
		foreach ($files as $file) {
1760
			if (stristr($file, ".sh") == true) {
1761
				mwexec($directory . $file . " start");
1762
			} else if (!is_dir($directory . "/" . $file) && stristr($file, ".inc")) {
1763
				require_once($directory . "/" . $file);
1764
			}
1765
		}
1766
	}
1767
}
1768

    
1769
/*
1770
 *    safe_mkdir($path, $mode = 0755)
1771
 *    create directory if it doesn't already exist and isn't a file!
1772
 */
1773
function safe_mkdir($path, $mode = 0755) {
1774
	global $g;
1775

    
1776
	if (!is_file($path) && !is_dir($path)) {
1777
		return @mkdir($path, $mode, true);
1778
	} else {
1779
		return false;
1780
	}
1781
}
1782

    
1783
/*
1784
 * get_sysctl($names)
1785
 * Get values of sysctl OID's listed in $names (accepts an array or a single
1786
 * name) and return an array of key/value pairs set for those that exist
1787
 */
1788
function get_sysctl($names) {
1789
	if (empty($names)) {
1790
		return array();
1791
	}
1792

    
1793
	if (is_array($names)) {
1794
		$name_list = array();
1795
		foreach ($names as $name) {
1796
			$name_list[] = escapeshellarg($name);
1797
		}
1798
	} else {
1799
		$name_list = array(escapeshellarg($names));
1800
	}
1801

    
1802
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1803
	$values = array();
1804
	foreach ($output as $line) {
1805
		$line = explode(": ", $line, 2);
1806
		if (count($line) == 2) {
1807
			$values[$line[0]] = $line[1];
1808
		}
1809
	}
1810

    
1811
	return $values;
1812
}
1813

    
1814
/*
1815
 * get_single_sysctl($name)
1816
 * Wrapper for get_sysctl() to simplify read of a single sysctl value
1817
 * return the value for sysctl $name or empty string if it doesn't exist
1818
 */
1819
function get_single_sysctl($name) {
1820
	if (empty($name)) {
1821
		return "";
1822
	}
1823

    
1824
	$value = get_sysctl($name);
1825
	if (empty($value) || !isset($value[$name])) {
1826
		return "";
1827
	}
1828

    
1829
	return $value[$name];
1830
}
1831

    
1832
/*
1833
 * set_sysctl($value_list)
1834
 * Set sysctl OID's listed as key/value pairs and return
1835
 * an array with keys set for those that succeeded
1836
 */
1837
function set_sysctl($values) {
1838
	if (empty($values)) {
1839
		return array();
1840
	}
1841

    
1842
	$value_list = array();
1843
	foreach ($values as $key => $value) {
1844
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
1845
	}
1846

    
1847
	exec("/sbin/sysctl -i " . implode(" ", $value_list), $output, $success);
1848

    
1849
	/* Retry individually if failed (one or more read-only) */
1850
	if ($success <> 0 && count($value_list) > 1) {
1851
		foreach ($value_list as $value) {
1852
			exec("/sbin/sysctl -i " . $value, $output);
1853
		}
1854
	}
1855

    
1856
	$ret = array();
1857
	foreach ($output as $line) {
1858
		$line = explode(": ", $line, 2);
1859
		if (count($line) == 2) {
1860
			$ret[$line[0]] = true;
1861
		}
1862
	}
1863

    
1864
	return $ret;
1865
}
1866

    
1867
/*
1868
 * set_single_sysctl($name, $value)
1869
 * Wrapper to set_sysctl() to make it simple to set only one sysctl
1870
 * returns boolean meaning if it succeeded
1871
 */
1872
function set_single_sysctl($name, $value) {
1873
	if (empty($name)) {
1874
		return false;
1875
	}
1876

    
1877
	$result = set_sysctl(array($name => $value));
1878

    
1879
	if (!isset($result[$name]) || $result[$name] != $value) {
1880
		return false;
1881
	}
1882

    
1883
	return true;
1884
}
1885

    
1886
/*
1887
 *     get_memory()
1888
 *     returns an array listing the amount of
1889
 *     memory installed in the hardware
1890
 *     [0] net memory available for the OS (FreeBSD) after some is taken by BIOS, video or whatever - e.g. 235 MBytes
1891
 *     [1] real (actual) memory of the system, should be the size of the RAM card/s - e.g. 256 MBytes
1892
 */
1893
function get_memory() {
1894
	$physmem = get_single_sysctl("hw.physmem");
1895
	$realmem = get_single_sysctl("hw.realmem");
1896
	/* convert from bytes to megabytes */
1897
	return array(($physmem/1048576), ($realmem/1048576));
1898
}
1899

    
1900
function mute_kernel_msgs() {
1901
	global $g, $config;
1902
	// Do not mute serial console.  The kernel gets very very cranky
1903
	// and will start dishing you cannot control tty errors.
1904
	if ($g['platform'] == 'nanobsd') {
1905
		return;
1906
	}
1907
	if ($config['system']['enableserial']) {
1908
		return;
1909
	}
1910
	exec("/sbin/conscontrol mute on");
1911
}
1912

    
1913
function unmute_kernel_msgs() {
1914
	global $g;
1915
	// Do not mute serial console.  The kernel gets very very cranky
1916
	// and will start dishing you cannot control tty errors.
1917
	if ($g['platform'] == 'nanobsd') {
1918
		return;
1919
	}
1920
	exec("/sbin/conscontrol mute off");
1921
}
1922

    
1923
function start_devd() {
1924
	/* Use the undocumented -q options of devd to quiet its log spamming */
1925
	$_gb = exec("/sbin/devd -q");
1926
	sleep(1);
1927
	unset($_gb);
1928
}
1929

    
1930
function is_interface_vlan_mismatch() {
1931
	global $config, $g;
1932

    
1933
	if (is_array($config['vlans']['vlan'])) {
1934
		foreach ($config['vlans']['vlan'] as $vlan) {
1935
			if (does_interface_exist($vlan['if']) == false) {
1936
				return true;
1937
			}
1938
		}
1939
	}
1940

    
1941
	return false;
1942
}
1943

    
1944
function is_interface_mismatch() {
1945
	global $config, $g;
1946

    
1947
	$do_assign = false;
1948
	$i = 0;
1949
	$missing_interfaces = array();
1950
	if (is_array($config['interfaces'])) {
1951
		foreach ($config['interfaces'] as $ifname => $ifcfg) {
1952
			if (preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_wlan/i", $ifcfg['if'])) {
1953
				// Do not check these interfaces.
1954
				$i++;
1955
				continue;
1956
			} else if (does_interface_exist($ifcfg['if']) == false) {
1957
				$missing_interfaces[] = $ifcfg['if'];
1958
				$do_assign = true;
1959
			} else {
1960
				$i++;
1961
			}
1962
		}
1963
	}
1964

    
1965
	if (file_exists("{$g['tmp_path']}/assign_complete")) {
1966
		$do_assign = false;
1967
	}
1968

    
1969
	if (!empty($missing_interfaces) && $do_assign) {
1970
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
1971
	} else {
1972
		@unlink("{$g['tmp_path']}/missing_interfaces");
1973
	}
1974

    
1975
	return $do_assign;
1976
}
1977

    
1978
/* sync carp entries to other firewalls */
1979
function carp_sync_client() {
1980
	global $g;
1981
	send_event("filter sync");
1982
}
1983

    
1984
/****f* util/isAjax
1985
 * NAME
1986
 *   isAjax - reports if the request is driven from prototype
1987
 * INPUTS
1988
 *   none
1989
 * RESULT
1990
 *   true/false
1991
 ******/
1992
function isAjax() {
1993
	return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
1994
}
1995

    
1996
/****f* util/timeout
1997
 * NAME
1998
 *   timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space.
1999
 * INPUTS
2000
 *   optional, seconds to wait before timeout. Default 9 seconds.
2001
 * RESULT
2002
 *   returns 1 char of user input or null if no input.
2003
 ******/
2004
function timeout($timer = 9) {
2005
	while (!isset($key)) {
2006
		if ($timer >= 9) {
2007
			echo chr(8) . chr(8) . ($timer == 9 ? chr(32) : null) . "{$timer}";
2008
		} else {
2009
			echo chr(8). "{$timer}";
2010
		}
2011
		`/bin/stty -icanon min 0 time 25`;
2012
		$key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`);
2013
		`/bin/stty icanon`;
2014
		if ($key == '') {
2015
			unset($key);
2016
		}
2017
		$timer--;
2018
		if ($timer == 0) {
2019
			break;
2020
		}
2021
	}
2022
	return $key;
2023
}
2024

    
2025
/****f* util/msort
2026
 * NAME
2027
 *   msort - sort array
2028
 * INPUTS
2029
 *   $array to be sorted, field to sort by, direction of sort
2030
 * RESULT
2031
 *   returns newly sorted array
2032
 ******/
2033
function msort($array, $id = "id", $sort_ascending = true) {
2034
	$temp_array = array();
2035
	while (count($array)>0) {
2036
		$lowest_id = 0;
2037
		$index = 0;
2038
		foreach ($array as $item) {
2039
			if (isset($item[$id])) {
2040
				if ($array[$lowest_id][$id]) {
2041
					if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
2042
						$lowest_id = $index;
2043
					}
2044
				}
2045
			}
2046
			$index++;
2047
		}
2048
		$temp_array[] = $array[$lowest_id];
2049
		$array = array_merge(array_slice($array, 0, $lowest_id), array_slice($array, $lowest_id + 1));
2050
	}
2051
	if ($sort_ascending) {
2052
		return $temp_array;
2053
	} else {
2054
		return array_reverse($temp_array);
2055
	}
2056
}
2057

    
2058
/****f* util/is_URL
2059
 * NAME
2060
 *   is_URL
2061
 * INPUTS
2062
 *   string to check
2063
 * RESULT
2064
 *   Returns true if item is a URL
2065
 ******/
2066
function is_URL($url) {
2067
	$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
2068
	if ($match) {
2069
		return true;
2070
	}
2071
	return false;
2072
}
2073

    
2074
function is_file_included($file = "") {
2075
	$files = get_included_files();
2076
	if (in_array($file, $files)) {
2077
		return true;
2078
	}
2079

    
2080
	return false;
2081
}
2082

    
2083
/*
2084
 * Replace a value on a deep associative array using regex
2085
 */
2086
function array_replace_values_recursive($data, $match, $replace) {
2087
	if (empty($data)) {
2088
		return $data;
2089
	}
2090

    
2091
	if (is_string($data)) {
2092
		$data = preg_replace("/{$match}/", $replace, $data);
2093
	} else if (is_array($data)) {
2094
		foreach ($data as $k => $v) {
2095
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
2096
		}
2097
	}
2098

    
2099
	return $data;
2100
}
2101

    
2102
/*
2103
	This function was borrowed from a comment on PHP.net at the following URL:
2104
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
2105
 */
2106
function array_merge_recursive_unique($array0, $array1) {
2107

    
2108
	$arrays = func_get_args();
2109
	$remains = $arrays;
2110

    
2111
	// We walk through each arrays and put value in the results (without
2112
	// considering previous value).
2113
	$result = array();
2114

    
2115
	// loop available array
2116
	foreach ($arrays as $array) {
2117

    
2118
		// The first remaining array is $array. We are processing it. So
2119
		// we remove it from remaining arrays.
2120
		array_shift($remains);
2121

    
2122
		// We don't care non array param, like array_merge since PHP 5.0.
2123
		if (is_array($array)) {
2124
			// Loop values
2125
			foreach ($array as $key => $value) {
2126
				if (is_array($value)) {
2127
					// we gather all remaining arrays that have such key available
2128
					$args = array();
2129
					foreach ($remains as $remain) {
2130
						if (array_key_exists($key, $remain)) {
2131
							array_push($args, $remain[$key]);
2132
						}
2133
					}
2134

    
2135
					if (count($args) > 2) {
2136
						// put the recursion
2137
						$result[$key] = call_user_func_array(__FUNCTION__, $args);
2138
					} else {
2139
						foreach ($value as $vkey => $vval) {
2140
							$result[$key][$vkey] = $vval;
2141
						}
2142
					}
2143
				} else {
2144
					// simply put the value
2145
					$result[$key] = $value;
2146
				}
2147
			}
2148
		}
2149
	}
2150
	return $result;
2151
}
2152

    
2153

    
2154
/*
2155
 * converts a string like "a,b,c,d"
2156
 * into an array like array("a" => "b", "c" => "d")
2157
 */
2158
function explode_assoc($delimiter, $string) {
2159
	$array = explode($delimiter, $string);
2160
	$result = array();
2161
	$numkeys = floor(count($array) / 2);
2162
	for ($i = 0; $i < $numkeys; $i += 1) {
2163
		$result[$array[$i * 2]] = $array[$i * 2 + 1];
2164
	}
2165
	return $result;
2166
}
2167

    
2168
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false) {
2169
	global $config, $aliastable;
2170

    
2171
	/* Bail if there are no routes, but return an array always so callers don't have to check. */
2172
	if (!is_array($config['staticroutes']['route'])) {
2173
		return array();
2174
	}
2175

    
2176
	$allstaticroutes = array();
2177
	$allsubnets = array();
2178
	/* Loop through routes and expand aliases as we find them. */
2179
	foreach ($config['staticroutes']['route'] as $route) {
2180
		if (is_alias($route['network'])) {
2181
			if (!isset($aliastable[$route['network']])) {
2182
				continue;
2183
			}
2184

    
2185
			$subnets = preg_split('/\s+/', $aliastable[$route['network']]);
2186
			foreach ($subnets as $net) {
2187
				if (!is_subnet($net)) {
2188
					if (is_ipaddrv4($net)) {
2189
						$net .= "/32";
2190
					} else if (is_ipaddrv6($net)) {
2191
						$net .= "/128";
2192
					} else if ($returnhostnames === false || !is_fqdn($net)) {
2193
						continue;
2194
					}
2195
				}
2196
				$temproute = $route;
2197
				$temproute['network'] = $net;
2198
				$allstaticroutes[] = $temproute;
2199
				$allsubnets[] = $net;
2200
			}
2201
		} elseif (is_subnet($route['network'])) {
2202
			$allstaticroutes[] = $route;
2203
			$allsubnets[] = $route['network'];
2204
		}
2205
	}
2206
	if ($returnsubnetsonly) {
2207
		return $allsubnets;
2208
	} else {
2209
		return $allstaticroutes;
2210
	}
2211
}
2212

    
2213
/****f* util/get_alias_list
2214
 * NAME
2215
 *   get_alias_list - Provide a list of aliases.
2216
 * INPUTS
2217
 *   $type          - Optional, can be a string or array specifying what type(s) of aliases you need.
2218
 * RESULT
2219
 *   Array containing list of aliases.
2220
 *   If $type is unspecified, all aliases are returned.
2221
 *   If $type is a string, all aliases of the type specified in $type are returned.
2222
 *   If $type is an array, all aliases of any type specified in any element of $type are returned.
2223
 */
2224
function get_alias_list($type = null) {
2225
	global $config;
2226
	$result = array();
2227
	if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) {
2228
		foreach ($config['aliases']['alias'] as $alias) {
2229
			if ($type === null) {
2230
				$result[] = $alias['name'];
2231
			} else if (is_array($type)) {
2232
				if (in_array($alias['type'], $type)) {
2233
					$result[] = $alias['name'];
2234
				}
2235
			} else if ($type === $alias['type']) {
2236
				$result[] = $alias['name'];
2237
			}
2238
		}
2239
	}
2240
	return $result;
2241
}
2242

    
2243
/* returns an array consisting of every element of $haystack that is not equal to $needle. */
2244
function array_exclude($needle, $haystack) {
2245
	$result = array();
2246
	if (is_array($haystack)) {
2247
		foreach ($haystack as $thing) {
2248
			if ($needle !== $thing) {
2249
				$result[] = $thing;
2250
			}
2251
		}
2252
	}
2253
	return $result;
2254
}
2255

    
2256
/* Define what is preferred, IPv4 or IPv6 */
2257
function prefer_ipv4_or_ipv6() {
2258
	global $config;
2259

    
2260
	if (isset($config['system']['prefer_ipv4'])) {
2261
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv4");
2262
	} else {
2263
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv6");
2264
	}
2265
}
2266

    
2267
/* Redirect to page passing parameters via POST */
2268
function post_redirect($page, $params) {
2269
	if (!is_array($params)) {
2270
		return;
2271
	}
2272

    
2273
	print "<html><body><form action=\"{$page}\" name=\"formredir\" method=\"post\">\n";
2274
	foreach ($params as $key => $value) {
2275
		print "<input type=\"hidden\" name=\"{$key}\" value=\"{$value}\" />\n";
2276
	}
2277
	print "</form>\n";
2278
	print "<script type=\"text/javascript\">\n";
2279
	print "//<![CDATA[\n";
2280
	print "document.formredir.submit();\n";
2281
	print "//]]>\n";
2282
	print "</script>\n";
2283
	print "</body></html>\n";
2284
}
2285

    
2286
/* Locate disks that can be queried for S.M.A.R.T. data. */
2287
function get_smart_drive_list() {
2288
	$disk_list = explode(" ", get_single_sysctl("kern.disks"));
2289
	foreach ($disk_list as $id => $disk) {
2290
		// We only want certain kinds of disks for S.M.A.R.T.
2291
		// 1 is a match, 0 is no match, False is any problem processing the regex
2292
		if (preg_match("/^(ad|da|ada).*[0-9]{1,2}$/", $disk) !== 1) {
2293
			unset($disk_list[$id]);
2294
		}
2295
	}
2296
	sort($disk_list);
2297
	return $disk_list;
2298
}
2299

    
2300
?>
(55-55/65)