Project

General

Profile

Download (61.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 IPv4/IPv6 CIDR subnets overlap.
851
   Note: CIDR overlap implies one is identical or included so largest sn will be the same */
852
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
853
	if (is_ipaddrv4($subnet1)) {
854
		return check_subnetsv4_overlap($subnet1, $bits1, $subnet2, $bits2);
855
	} else {
856
		return check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2);
857
	}
858
}
859

    
860
/* find out whether two IPv4 CIDR subnets overlap.
861
   Note: CIDR overlap means sn1/sn2 are identical or one is included in other. So sn using largest $bits will be the same  */
862
function check_subnetsv4_overlap($subnet1, $bits1, $subnet2, $bits2) {
863
	$largest_sn = min($bits1, $bits2);
864
	$subnetv4_start1 = gen_subnetv4($subnet1, $largest_sn);
865
	$subnetv4_start2 = gen_subnetv4($subnet2, $largest_sn);
866
	
867
	if($subnetv4_start1 == '' || $subnetv4_start2 == '') {
868
		// One or both args is not a valid IPv4 subnet
869
		//FIXME: needs to return "bad data" not true/false if bad. For now return false, best we can do until fixed
870
		return false;
871
	}
872
	return ($subnetv4_start1 == $subnetv4_start2);
873
}
874

    
875
/* find out whether two IPv6 CIDR subnets overlap.
876
   Note: CIDR overlap means sn1/sn2 are identical or one is included in other. So sn using largest $bits will be the same  */
877
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
878
	$largest_sn = min($bits1, $bits2);
879
	$subnetv6_start1 = gen_subnetv6($subnet1, $largest_sn);
880
	$subnetv6_start2 = gen_subnetv6($subnet2, $largest_sn);
881
	
882
	if($subnetv6_start1 == '' || $subnetv6_start2 == '') {
883
		// One or both args is not a valid IPv6 subnet
884
		//FIXME: needs to return "bad data" not true/false if bad. For now return false, best we can do until fixed
885
		return false;
886
	}
887
	return ($subnetv6_start1 == $subnetv6_start2);
888
}
889

    
890
/* return true if $addr is in $subnet, false if not */
891
function ip_in_subnet($addr, $subnet) {
892
	if (is_ipaddrv6($addr) && is_subnetv6($subnet)) {
893
		return (Net_IPv6::isInNetmask($addr, $subnet));
894
	} else if (is_ipaddrv4($addr) && is_subnetv4($subnet)) {
895
		list($ip, $mask) = explode('/', $subnet);
896
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
897
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
898
	}
899
	return false;
900
}
901

    
902
/* returns true if $hostname is just a valid hostname (top part without any of the domain part) */
903
function is_unqualified_hostname($hostname) {
904
	if (!is_string($hostname)) {
905
		return false;
906
	}
907

    
908
	if (preg_match('/^(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])$/i', $hostname)) {
909
		return true;
910
	} else {
911
		return false;
912
	}
913
}
914

    
915
/* returns true if $hostname is a valid hostname, with or without being a fully-qualified domain name. */
916
function is_hostname($hostname, $allow_wildcard=false) {
917
	if (!is_string($hostname)) {
918
		return false;
919
	}
920

    
921
	if (is_domain($hostname, $allow_wildcard)) {
922
		if ((substr_count($hostname, ".") == 1) && ($hostname[strlen($hostname)-1] == ".")) {
923
			/* Only a single dot at the end like "test." - hosts cannot be directly in the root domain. */
924
			return false;
925
		} else {
926
			return true;
927
		}
928
	} else {
929
		return false;
930
	}
931
}
932

    
933
/* returns true if $domain is a valid domain name */
934
function is_domain($domain, $allow_wildcard=false) {
935
	if (!is_string($domain)) {
936
		return false;
937
	}
938
	if ($allow_wildcard) {
939
		$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';
940
	} else {
941
		$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';
942
	}
943

    
944
	if (preg_match($domain_regex, $domain)) {
945
		return true;
946
	} else {
947
		return false;
948
	}
949
}
950

    
951
/* returns true if $macaddr is a valid MAC address */
952
function is_macaddr($macaddr, $partial=false) {
953
	$repeat = ($partial) ? '1,5' : '5';
954
	return preg_match('/^[0-9A-F]{2}(?:[:][0-9A-F]{2}){'.$repeat.'}$/i', $macaddr) == 1 ? true : false;
955
}
956

    
957
/* returns true if $name is a valid name for an alias
958
   returns NULL if a reserved word is used
959
   returns FALSE for bad chars in the name - this allows calling code to determine what the problem was.
960
   aliases cannot be:
961
	bad chars: anything except a-z 0-9 and underscore
962
	bad names: empty string, pure numeric, pure underscore
963
	reserved words: pre-defined service/protocol/port names which should not be ambiguous, and the words "port" and  "pass" */
964

    
965
function is_validaliasname($name) {
966
	/* Array of reserved words */
967
	$reserved = array("port", "pass");
968

    
969
	if (!is_string($name) || strlen($name) >= 32 || preg_match('/(^_*$|^\d*$|[^a-z0-9_])/i', $name)) {
970
		return false;
971
	}
972
	if (in_array($name, $reserved, true) || getservbyname($name, "tcp") || getservbyname($name, "udp") || getprotobyname($name)) {
973
		return; /* return NULL */
974
	}
975
	return true;
976
}
977

    
978
/* returns true if $port is a valid TCP/UDP port */
979
function is_port($port) {
980
	if (ctype_digit($port) && ((intval($port) >= 1) && (intval($port) <= 65535))) {
981
		return true;
982
	}
983
	if (getservbyname($port, "tcp") || getservbyname($port, "udp")) {
984
		return true;
985
	}
986
	return false;
987
}
988

    
989
/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */
990
function is_portrange($portrange) {
991
	$ports = explode(":", $portrange);
992

    
993
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
994
}
995

    
996
/* returns true if $port is a valid port number or an alias thereof */
997
function is_portoralias($port) {
998
	global $config;
999

    
1000
	if (is_alias($port)) {
1001
		if (is_array($config['aliases']['alias'])) {
1002
			foreach ($config['aliases']['alias'] as $alias) {
1003
				if ($alias['name'] == $port && preg_match("/port/i", $alias['type'])) {
1004
					return true;
1005
				}
1006
			}
1007
		}
1008
		return false;
1009
	} else {
1010
		return is_port($port);
1011
	}
1012
}
1013

    
1014
/* create ranges of sequential port numbers (200:215) and remove duplicates */
1015
function group_ports($ports) {
1016
	if (!is_array($ports) || empty($ports)) {
1017
		return;
1018
	}
1019

    
1020
	$uniq = array();
1021
	foreach ($ports as $port) {
1022
		if (is_portrange($port)) {
1023
			list($begin, $end) = explode(":", $port);
1024
			if ($begin > $end) {
1025
				$aux = $begin;
1026
				$begin = $end;
1027
				$end = $aux;
1028
			}
1029
			for ($i = $begin; $i <= $end; $i++) {
1030
				if (!in_array($i, $uniq)) {
1031
					$uniq[] = $i;
1032
				}
1033
			}
1034
		} else if (is_port($port)) {
1035
			if (!in_array($port, $uniq)) {
1036
				$uniq[] = $port;
1037
			}
1038
		}
1039
	}
1040
	sort($uniq, SORT_NUMERIC);
1041

    
1042
	$result = array();
1043
	foreach ($uniq as $idx => $port) {
1044
		if ($idx == 0) {
1045
			$result[] = $port;
1046
			continue;
1047
		}
1048

    
1049
		$last = end($result);
1050
		if (is_portrange($last)) {
1051
			list($begin, $end) = explode(":", $last);
1052
		} else {
1053
			$begin = $end = $last;
1054
		}
1055

    
1056
		if ($port == ($end+1)) {
1057
			$end++;
1058
			$result[count($result)-1] = "{$begin}:{$end}";
1059
		} else {
1060
			$result[] = $port;
1061
		}
1062
	}
1063

    
1064
	return $result;
1065
}
1066

    
1067
/* returns true if $val is a valid shaper bandwidth value */
1068
function is_valid_shaperbw($val) {
1069
	return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val));
1070
}
1071

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

    
1081
/* returns true if $test is in the range between $start and $end */
1082
function is_inrange_v6($test, $start, $end) {
1083
	if ((inet_pton($test) <= inet_pton($end)) && (inet_pton($test) >= inet_pton($start))) {
1084
		return true;
1085
	} else {
1086
		return false;
1087
	}
1088
}
1089

    
1090
/* returns true if $test is in the range between $start and $end */
1091
function is_inrange($test, $start, $end) {
1092
	return is_ipaddrv6($test) ? is_inrange_v6($test, $start, $end) : is_inrange_v4($test, $start, $end);
1093
}
1094

    
1095
/* XXX: return the configured carp interface list */
1096
function get_configured_carp_interface_list($carpinterface = '', $family = 'inet', $what = 'ip') {
1097
	global $config;
1098

    
1099
	$iflist = array();
1100

    
1101
	if (!is_array($config['virtualip']['vip']) || empty($config['virtualip']['vip'])) {
1102
		return $iflist;
1103
	}
1104

    
1105
	$viparr = &$config['virtualip']['vip'];
1106
	foreach ($viparr as $vip) {
1107
		if ($vip['mode'] != "carp") {
1108
			continue;
1109
		}
1110

    
1111
		if (empty($carpinterface)) {
1112
			$iflist["_vip{$vip['uniqid']}"] = $vip['subnet'];
1113
			continue;
1114
		}
1115

    
1116
		if ($carpinterface != "_vip{$vip['uniqid']}") {
1117
			continue;
1118
		}
1119

    
1120
		switch ($what) {
1121
			case 'subnet':
1122
				if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1123
					return $vip['subnet_bits'];
1124
				} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1125
					return $vip['subnet_bits'];
1126
				}
1127
				break;
1128
			case 'iface':
1129
				if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1130
					return $vip['interface'];
1131
				} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1132
					return $vip['interface'];
1133
				}
1134
				break;
1135
			case 'vip':
1136
				if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1137
					return $vip;
1138
				} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1139
					return $vip;
1140
				}
1141
				break;
1142
			case 'ip':
1143
			default:
1144
				if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1145
					return $vip['subnet'];
1146
				} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1147
					return $vip['subnet'];
1148
				}
1149
				break;
1150
		}
1151
		break;
1152
	}
1153

    
1154
	return $iflist;
1155
}
1156

    
1157
/* return the configured IP aliases list */
1158
function get_configured_ip_aliases_list($returnfullentry = false) {
1159
	global $config;
1160

    
1161
	$alias_list = array();
1162

    
1163
	if (is_array($config['virtualip']['vip'])) {
1164
		$viparr = &$config['virtualip']['vip'];
1165
		foreach ($viparr as $vip) {
1166
			if ($vip['mode'] == "ipalias") {
1167
				if ($returnfullentry) {
1168
					$alias_list[$vip['subnet']] = $vip;
1169
				} else {
1170
					$alias_list[$vip['subnet']] = $vip['interface'];
1171
				}
1172
			}
1173
		}
1174
	}
1175

    
1176
	return $alias_list;
1177
}
1178

    
1179
/* return all configured aliases list (IP, carp, proxyarp and other) */
1180
function get_configured_vips_list() {
1181
	global $config;
1182

    
1183
	$alias_list = array();
1184

    
1185
	if (is_array($config['virtualip']['vip'])) {
1186
		$viparr = &$config['virtualip']['vip'];
1187
		foreach ($viparr as $vip) {
1188
			if ($vip['mode'] == "carp") {
1189
				$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => "_vip{$vip['uniqid']}");
1190
			} else {
1191
				$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => $vip['interface']);
1192
			}
1193
		}
1194
	}
1195

    
1196
	return $alias_list;
1197
}
1198

    
1199
/* comparison function for sorting by the order in which interfaces are normally created */
1200
function compare_interface_friendly_names($a, $b) {
1201
	if ($a == $b) {
1202
		return 0;
1203
	} else if ($a == 'wan') {
1204
		return -1;
1205
	} else if ($b == 'wan') {
1206
		return 1;
1207
	} else if ($a == 'lan') {
1208
		return -1;
1209
	} else if ($b == 'lan') {
1210
		return 1;
1211
	}
1212

    
1213
	return strnatcmp($a, $b);
1214
}
1215

    
1216
/* return the configured interfaces list. */
1217
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
1218
	global $config;
1219

    
1220
	$iflist = array();
1221

    
1222
	/* if list */
1223
	foreach ($config['interfaces'] as $if => $ifdetail) {
1224
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1225
			continue;
1226
		}
1227
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1228
			$iflist[$if] = $if;
1229
		}
1230
	}
1231

    
1232
	return $iflist;
1233
}
1234

    
1235
/* return the configured interfaces list. */
1236
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
1237
	global $config;
1238

    
1239
	$iflist = array();
1240

    
1241
	/* if list */
1242
	foreach ($config['interfaces'] as $if => $ifdetail) {
1243
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1244
			continue;
1245
		}
1246
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1247
			$tmpif = get_real_interface($if);
1248
			if (!empty($tmpif)) {
1249
				$iflist[$tmpif] = $if;
1250
			}
1251
		}
1252
	}
1253

    
1254
	return $iflist;
1255
}
1256

    
1257
/* return the configured interfaces list with their description. */
1258
function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) {
1259
	global $config;
1260

    
1261
	$iflist = array();
1262

    
1263
	/* if list */
1264
	foreach ($config['interfaces'] as $if => $ifdetail) {
1265
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1266
			continue;
1267
		}
1268
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1269
			if (empty($ifdetail['descr'])) {
1270
				$iflist[$if] = strtoupper($if);
1271
			} else {
1272
				$iflist[$if] = strtoupper($ifdetail['descr']);
1273
			}
1274
		}
1275
	}
1276

    
1277
	return $iflist;
1278
}
1279

    
1280
/*
1281
 *   get_configured_ip_addresses() - Return a list of all configured
1282
 *   interfaces IP Addresses
1283
 *
1284
 */
1285
function get_configured_ip_addresses() {
1286
	global $config;
1287

    
1288
	if (!function_exists('get_interface_ip')) {
1289
		require_once("interfaces.inc");
1290
	}
1291
	$ip_array = array();
1292
	$interfaces = get_configured_interface_list();
1293
	if (is_array($interfaces)) {
1294
		foreach ($interfaces as $int) {
1295
			$ipaddr = get_interface_ip($int);
1296
			$ip_array[$int] = $ipaddr;
1297
		}
1298
	}
1299
	$interfaces = get_configured_carp_interface_list();
1300
	if (is_array($interfaces)) {
1301
		foreach ($interfaces as $int => $ipaddr) {
1302
			$ip_array[$int] = $ipaddr;
1303
		}
1304
	}
1305

    
1306
	/* pppoe server */
1307
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
1308
		foreach ($config['pppoes']['pppoe'] as $pppoe) {
1309
			if ($pppoe['mode'] == "server") {
1310
				if (is_ipaddr($pppoe['localip'])) {
1311
					$int = "pppoes". $pppoe['pppoeid'];
1312
					$ip_array[$int] = $pppoe['localip'];
1313
				}
1314
			}
1315
		}
1316
	}
1317

    
1318
	return $ip_array;
1319
}
1320

    
1321
/*
1322
 *   get_configured_ipv6_addresses() - Return a list of all configured
1323
 *   interfaces IPv6 Addresses
1324
 *
1325
 */
1326
function get_configured_ipv6_addresses() {
1327
	require_once("interfaces.inc");
1328
	$ipv6_array = array();
1329
	$interfaces = get_configured_interface_list();
1330
	if (is_array($interfaces)) {
1331
		foreach ($interfaces as $int) {
1332
			$ipaddrv6 = get_interface_ipv6($int);
1333
			$ipv6_array[$int] = $ipaddrv6;
1334
		}
1335
	}
1336
	$interfaces = get_configured_carp_interface_list();
1337
	if (is_array($interfaces)) {
1338
		foreach ($interfaces as $int => $ipaddrv6) {
1339
			$ipv6_array[$int] = $ipaddrv6;
1340
		}
1341
	}
1342
	return $ipv6_array;
1343
}
1344

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

    
1449
			case "friendly":
1450
				if ($friendly != "") {
1451
					$toput['if'] = $ifname;
1452
					$iflist[$friendly] = $toput;
1453
				}
1454
				break;
1455
			}
1456
		}
1457
	}
1458
	return $iflist;
1459
}
1460

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

    
1483
/****f* util/log_auth
1484
* NAME
1485
*   log_auth   - Sends a string to syslog as LOG_AUTH facility
1486
* INPUTS
1487
*   $error     - string containing the syslog message.
1488
* RESULT
1489
*   null
1490
******/
1491
function log_auth($error) {
1492
	global $g;
1493
	$page = $_SERVER['SCRIPT_NAME'];
1494
	syslog(LOG_AUTH, "$page: $error");
1495
	if ($g['debug']) {
1496
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1497
	}
1498
	return;
1499
}
1500

    
1501
/****f* util/exec_command
1502
 * NAME
1503
 *   exec_command - Execute a command and return a string of the result.
1504
 * INPUTS
1505
 *   $command   - String of the command to be executed.
1506
 * RESULT
1507
 *   String containing the command's result.
1508
 * NOTES
1509
 *   This function returns the command's stdout and stderr.
1510
 ******/
1511
function exec_command($command) {
1512
	$output = array();
1513
	exec($command . ' 2>&1', $output);
1514
	return(implode("\n", $output));
1515
}
1516

    
1517
/* wrapper for exec()
1518
   Executes in background or foreground.
1519
   For background execution, returns PID of background process to allow calling code control */
1520
function mwexec($command, $nologentry = false, $clearsigmask = false, $background = false) {
1521
	global $g;
1522
	$retval = 0;
1523

    
1524
	if ($g['debug']) {
1525
		if (!$_SERVER['REMOTE_ADDR']) {
1526
			echo "mwexec(): $command" . ($background ? " [BG]":"") . "\n";
1527
		}
1528
	}
1529
	if ($clearsigmask) {
1530
		$oldset = array();
1531
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1532
	}
1533

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

    
1546
	if ($clearsigmask) {
1547
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1548
	}
1549

    
1550
	return $retval;
1551
}
1552

    
1553
/* wrapper for exec() in background */
1554
function mwexec_bg($command, $clearsigmask = false) {
1555
	return mwexec($command, false, $clearsigmask, true);
1556
}
1557

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

    
1573
	$aliastable = array();
1574

    
1575
	if (is_array($config['aliases']['alias'])) {
1576
		foreach ($config['aliases']['alias'] as $alias) {
1577
			if ($alias['name']) {
1578
				$aliastable[$alias['name']] = $alias['address'];
1579
			}
1580
		}
1581
	}
1582
}
1583

    
1584
/* check if an alias exists */
1585
function is_alias($name) {
1586
	global $aliastable;
1587

    
1588
	return isset($aliastable[$name]);
1589
}
1590

    
1591
function alias_get_type($name) {
1592
	global $config;
1593

    
1594
	if (is_array($config['aliases']['alias'])) {
1595
		foreach ($config['aliases']['alias'] as $alias) {
1596
			if ($name == $alias['name']) {
1597
				return $alias['type'];
1598
			}
1599
		}
1600
	}
1601

    
1602
	return "";
1603
}
1604

    
1605
/* expand a host or network alias, if necessary */
1606
function alias_expand($name) {
1607
	global $aliastable;
1608

    
1609
	if (isset($aliastable[$name])) {
1610
		// alias names cannot be strictly numeric. redmine #4289
1611
		if (is_numericint($name)) {
1612
			return null;
1613
		}
1614
		return "\${$name}";
1615
	} else if (is_ipaddr($name) || is_subnet($name) || is_port($name) || is_portrange($name)) {
1616
		return "{$name}";
1617
	} else {
1618
		return null;
1619
	}
1620
}
1621

    
1622
function alias_expand_urltable($name) {
1623
	global $config;
1624
	$urltable_prefix = "/var/db/aliastables/";
1625
	$urltable_filename = $urltable_prefix . $name . ".txt";
1626

    
1627
	if (is_array($config['aliases']['alias'])) {
1628
		foreach ($config['aliases']['alias'] as $alias) {
1629
			if (preg_match("/urltable/i", $alias['type']) && ($alias['name'] == $name)) {
1630
				if (is_URL($alias["url"]) && file_exists($urltable_filename) && filesize($urltable_filename)) {
1631
					return $urltable_filename;
1632
				} else {
1633
					send_event("service sync alias {$name}");
1634
					break;
1635
				}
1636
			}
1637
		}
1638
	}
1639
	return null;
1640
}
1641

    
1642
/* obtain MAC address given an IP address by looking at the ARP table */
1643
function arp_get_mac_by_ip($ip) {
1644
	mwexec("/sbin/ping -c 1 -t 1 " . escapeshellarg($ip), true);
1645
	$arpoutput = "";
1646
	exec("/usr/sbin/arp -n " . escapeshellarg($ip), $arpoutput);
1647

    
1648
	if ($arpoutput[0]) {
1649
		$arpi = explode(" ", $arpoutput[0]);
1650
		$macaddr = $arpi[3];
1651
		if (is_macaddr($macaddr)) {
1652
			return $macaddr;
1653
		} else {
1654
			return false;
1655
		}
1656
	}
1657

    
1658
	return false;
1659
}
1660

    
1661
/* return a fieldname that is safe for xml usage */
1662
function xml_safe_fieldname($fieldname) {
1663
	$replace = array('/', '-', ' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
1664
			 '_', '+', '=', '{', '}', '[', ']', '|', '/', '<', '>', '?',
1665
			 ':', ',', '.', '\'', '\\'
1666
		);
1667
	return strtolower(str_replace($replace, "", $fieldname));
1668
}
1669

    
1670
function mac_format($clientmac) {
1671
	global $config, $cpzone;
1672

    
1673
	$mac = explode(":", $clientmac);
1674
	$mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1675

    
1676
	switch ($mac_format) {
1677
		case 'singledash':
1678
			return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1679

    
1680
		case 'ietf':
1681
			return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1682

    
1683
		case 'cisco':
1684
			return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1685

    
1686
		case 'unformatted':
1687
			return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1688

    
1689
		default:
1690
			return $clientmac;
1691
	}
1692
}
1693

    
1694
function resolve_retry($hostname, $retries = 5) {
1695

    
1696
	if (is_ipaddr($hostname)) {
1697
		return $hostname;
1698
	}
1699

    
1700
	for ($i = 0; $i < $retries; $i++) {
1701
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1702
		$ip = gethostbyname($hostname);
1703

    
1704
		if ($ip && $ip != $hostname) {
1705
			/* success */
1706
			return $ip;
1707
		}
1708

    
1709
		sleep(1);
1710
	}
1711

    
1712
	return false;
1713
}
1714

    
1715
function format_bytes($bytes) {
1716
	if ($bytes >= 1073741824) {
1717
		return sprintf("%.2f GB", $bytes/1073741824);
1718
	} else if ($bytes >= 1048576) {
1719
		return sprintf("%.2f MB", $bytes/1048576);
1720
	} else if ($bytes >= 1024) {
1721
		return sprintf("%.0f KB", $bytes/1024);
1722
	} else {
1723
		return sprintf("%d B", $bytes);
1724
	}
1725
}
1726

    
1727
function format_number($num, $precision = 3) {
1728
	$units = array('', 'K', 'M', 'G', 'T');
1729

    
1730
	$i = 0;
1731
	while ($num > 1000 && $i < count($units)) {
1732
		$num /= 1000;
1733
		$i++;
1734
	}
1735
	round($num, $precision);
1736

    
1737
	return ("$num {$units[$i]}");
1738
}
1739

    
1740
function update_filter_reload_status($text) {
1741
	global $g;
1742

    
1743
	file_put_contents("{$g['varrun_path']}/filter_reload_status", $text);
1744
}
1745

    
1746
/****** util/return_dir_as_array
1747
 * NAME
1748
 *   return_dir_as_array - Return a directory's contents as an array.
1749
 * INPUTS
1750
 *   $dir          - string containing the path to the desired directory.
1751
 *   $filter_regex - string containing a regular expression to filter file names. Default empty.
1752
 * RESULT
1753
 *   $dir_array - array containing the directory's contents. This array will be empty if the path specified is invalid.
1754
 ******/
1755
function return_dir_as_array($dir, $filter_regex = '') {
1756
	$dir_array = array();
1757
	if (is_dir($dir)) {
1758
		if ($dh = opendir($dir)) {
1759
			while (($file = readdir($dh)) !== false) {
1760
				if (($file == ".") || ($file == "..")) {
1761
					continue;
1762
				}
1763

    
1764
				if (empty($filter_regex) || preg_match($filter_regex, $file)) {
1765
					array_push($dir_array, $file);
1766
				}
1767
			}
1768
			closedir($dh);
1769
		}
1770
	}
1771
	return $dir_array;
1772
}
1773

    
1774
function run_plugins($directory) {
1775
	global $config, $g;
1776

    
1777
	/* process packager manager custom rules */
1778
	$files = return_dir_as_array($directory);
1779
	if (is_array($files)) {
1780
		foreach ($files as $file) {
1781
			if (stristr($file, ".sh") == true) {
1782
				mwexec($directory . $file . " start");
1783
			} else if (!is_dir($directory . "/" . $file) && stristr($file, ".inc")) {
1784
				require_once($directory . "/" . $file);
1785
			}
1786
		}
1787
	}
1788
}
1789

    
1790
/*
1791
 *    safe_mkdir($path, $mode = 0755)
1792
 *    create directory if it doesn't already exist and isn't a file!
1793
 */
1794
function safe_mkdir($path, $mode = 0755) {
1795
	global $g;
1796

    
1797
	if (!is_file($path) && !is_dir($path)) {
1798
		return @mkdir($path, $mode, true);
1799
	} else {
1800
		return false;
1801
	}
1802
}
1803

    
1804
/*
1805
 * get_sysctl($names)
1806
 * Get values of sysctl OID's listed in $names (accepts an array or a single
1807
 * name) and return an array of key/value pairs set for those that exist
1808
 */
1809
function get_sysctl($names) {
1810
	if (empty($names)) {
1811
		return array();
1812
	}
1813

    
1814
	if (is_array($names)) {
1815
		$name_list = array();
1816
		foreach ($names as $name) {
1817
			$name_list[] = escapeshellarg($name);
1818
		}
1819
	} else {
1820
		$name_list = array(escapeshellarg($names));
1821
	}
1822

    
1823
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1824
	$values = array();
1825
	foreach ($output as $line) {
1826
		$line = explode(": ", $line, 2);
1827
		if (count($line) == 2) {
1828
			$values[$line[0]] = $line[1];
1829
		}
1830
	}
1831

    
1832
	return $values;
1833
}
1834

    
1835
/*
1836
 * get_single_sysctl($name)
1837
 * Wrapper for get_sysctl() to simplify read of a single sysctl value
1838
 * return the value for sysctl $name or empty string if it doesn't exist
1839
 */
1840
function get_single_sysctl($name) {
1841
	if (empty($name)) {
1842
		return "";
1843
	}
1844

    
1845
	$value = get_sysctl($name);
1846
	if (empty($value) || !isset($value[$name])) {
1847
		return "";
1848
	}
1849

    
1850
	return $value[$name];
1851
}
1852

    
1853
/*
1854
 * set_sysctl($value_list)
1855
 * Set sysctl OID's listed as key/value pairs and return
1856
 * an array with keys set for those that succeeded
1857
 */
1858
function set_sysctl($values) {
1859
	if (empty($values)) {
1860
		return array();
1861
	}
1862

    
1863
	$value_list = array();
1864
	foreach ($values as $key => $value) {
1865
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
1866
	}
1867

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

    
1870
	/* Retry individually if failed (one or more read-only) */
1871
	if ($success <> 0 && count($value_list) > 1) {
1872
		foreach ($value_list as $value) {
1873
			exec("/sbin/sysctl -i " . $value, $output);
1874
		}
1875
	}
1876

    
1877
	$ret = array();
1878
	foreach ($output as $line) {
1879
		$line = explode(": ", $line, 2);
1880
		if (count($line) == 2) {
1881
			$ret[$line[0]] = true;
1882
		}
1883
	}
1884

    
1885
	return $ret;
1886
}
1887

    
1888
/*
1889
 * set_single_sysctl($name, $value)
1890
 * Wrapper to set_sysctl() to make it simple to set only one sysctl
1891
 * returns boolean meaning if it succeeded
1892
 */
1893
function set_single_sysctl($name, $value) {
1894
	if (empty($name)) {
1895
		return false;
1896
	}
1897

    
1898
	$result = set_sysctl(array($name => $value));
1899

    
1900
	if (!isset($result[$name]) || $result[$name] != $value) {
1901
		return false;
1902
	}
1903

    
1904
	return true;
1905
}
1906

    
1907
/*
1908
 *     get_memory()
1909
 *     returns an array listing the amount of
1910
 *     memory installed in the hardware
1911
 *     [0] net memory available for the OS (FreeBSD) after some is taken by BIOS, video or whatever - e.g. 235 MBytes
1912
 *     [1] real (actual) memory of the system, should be the size of the RAM card/s - e.g. 256 MBytes
1913
 */
1914
function get_memory() {
1915
	$physmem = get_single_sysctl("hw.physmem");
1916
	$realmem = get_single_sysctl("hw.realmem");
1917
	/* convert from bytes to megabytes */
1918
	return array(($physmem/1048576), ($realmem/1048576));
1919
}
1920

    
1921
function mute_kernel_msgs() {
1922
	global $g, $config;
1923
	// Do not mute serial console.  The kernel gets very very cranky
1924
	// and will start dishing you cannot control tty errors.
1925
	if ($g['platform'] == 'nanobsd') {
1926
		return;
1927
	}
1928
	if ($config['system']['enableserial']) {
1929
		return;
1930
	}
1931
	exec("/sbin/conscontrol mute on");
1932
}
1933

    
1934
function unmute_kernel_msgs() {
1935
	global $g;
1936
	// Do not mute serial console.  The kernel gets very very cranky
1937
	// and will start dishing you cannot control tty errors.
1938
	if ($g['platform'] == 'nanobsd') {
1939
		return;
1940
	}
1941
	exec("/sbin/conscontrol mute off");
1942
}
1943

    
1944
function start_devd() {
1945
	/* Use the undocumented -q options of devd to quiet its log spamming */
1946
	$_gb = exec("/sbin/devd -q");
1947
	sleep(1);
1948
	unset($_gb);
1949
}
1950

    
1951
function is_interface_vlan_mismatch() {
1952
	global $config, $g;
1953

    
1954
	if (is_array($config['vlans']['vlan'])) {
1955
		foreach ($config['vlans']['vlan'] as $vlan) {
1956
			if (does_interface_exist($vlan['if']) == false) {
1957
				return true;
1958
			}
1959
		}
1960
	}
1961

    
1962
	return false;
1963
}
1964

    
1965
function is_interface_mismatch() {
1966
	global $config, $g;
1967

    
1968
	$do_assign = false;
1969
	$i = 0;
1970
	$missing_interfaces = array();
1971
	if (is_array($config['interfaces'])) {
1972
		foreach ($config['interfaces'] as $ifname => $ifcfg) {
1973
			if (preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_wlan/i", $ifcfg['if'])) {
1974
				// Do not check these interfaces.
1975
				$i++;
1976
				continue;
1977
			} else if (does_interface_exist($ifcfg['if']) == false) {
1978
				$missing_interfaces[] = $ifcfg['if'];
1979
				$do_assign = true;
1980
			} else {
1981
				$i++;
1982
			}
1983
		}
1984
	}
1985

    
1986
	if (file_exists("{$g['tmp_path']}/assign_complete")) {
1987
		$do_assign = false;
1988
	}
1989

    
1990
	if (!empty($missing_interfaces) && $do_assign) {
1991
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
1992
	} else {
1993
		@unlink("{$g['tmp_path']}/missing_interfaces");
1994
	}
1995

    
1996
	return $do_assign;
1997
}
1998

    
1999
/* sync carp entries to other firewalls */
2000
function carp_sync_client() {
2001
	global $g;
2002
	send_event("filter sync");
2003
}
2004

    
2005
/****f* util/isAjax
2006
 * NAME
2007
 *   isAjax - reports if the request is driven from prototype
2008
 * INPUTS
2009
 *   none
2010
 * RESULT
2011
 *   true/false
2012
 ******/
2013
function isAjax() {
2014
	return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
2015
}
2016

    
2017
/****f* util/timeout
2018
 * NAME
2019
 *   timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space.
2020
 * INPUTS
2021
 *   optional, seconds to wait before timeout. Default 9 seconds.
2022
 * RESULT
2023
 *   returns 1 char of user input or null if no input.
2024
 ******/
2025
function timeout($timer = 9) {
2026
	while (!isset($key)) {
2027
		if ($timer >= 9) {
2028
			echo chr(8) . chr(8) . ($timer == 9 ? chr(32) : null) . "{$timer}";
2029
		} else {
2030
			echo chr(8). "{$timer}";
2031
		}
2032
		`/bin/stty -icanon min 0 time 25`;
2033
		$key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`);
2034
		`/bin/stty icanon`;
2035
		if ($key == '') {
2036
			unset($key);
2037
		}
2038
		$timer--;
2039
		if ($timer == 0) {
2040
			break;
2041
		}
2042
	}
2043
	return $key;
2044
}
2045

    
2046
/****f* util/msort
2047
 * NAME
2048
 *   msort - sort array
2049
 * INPUTS
2050
 *   $array to be sorted, field to sort by, direction of sort
2051
 * RESULT
2052
 *   returns newly sorted array
2053
 ******/
2054
function msort($array, $id = "id", $sort_ascending = true) {
2055
	$temp_array = array();
2056
	while (count($array)>0) {
2057
		$lowest_id = 0;
2058
		$index = 0;
2059
		foreach ($array as $item) {
2060
			if (isset($item[$id])) {
2061
				if ($array[$lowest_id][$id]) {
2062
					if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
2063
						$lowest_id = $index;
2064
					}
2065
				}
2066
			}
2067
			$index++;
2068
		}
2069
		$temp_array[] = $array[$lowest_id];
2070
		$array = array_merge(array_slice($array, 0, $lowest_id), array_slice($array, $lowest_id + 1));
2071
	}
2072
	if ($sort_ascending) {
2073
		return $temp_array;
2074
	} else {
2075
		return array_reverse($temp_array);
2076
	}
2077
}
2078

    
2079
/****f* util/is_URL
2080
 * NAME
2081
 *   is_URL
2082
 * INPUTS
2083
 *   string to check
2084
 * RESULT
2085
 *   Returns true if item is a URL
2086
 ******/
2087
function is_URL($url) {
2088
	$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
2089
	if ($match) {
2090
		return true;
2091
	}
2092
	return false;
2093
}
2094

    
2095
function is_file_included($file = "") {
2096
	$files = get_included_files();
2097
	if (in_array($file, $files)) {
2098
		return true;
2099
	}
2100

    
2101
	return false;
2102
}
2103

    
2104
/*
2105
 * Replace a value on a deep associative array using regex
2106
 */
2107
function array_replace_values_recursive($data, $match, $replace) {
2108
	if (empty($data)) {
2109
		return $data;
2110
	}
2111

    
2112
	if (is_string($data)) {
2113
		$data = preg_replace("/{$match}/", $replace, $data);
2114
	} else if (is_array($data)) {
2115
		foreach ($data as $k => $v) {
2116
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
2117
		}
2118
	}
2119

    
2120
	return $data;
2121
}
2122

    
2123
/*
2124
	This function was borrowed from a comment on PHP.net at the following URL:
2125
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
2126
 */
2127
function array_merge_recursive_unique($array0, $array1) {
2128

    
2129
	$arrays = func_get_args();
2130
	$remains = $arrays;
2131

    
2132
	// We walk through each arrays and put value in the results (without
2133
	// considering previous value).
2134
	$result = array();
2135

    
2136
	// loop available array
2137
	foreach ($arrays as $array) {
2138

    
2139
		// The first remaining array is $array. We are processing it. So
2140
		// we remove it from remaining arrays.
2141
		array_shift($remains);
2142

    
2143
		// We don't care non array param, like array_merge since PHP 5.0.
2144
		if (is_array($array)) {
2145
			// Loop values
2146
			foreach ($array as $key => $value) {
2147
				if (is_array($value)) {
2148
					// we gather all remaining arrays that have such key available
2149
					$args = array();
2150
					foreach ($remains as $remain) {
2151
						if (array_key_exists($key, $remain)) {
2152
							array_push($args, $remain[$key]);
2153
						}
2154
					}
2155

    
2156
					if (count($args) > 2) {
2157
						// put the recursion
2158
						$result[$key] = call_user_func_array(__FUNCTION__, $args);
2159
					} else {
2160
						foreach ($value as $vkey => $vval) {
2161
							$result[$key][$vkey] = $vval;
2162
						}
2163
					}
2164
				} else {
2165
					// simply put the value
2166
					$result[$key] = $value;
2167
				}
2168
			}
2169
		}
2170
	}
2171
	return $result;
2172
}
2173

    
2174

    
2175
/*
2176
 * converts a string like "a,b,c,d"
2177
 * into an array like array("a" => "b", "c" => "d")
2178
 */
2179
function explode_assoc($delimiter, $string) {
2180
	$array = explode($delimiter, $string);
2181
	$result = array();
2182
	$numkeys = floor(count($array) / 2);
2183
	for ($i = 0; $i < $numkeys; $i += 1) {
2184
		$result[$array[$i * 2]] = $array[$i * 2 + 1];
2185
	}
2186
	return $result;
2187
}
2188

    
2189
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false) {
2190
	global $config, $aliastable;
2191

    
2192
	/* Bail if there are no routes, but return an array always so callers don't have to check. */
2193
	if (!is_array($config['staticroutes']['route'])) {
2194
		return array();
2195
	}
2196

    
2197
	$allstaticroutes = array();
2198
	$allsubnets = array();
2199
	/* Loop through routes and expand aliases as we find them. */
2200
	foreach ($config['staticroutes']['route'] as $route) {
2201
		if (is_alias($route['network'])) {
2202
			if (!isset($aliastable[$route['network']])) {
2203
				continue;
2204
			}
2205

    
2206
			$subnets = preg_split('/\s+/', $aliastable[$route['network']]);
2207
			foreach ($subnets as $net) {
2208
				if (!is_subnet($net)) {
2209
					if (is_ipaddrv4($net)) {
2210
						$net .= "/32";
2211
					} else if (is_ipaddrv6($net)) {
2212
						$net .= "/128";
2213
					} else if ($returnhostnames === false || !is_fqdn($net)) {
2214
						continue;
2215
					}
2216
				}
2217
				$temproute = $route;
2218
				$temproute['network'] = $net;
2219
				$allstaticroutes[] = $temproute;
2220
				$allsubnets[] = $net;
2221
			}
2222
		} elseif (is_subnet($route['network'])) {
2223
			$allstaticroutes[] = $route;
2224
			$allsubnets[] = $route['network'];
2225
		}
2226
	}
2227
	if ($returnsubnetsonly) {
2228
		return $allsubnets;
2229
	} else {
2230
		return $allstaticroutes;
2231
	}
2232
}
2233

    
2234
/****f* util/get_alias_list
2235
 * NAME
2236
 *   get_alias_list - Provide a list of aliases.
2237
 * INPUTS
2238
 *   $type          - Optional, can be a string or array specifying what type(s) of aliases you need.
2239
 * RESULT
2240
 *   Array containing list of aliases.
2241
 *   If $type is unspecified, all aliases are returned.
2242
 *   If $type is a string, all aliases of the type specified in $type are returned.
2243
 *   If $type is an array, all aliases of any type specified in any element of $type are returned.
2244
 */
2245
function get_alias_list($type = null) {
2246
	global $config;
2247
	$result = array();
2248
	if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) {
2249
		foreach ($config['aliases']['alias'] as $alias) {
2250
			if ($type === null) {
2251
				$result[] = $alias['name'];
2252
			} else if (is_array($type)) {
2253
				if (in_array($alias['type'], $type)) {
2254
					$result[] = $alias['name'];
2255
				}
2256
			} else if ($type === $alias['type']) {
2257
				$result[] = $alias['name'];
2258
			}
2259
		}
2260
	}
2261
	return $result;
2262
}
2263

    
2264
/* returns an array consisting of every element of $haystack that is not equal to $needle. */
2265
function array_exclude($needle, $haystack) {
2266
	$result = array();
2267
	if (is_array($haystack)) {
2268
		foreach ($haystack as $thing) {
2269
			if ($needle !== $thing) {
2270
				$result[] = $thing;
2271
			}
2272
		}
2273
	}
2274
	return $result;
2275
}
2276

    
2277
/* Define what is preferred, IPv4 or IPv6 */
2278
function prefer_ipv4_or_ipv6() {
2279
	global $config;
2280

    
2281
	if (isset($config['system']['prefer_ipv4'])) {
2282
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv4");
2283
	} else {
2284
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv6");
2285
	}
2286
}
2287

    
2288
/* Redirect to page passing parameters via POST */
2289
function post_redirect($page, $params) {
2290
	if (!is_array($params)) {
2291
		return;
2292
	}
2293

    
2294
	print "<html><body><form action=\"{$page}\" name=\"formredir\" method=\"post\">\n";
2295
	foreach ($params as $key => $value) {
2296
		print "<input type=\"hidden\" name=\"{$key}\" value=\"{$value}\" />\n";
2297
	}
2298
	print "</form>\n";
2299
	print "<script type=\"text/javascript\">\n";
2300
	print "//<![CDATA[\n";
2301
	print "document.formredir.submit();\n";
2302
	print "//]]>\n";
2303
	print "</script>\n";
2304
	print "</body></html>\n";
2305
}
2306

    
2307
/* Locate disks that can be queried for S.M.A.R.T. data. */
2308
function get_smart_drive_list() {
2309
	$disk_list = explode(" ", get_single_sysctl("kern.disks"));
2310
	foreach ($disk_list as $id => $disk) {
2311
		// We only want certain kinds of disks for S.M.A.R.T.
2312
		// 1 is a match, 0 is no match, False is any problem processing the regex
2313
		if (preg_match("/^(ad|da|ada).*[0-9]{1,2}$/", $disk) !== 1) {
2314
			unset($disk_list[$id]);
2315
		}
2316
	}
2317
	sort($disk_list);
2318
	return $disk_list;
2319
}
2320

    
2321
?>
(55-55/65)