Project

General

Profile

Download (70.5 KB) Statistics
| Branch: | Tag: | Revision:
1 86a5e1a8 Renato Botelho
<?php
2 417fc5c4 Scott Ullrich
/*
3 ac24dc24 Renato Botelho
 * util.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6 81299b5c Renato Botelho
 * Copyright (c) 2004-2016 Rubicon Communications, LLC (Netgate)
7 ac24dc24 Renato Botelho
 * All rights reserved.
8
 *
9
 * originally part of m0n0wall (http://m0n0.ch/wall)
10 c5d81585 Renato Botelho
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
11 ac24dc24 Renato Botelho
 * All rights reserved.
12
 *
13 b12ea3fb Renato Botelho
 * Licensed under the Apache License, Version 2.0 (the "License");
14
 * you may not use this file except in compliance with the License.
15
 * You may obtain a copy of the License at
16 ac24dc24 Renato Botelho
 *
17 b12ea3fb Renato Botelho
 * http://www.apache.org/licenses/LICENSE-2.0
18 ac24dc24 Renato Botelho
 *
19 b12ea3fb Renato Botelho
 * Unless required by applicable law or agreed to in writing, software
20
 * distributed under the License is distributed on an "AS IS" BASIS,
21
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
 * See the License for the specific language governing permissions and
23
 * limitations under the License.
24 995df6c3 Stephen Beaver
 */
25 523855b0 Scott Ullrich
26 820562e8 NewEraCracker
define('VIP_ALL', 1);
27
define('VIP_CARP', 2);
28
define('VIP_IPALIAS', 3);
29 ce94deb0 Luiz Otavio O Souza
30 5b237745 Scott Ullrich
/* kill a process by pid file */
31
function killbypid($pidfile) {
32 435a418f Ermal
	return sigkillbypid($pidfile, "TERM");
33 5b237745 Scott Ullrich
}
34
35 c4594e36 Phil Davis
function isvalidpid($pidfile) {
36 0e604b3a Ermal
	$output = "";
37 c4594e36 Phil Davis
	if (file_exists($pidfile)) {
38 b885c8cf Renato Botelho
		exec("/bin/pgrep -qnF {$pidfile} 2>/dev/null", $output, $retval);
39 c4594e36 Phil Davis
		return (intval($retval) == 0);
40
	}
41
	return false;
42 53aca1fd Scott Ullrich
}
43
44 6dc3a5c2 Ermal Lu?i
function is_process_running($process) {
45 01d4b621 Ermal
	$output = "";
46 873c1701 Renato Botelho
	exec("/bin/pgrep -anx " . escapeshellarg($process), $output, $retval);
47 6dc3a5c2 Ermal Lu?i
48 5bbd08e1 Warren Baker
	return (intval($retval) == 0);
49 6dc3a5c2 Ermal Lu?i
}
50
51 53aca1fd Scott Ullrich
function isvalidproc($proc) {
52 ba8495f0 Ermal
	return is_process_running($proc);
53 53aca1fd Scott Ullrich
}
54
55 5b237745 Scott Ullrich
/* sigkill a process by pid file */
56 53aca1fd Scott Ullrich
/* return 1 for success and 0 for a failure */
57 5b237745 Scott Ullrich
function sigkillbypid($pidfile, $sig) {
58 0b8b5069 Renato Botelho
	if (isvalidpid($pidfile)) {
59
		return mwexec("/bin/pkill " . escapeshellarg("-{$sig}") .
60
		    " -F {$pidfile}", true);
61 751533a2 Phil Davis
	}
62 ba8495f0 Ermal
63 53aca1fd Scott Ullrich
	return 0;
64
}
65
66
/* kill a process by name */
67
function sigkillbyname($procname, $sig) {
68 751533a2 Phil Davis
	if (isvalidproc($procname)) {
69 873c1701 Renato Botelho
		return mwexec("/usr/bin/killall " . escapeshellarg("-{$sig}") . " " . escapeshellarg($procname), true);
70 751533a2 Phil Davis
	}
71 5b237745 Scott Ullrich
}
72
73
/* kill a process by name */
74
function killbyname($procname) {
75 751533a2 Phil Davis
	if (isvalidproc($procname)) {
76 53aca1fd Scott Ullrich
		mwexec("/usr/bin/killall " . escapeshellarg($procname));
77 751533a2 Phil Davis
	}
78 5b237745 Scott Ullrich
}
79
80 a368a026 Ermal Lu?i
function is_subsystem_dirty($subsystem = "") {
81
	global $g;
82
83 751533a2 Phil Davis
	if ($subsystem == "") {
84 a368a026 Ermal Lu?i
		return false;
85 751533a2 Phil Davis
	}
86 a368a026 Ermal Lu?i
87 751533a2 Phil Davis
	if (file_exists("{$g['varrun_path']}/{$subsystem}.dirty")) {
88 a368a026 Ermal Lu?i
		return true;
89 751533a2 Phil Davis
	}
90 a368a026 Ermal Lu?i
91
	return false;
92
}
93
94
function mark_subsystem_dirty($subsystem = "") {
95
	global $g;
96
97 751533a2 Phil Davis
	if (!file_put_contents("{$g['varrun_path']}/{$subsystem}.dirty", "DIRTY")) {
98 fd7b47b6 Renato Botelho
		log_error(sprintf(gettext("WARNING: Could not mark subsystem: %s dirty"), $subsystem));
99 751533a2 Phil Davis
	}
100 a368a026 Ermal Lu?i
}
101
102
function clear_subsystem_dirty($subsystem = "") {
103
	global $g;
104
105
	@unlink("{$g['varrun_path']}/{$subsystem}.dirty");
106
}
107
108 0027de0a Ermal Lu?i
function config_lock() {
109
	return;
110
}
111
function config_unlock() {
112
	return;
113
}
114
115
/* lock configuration file */
116 b6c34bfc Ermal
function lock($lock, $op = LOCK_SH) {
117 9e7ef1a5 Scott Ullrich
	global $g, $cfglckkeyconsumers;
118 751533a2 Phil Davis
	if (!$lock) {
119 530e4707 NOYB
		die(gettext("WARNING: A name must be given as parameter to lock() function."));
120 751533a2 Phil Davis
	}
121 6bee76d5 Ermal
	if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) {
122 9e7ef1a5 Scott Ullrich
		@touch("{$g['tmp_path']}/{$lock}.lock");
123 6bee76d5 Ermal
		@chmod("{$g['tmp_path']}/{$lock}.lock", 0666);
124
	}
125 9e7ef1a5 Scott Ullrich
	$cfglckkeyconsumers++;
126 b6c34bfc Ermal
	if ($fp = fopen("{$g['tmp_path']}/{$lock}.lock", "w")) {
127 751533a2 Phil Davis
		if (flock($fp, $op)) {
128 9e7ef1a5 Scott Ullrich
			return $fp;
129 751533a2 Phil Davis
		} else {
130 b6c34bfc Ermal
			fclose($fp);
131 751533a2 Phil Davis
		}
132 9e7ef1a5 Scott Ullrich
	}
133 0027de0a Ermal Lu?i
}
134
135 8171a2c2 Ermal
function try_lock($lock, $timeout = 5) {
136
	global $g, $cfglckkeyconsumers;
137 751533a2 Phil Davis
	if (!$lock) {
138 530e4707 NOYB
		die(gettext("WARNING: A name must be given as parameter to try_lock() function."));
139 751533a2 Phil Davis
	}
140 8171a2c2 Ermal
	if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) {
141
		@touch("{$g['tmp_path']}/{$lock}.lock");
142
		@chmod("{$g['tmp_path']}/{$lock}.lock", 0666);
143
	}
144
	$cfglckkeyconsumers++;
145
	if ($fp = fopen("{$g['tmp_path']}/{$lock}.lock", "w")) {
146
		$trycounter = 0;
147 751533a2 Phil Davis
		while (!flock($fp, LOCK_EX | LOCK_NB)) {
148 8171a2c2 Ermal
			if ($trycounter >= $timeout) {
149
				fclose($fp);
150
				return NULL;
151
			}
152
			sleep(1);
153
			$trycounter++;
154
		}
155
156
		return $fp;
157
	}
158
159
	return NULL;
160
}
161
162 0027de0a Ermal Lu?i
/* unlock configuration file */
163
function unlock($cfglckkey = 0) {
164 9e7ef1a5 Scott Ullrich
	global $g, $cfglckkeyconsumers;
165
	flock($cfglckkey, LOCK_UN);
166 cb6fd90b Ermal Lu?i
	fclose($cfglckkey);
167 9e7ef1a5 Scott Ullrich
	return;
168 0027de0a Ermal Lu?i
}
169
170 8171a2c2 Ermal
/* unlock forcefully configuration file */
171
function unlock_force($lock) {
172
	global $g;
173
174
	@unlink("{$g['tmp_path']}/{$lock}.lock");
175
}
176
177 0ae6daf8 Ermal
function send_event($cmd) {
178
	global $g;
179
180 751533a2 Phil Davis
	if (!isset($g['event_address'])) {
181 1015b3a9 Warren Baker
		$g['event_address'] = "unix:///var/run/check_reload_status";
182 751533a2 Phil Davis
	}
183 86a5e1a8 Renato Botelho
184 838feb14 Ermal
	$try = 0;
185
	while ($try < 3) {
186
		$fd = @fsockopen($g['event_address']);
187
		if ($fd) {
188
			fwrite($fd, $cmd);
189
			$resp = fread($fd, 4096);
190 751533a2 Phil Davis
			if ($resp != "OK\n") {
191 838feb14 Ermal
				log_error("send_event: sent {$cmd} got {$resp}");
192 751533a2 Phil Davis
			}
193 838feb14 Ermal
			fclose($fd);
194
			$try = 3;
195 751533a2 Phil Davis
		} else if (!is_process_running("check_reload_status")) {
196 838feb14 Ermal
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
197 751533a2 Phil Davis
		}
198 838feb14 Ermal
		$try++;
199 0ae6daf8 Ermal
	}
200
}
201
202
function send_multiple_events($cmds) {
203 1015b3a9 Warren Baker
	global $g;
204 0ae6daf8 Ermal
205 751533a2 Phil Davis
	if (!isset($g['event_address'])) {
206 1015b3a9 Warren Baker
		$g['event_address'] = "unix:///var/run/check_reload_status";
207 751533a2 Phil Davis
	}
208 86a5e1a8 Renato Botelho
209 751533a2 Phil Davis
	if (!is_array($cmds)) {
210 0ae6daf8 Ermal
		return;
211 751533a2 Phil Davis
	}
212 6e1f456f Ermal
213 915089b7 Ermal
	while ($try < 3) {
214
		$fd = @fsockopen($g['event_address']);
215
		if ($fd) {
216
			foreach ($cmds as $cmd) {
217
				fwrite($fd, $cmd);
218
				$resp = fread($fd, 4096);
219 751533a2 Phil Davis
				if ($resp != "OK\n") {
220 915089b7 Ermal
					log_error("send_event: sent {$cmd} got {$resp}");
221 751533a2 Phil Davis
				}
222 915089b7 Ermal
			}
223
			fclose($fd);
224
			$try = 3;
225 751533a2 Phil Davis
		} else if (!is_process_running("check_reload_status")) {
226 915089b7 Ermal
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
227 751533a2 Phil Davis
		}
228 915089b7 Ermal
		$try++;
229
	}
230 0ae6daf8 Ermal
}
231
232 1ab56363 Ermal Lu?i
function is_module_loaded($module_name) {
233 302c005e Ermal
	$module_name = str_replace(".ko", "", $module_name);
234
	$running = 0;
235 ec25f18a Renato Botelho
	$_gb = exec("/sbin/kldstat -qn {$module_name} 2>&1", $_gb, $running);
236 751533a2 Phil Davis
	if (intval($running) == 0) {
237 1ab56363 Ermal Lu?i
		return true;
238 751533a2 Phil Davis
	} else {
239 1ab56363 Ermal Lu?i
		return false;
240 751533a2 Phil Davis
	}
241 1ab56363 Ermal Lu?i
}
242
243 4caa9574 stilez
/* validate non-negative numeric string, or equivalent numeric variable */
244
function is_numericint($arg) {
245 751533a2 Phil Davis
	return (((is_int($arg) && $arg >= 0) || (is_string($arg) && strlen($arg) > 0 && ctype_digit($arg))) ? true : false);
246 4caa9574 stilez
}
247
248 751533a2 Phil Davis
/* Generate the (human readable) ipv4 or ipv6 subnet address (i.e., netmask, or subnet start IP)
249 e89d2995 stilez
   given an (human readable) ipv4 or ipv6 host address and subnet bit count */
250 5b237745 Scott Ullrich
function gen_subnet($ipaddr, $bits) {
251 751533a2 Phil Davis
	if (($sn = gen_subnetv6($ipaddr, $bits)) == '') {
252 e89d2995 stilez
		$sn = gen_subnetv4($ipaddr, $bits);  // try to avoid rechecking IPv4/v6
253 751533a2 Phil Davis
	}
254 e89d2995 stilez
	return $sn;
255
}
256 3bad4691 Renato Botelho
257 e89d2995 stilez
/* same as gen_subnet() but accepts IPv4 only */
258
function gen_subnetv4($ipaddr, $bits) {
259
	if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) {
260 751533a2 Phil Davis
		if ($bits == 0) {
261 e89d2995 stilez
			return '0.0.0.0';  // avoids <<32
262 751533a2 Phil Davis
		}
263 e89d2995 stilez
		return long2ip(ip2long($ipaddr) & ((0xFFFFFFFF << (32 - $bits)) & 0xFFFFFFFF));
264
	}
265
	return "";
266 22b5abac Seth Mos
}
267
268 e89d2995 stilez
/* same as gen_subnet() but accepts IPv6 only */
269 22b5abac Seth Mos
function gen_subnetv6($ipaddr, $bits) {
270 751533a2 Phil Davis
	if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) {
271 e89d2995 stilez
		return Net_IPv6::compress(Net_IPv6::getNetmask($ipaddr, $bits));
272 751533a2 Phil Davis
	}
273 e89d2995 stilez
	return "";
274 5b237745 Scott Ullrich
}
275
276 3bad4691 Renato Botelho
/* Generate the (human readable) ipv4 or ipv6 subnet end address (i.e., highest address, end IP, or IPv4 broadcast address)
277 e89d2995 stilez
   given an (human readable) ipv4 or ipv6 host address and subnet bit count. */
278 5b237745 Scott Ullrich
function gen_subnet_max($ipaddr, $bits) {
279 751533a2 Phil Davis
	if (($sn = gen_subnetv6_max($ipaddr, $bits)) == '') {
280 e89d2995 stilez
		$sn = gen_subnetv4_max($ipaddr, $bits);  // try to avoid rechecking IPv4/v6
281 751533a2 Phil Davis
	}
282 e89d2995 stilez
	return $sn;
283
}
284 98bbf05a Scott Ullrich
285 e89d2995 stilez
/* same as gen_subnet_max() but validates IPv4 only */
286
function gen_subnetv4_max($ipaddr, $bits) {
287
	if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) {
288 751533a2 Phil Davis
		if ($bits == 32) {
289 e89d2995 stilez
			return $ipaddr;
290 751533a2 Phil Davis
		}
291 c18ba6bf Phil Davis
		return long2ip32(ip2long($ipaddr) | (~gen_subnet_mask_long($bits) & 0xFFFFFFFF));
292 e89d2995 stilez
	}
293
	return "";
294 5b237745 Scott Ullrich
}
295
296 e89d2995 stilez
/* same as gen_subnet_max() but validates IPv6 only */
297 c75a8185 Seth Mos
function gen_subnetv6_max($ipaddr, $bits) {
298 e89d2995 stilez
	if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) {
299 de645734 Renato Botelho
		$endip_bin = substr(ip6_to_bin($ipaddr), 0, $bits) . str_repeat('1', 128 - $bits);
300
		return bin_to_compressed_ip6($endip_bin);
301 e89d2995 stilez
	}
302
	return "";
303 c75a8185 Seth Mos
}
304
305 5b237745 Scott Ullrich
/* returns a subnet mask (long given a bit count) */
306
function gen_subnet_mask_long($bits) {
307
	$sm = 0;
308
	for ($i = 0; $i < $bits; $i++) {
309
		$sm >>= 1;
310
		$sm |= 0x80000000;
311
	}
312
	return $sm;
313
}
314
315
/* same as above but returns a string */
316
function gen_subnet_mask($bits) {
317
	return long2ip(gen_subnet_mask_long($bits));
318
}
319
320 31b15180 jim-p
/* Convert a prefix length to an IPv6 address-like mask notation. Very rare but at least ntp needs it. See #4463 */
321
function gen_subnet_mask_v6($bits) {
322
	/* Binary representation of the prefix length */
323
	$bin = str_repeat('1', $bits);
324
	/* Pad right with zeroes to reach the full address length */
325
	$bin = str_pad($bin, 128, '0', STR_PAD_RIGHT);
326
	/* Convert back to an IPv6 address style notation */
327 de645734 Renato Botelho
	return bin_to_ip6($bin);
328 31b15180 jim-p
}
329
330 ce9dc198 stilez
/* Convert long int to IPv4 address
331 f8a6c824 Chris Buechler
   Returns '' if not valid IPv4 (including if any bits >32 are non-zero) */
332 96033063 Erik Fonnesbeck
function long2ip32($ip) {
333 f8a6c824 Chris Buechler
	return long2ip($ip & 0xFFFFFFFF);
334
}
335 96033063 Erik Fonnesbeck
336 ce9dc198 stilez
/* Convert IPv4 address to long int, truncated to 32-bits to avoid sign extension on 64-bit platforms.
337
   Returns '' if not valid IPv4. */
338 96033063 Erik Fonnesbeck
function ip2long32($ip) {
339 f8a6c824 Chris Buechler
	return (ip2long($ip) & 0xFFFFFFFF);
340 96033063 Erik Fonnesbeck
}
341
342 ce9dc198 stilez
/* Convert IPv4 address to unsigned long int.
343
   Returns '' if not valid IPv4. */
344 ecd1f2d9 jim-p
function ip2ulong($ip) {
345 f8a6c824 Chris Buechler
	return sprintf("%u", ip2long32($ip));
346 ecd1f2d9 jim-p
}
347
348 de645734 Renato Botelho
/*
349
 * Convert IPv6 address to binary
350
 *
351
 * Obtained from: pear-Net_IPv6
352
 */
353
function ip6_to_bin($ip) {
354
	$binstr = '';
355
356
	$ip = Net_IPv6::removeNetmaskSpec($ip);
357
	$ip = Net_IPv6::Uncompress($ip);
358
359
	$parts = explode(':', $ip);
360
361
	foreach ( $parts as $v ) {
362
363
		$str     = base_convert($v, 16, 2);
364
		$binstr .= str_pad($str, 16, '0', STR_PAD_LEFT);
365
366
	}
367
368
	return $binstr;
369
}
370
371
/*
372
 * Convert IPv6 binary to uncompressed address
373
 *
374
 * Obtained from: pear-Net_IPv6
375
 */
376
function bin_to_ip6($bin) {
377
	$ip = "";
378
379
	if (strlen($bin) < 128) {
380
		$bin = str_pad($bin, 128, '0', STR_PAD_LEFT);
381
	}
382
383
	$parts = str_split($bin, "16");
384
385
	foreach ( $parts as $v ) {
386
		$str = base_convert($v, 2, 16);
387
		$ip .= $str.":";
388
	}
389
390
	$ip = substr($ip, 0, -1);
391
392
	return $ip;
393
}
394
395
/*
396
 * Convert IPv6 binary to compressed address
397
 */
398
function bin_to_compressed_ip6($bin) {
399
	return Net_IPv6::compress(bin_to_ip6($bin));
400
}
401
402 ecd1f2d9 jim-p
/* Find out how many IPs are contained within a given IP range
403
 *  e.g. 192.168.0.0 to 192.168.0.255 returns 256
404
 */
405 bb67ac32 Phil Davis
function ip_range_size_v4($startip, $endip) {
406
	if (is_ipaddrv4($startip) && is_ipaddrv4($endip)) {
407 ecd1f2d9 jim-p
		// Operate as unsigned long because otherwise it wouldn't work
408
		//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
409
		return abs(ip2ulong($startip) - ip2ulong($endip)) + 1;
410
	}
411
	return -1;
412
}
413
414
/* Find the smallest possible subnet mask which can contain a given number of IPs
415
 *  e.g. 512 IPs can fit in a /23, but 513 IPs need a /22
416
 */
417 bb67ac32 Phil Davis
function find_smallest_cidr_v4($number) {
418 ecd1f2d9 jim-p
	$smallest = 1;
419
	for ($b=32; $b > 0; $b--) {
420 086cf944 Phil Davis
		$smallest = ($number <= pow(2, $b)) ? $b : $smallest;
421 ecd1f2d9 jim-p
	}
422
	return (32-$smallest);
423
}
424
425
/* Return the previous IP address before the given address */
426 aa181833 Phil Davis
function ip_before($ip, $offset = 1) {
427
	return long2ip32(ip2long($ip) - $offset);
428 ecd1f2d9 jim-p
}
429
430
/* Return the next IP address after the given address */
431 aa181833 Phil Davis
function ip_after($ip, $offset = 1) {
432
	return long2ip32(ip2long($ip) + $offset);
433 ecd1f2d9 jim-p
}
434
435
/* Return true if the first IP is 'before' the second */
436
function ip_less_than($ip1, $ip2) {
437
	// Compare as unsigned long because otherwise it wouldn't work when
438
	//   crossing over from 127.255.255.255 / 128.0.0.0 barrier
439
	return ip2ulong($ip1) < ip2ulong($ip2);
440
}
441
442
/* Return true if the first IP is 'after' the second */
443
function ip_greater_than($ip1, $ip2) {
444
	// Compare as unsigned long because otherwise it wouldn't work
445
	//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
446
	return ip2ulong($ip1) > ip2ulong($ip2);
447
}
448
449 b17ac4f7 stilez
/* compare two IP addresses */
450
function ipcmp($a, $b) {
451 751533a2 Phil Davis
	if (ip_less_than($a, $b)) {
452 b17ac4f7 stilez
		return -1;
453 751533a2 Phil Davis
	} else if (ip_greater_than($a, $b)) {
454 b17ac4f7 stilez
		return 1;
455 751533a2 Phil Davis
	} else {
456 b17ac4f7 stilez
		return 0;
457 751533a2 Phil Davis
	}
458 b17ac4f7 stilez
}
459
460 bb67ac32 Phil Davis
/* Convert a range of IPv4 addresses to an array of individual addresses. */
461
/* Note: IPv6 ranges are not yet supported here. */
462
function ip_range_to_address_array($startip, $endip, $max_size = 5000) {
463
	if (!is_ipaddrv4($startip) || !is_ipaddrv4($endip)) {
464
		return false;
465
	}
466
467
	if (ip_greater_than($startip, $endip)) {
468
		// Swap start and end so we can process sensibly.
469
		$temp = $startip;
470
		$startip = $endip;
471
		$endip = $temp;
472
	}
473
474 751533a2 Phil Davis
	if (ip_range_size_v4($startip, $endip) > $max_size) {
475 bb67ac32 Phil Davis
		return false;
476 751533a2 Phil Davis
	}
477
478 bb67ac32 Phil Davis
	// Container for IP addresses within this range.
479
	$rangeaddresses = array();
480
	$end_int = ip2ulong($endip);
481
	for ($ip_int = ip2ulong($startip); $ip_int <= $end_int; $ip_int++) {
482
		$rangeaddresses[] = long2ip($ip_int);
483
	}
484
485
	return $rangeaddresses;
486
}
487
488 4f3fc80d Renato Botelho
/*
489
 * Convert an IPv4 or IPv6 IP range to an array of subnets which can contain the range.
490
 * Algorithm and embodying code PD'ed by Stilez - enjoy as you like :-)
491
 *
492
 * Documented on pfsense dev list 19-20 May 2013. Summary:
493
 *
494
 * The algorithm looks at patterns of 0's and 1's in the least significant bit(s), whether IPv4 or IPv6.
495
 * These are all that needs checking to identify a _guaranteed_ correct, minimal and optimal subnet array.
496
 *
497
 * As a result, string/binary pattern matching of the binary IP is very efficient. It uses just 2 pattern-matching rules
498
 * to chop off increasingly larger subnets at both ends that can't be part of larger subnets, until nothing's left.
499
 *
500
 * (a) If any range has EITHER low bit 1 (in startip) or 0 (in endip), that end-point is _always guaranteed_ to be optimally
501
 * represented by its own 'single IP' CIDR; the remaining range then shrinks by one IP up or down, causing the new end-point's
502
 * low bit to change from 1->0 (startip) or 0->1 (endip). Only one edge case needs checking: if a range contains exactly 2
503
 * adjacent IPs of this format, then the two IPs themselves are required to span it, and we're done.
504
 * 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
505
 * low bits can now be ignored.
506
 *
507
 * (b) If any range has BOTH startip and endip ending in some number of 0's and 1's respectively, these low bits can
508
 * *always* be ignored and "bit-shifted" for subnet spanning. So provided we remember the bits we've place-shifted, we can
509
 * _always_ right-shift and chop off those bits, leaving a smaller range that has EITHER startip ending in 1 or endip ending
510
 * in 0 (ie can now apply (a) again) or the entire range has vanished and we're done.
511
 * We then loop to redo (a) again on the remaining (place shifted) range until after a few loops, the remaining (place shifted)
512
 * range 'vanishes' by meeting the exit criteria of (a) or (b), and we're done.
513
 */
514 ed516fa7 stilez
function ip_range_to_subnet_array($ip1, $ip2) {
515 bb67ac32 Phil Davis
516 ed516fa7 stilez
	if (is_ipaddrv4($ip1) && is_ipaddrv4($ip2)) {
517
		$proto = 'ipv4';  // for clarity
518
		$bits = 32;
519
		$ip1bin = decbin(ip2long32($ip1));
520
		$ip2bin = decbin(ip2long32($ip2));
521
	} elseif (is_ipaddrv6($ip1) && is_ipaddrv6($ip2)) {
522
		$proto = 'ipv6';
523
		$bits = 128;
524 de645734 Renato Botelho
		$ip1bin = ip6_to_bin($ip1);
525
		$ip2bin = ip6_to_bin($ip2);
526 19b802f4 Phil Davis
	} else {
527 ed516fa7 stilez
		return array();
528 19b802f4 Phil Davis
	}
529 ecd1f2d9 jim-p
530 ed516fa7 stilez
	// it's *crucial* that binary strings are guaranteed the expected length;  do this for certainty even though for IPv6 it's redundant
531
	$ip1bin = str_pad($ip1bin, $bits, '0', STR_PAD_LEFT);
532
	$ip2bin = str_pad($ip2bin, $bits, '0', STR_PAD_LEFT);
533 ecd1f2d9 jim-p
534 19b802f4 Phil Davis
	if ($ip1bin == $ip2bin) {
535 ed516fa7 stilez
		return array($ip1 . '/' . $bits); // exit if ip1=ip2 (trivial case)
536 19b802f4 Phil Davis
	}
537
538
	if ($ip1bin > $ip2bin) {
539 ed516fa7 stilez
		list ($ip1bin, $ip2bin) = array($ip2bin, $ip1bin);  // swap if needed (ensures ip1 < ip2)
540 19b802f4 Phil Davis
	}
541 ecd1f2d9 jim-p
542 ed516fa7 stilez
	$rangesubnets = array();
543
	$netsize = 0;
544
545
	do {
546
		// at loop start, $ip1 is guaranteed strictly less than $ip2 (important for edge case trapping and preventing accidental binary wrapround)
547
		// which means the assignments $ip1 += 1 and $ip2 -= 1 will always be "binary-wrapround-safe"
548
549
		// 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)
550 19b802f4 Phil Davis
551 ed516fa7 stilez
		if (substr($ip1bin, -1, 1) == '1') {
552
			// the start ip must be in a separate one-IP cidr range
553
			$new_subnet_ip = substr($ip1bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize);
554
			$rangesubnets[$new_subnet_ip] = $bits - $netsize;
555
			$n = strrpos($ip1bin, '0');  //can't be all 1's
556
			$ip1bin = ($n == 0 ? '' : substr($ip1bin, 0, $n)) . '1' . str_repeat('0', $bits - $n - 1);  // BINARY VERSION OF $ip1 += 1
557 19b802f4 Phil Davis
		}
558 ed516fa7 stilez
559
		// 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)
560 19b802f4 Phil Davis
561 ed516fa7 stilez
		if (substr($ip2bin, -1, 1) == '0') {
562
			// the end ip must be in a separate one-IP cidr range
563
			$new_subnet_ip = substr($ip2bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize);
564
			$rangesubnets[$new_subnet_ip] = $bits - $netsize;
565
			$n = strrpos($ip2bin, '1');  //can't be all 0's
566
			$ip2bin = ($n == 0 ? '' : substr($ip2bin, 0, $n)) . '0' . str_repeat('1', $bits - $n - 1);  // BINARY VERSION OF $ip2 -= 1
567
			// already checked for the edge case where end = start+1 and start ends in 0x1, above, so it's safe
568 ecd1f2d9 jim-p
		}
569
570 19b802f4 Phil Davis
		// this is the only edge case arising from increment/decrement.
571 ed516fa7 stilez
		// 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)
572 19b802f4 Phil Davis
573
		if ($ip2bin < $ip1bin) {
574 ed516fa7 stilez
			continue;
575 19b802f4 Phil Davis
		}
576 ed516fa7 stilez
577
		// step #3 the start and end ip MUST now end in '0's and '1's respectively
578
		// so we have a non-trivial range AND the last N bits are no longer important for CIDR purposes.
579
580
		$shift = $bits - max(strrpos($ip1bin, '0'), strrpos($ip2bin, '1'));  // num of low bits which are '0' in ip1 and '1' in ip2
581
		$ip1bin = str_repeat('0', $shift) . substr($ip1bin, 0, $bits - $shift);
582
		$ip2bin = str_repeat('0', $shift) . substr($ip2bin, 0, $bits - $shift);
583
		$netsize += $shift;
584
		if ($ip1bin == $ip2bin) {
585
			// we're done.
586
			$new_subnet_ip = substr($ip1bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize);
587
			$rangesubnets[$new_subnet_ip] = $bits - $netsize;
588
			continue;
589 ecd1f2d9 jim-p
		}
590 19b802f4 Phil Davis
591 ed516fa7 stilez
		// at this point there's still a remaining range, and either startip ends with '1', or endip ends with '0'. So repeat cycle.
592
	} while ($ip1bin < $ip2bin);
593 ecd1f2d9 jim-p
594 ed516fa7 stilez
	// subnets are ordered by bit size. Re sort by IP ("naturally") and convert back to IPv4/IPv6
595 ecd1f2d9 jim-p
596 ed516fa7 stilez
	ksort($rangesubnets, SORT_STRING);
597
	$out = array();
598 ecd1f2d9 jim-p
599 ed516fa7 stilez
	foreach ($rangesubnets as $ip => $netmask) {
600
		if ($proto == 'ipv4') {
601
			$i = str_split($ip, 8);
602 19b802f4 Phil Davis
			$out[] = implode('.', array(bindec($i[0]), bindec($i[1]), bindec($i[2]), bindec($i[3]))) . '/' . $netmask;
603
		} else {
604 de645734 Renato Botelho
			$out[] = bin_to_compressed_ip6($ip) . '/' . $netmask;
605 19b802f4 Phil Davis
		}
606 ecd1f2d9 jim-p
	}
607
608 ed516fa7 stilez
	return $out;
609 ecd1f2d9 jim-p
}
610
611 bb67ac32 Phil Davis
/* returns true if $range is a valid pair of IPv4 or IPv6 addresses separated by a "-"
612 751533a2 Phil Davis
	false - if not a valid pair
613
	true (numeric 4 or 6) - if valid, gives type of addresses */
614 ecd1f2d9 jim-p
function is_iprange($range) {
615
	if (substr_count($range, '-') != 1) {
616
		return false;
617
	}
618
	list($ip1, $ip2) = explode ('-', $range);
619 751533a2 Phil Davis
	if (is_ipaddrv4($ip1) && is_ipaddrv4($ip2)) {
620 bb67ac32 Phil Davis
		return 4;
621 751533a2 Phil Davis
	}
622
	if (is_ipaddrv6($ip1) && is_ipaddrv6($ip2)) {
623 bb67ac32 Phil Davis
		return 6;
624 751533a2 Phil Davis
	}
625 bb67ac32 Phil Davis
	return false;
626 ecd1f2d9 jim-p
}
627
628 31495068 stilez
/* returns true if $ipaddr is a valid dotted IPv4 address or a IPv6
629 751533a2 Phil Davis
	false - not valid
630
	true (numeric 4 or 6) - if valid, gives type of address */
631 5b237745 Scott Ullrich
function is_ipaddr($ipaddr) {
632 751533a2 Phil Davis
	if (is_ipaddrv4($ipaddr)) {
633 31495068 stilez
		return 4;
634 47593ac6 Seth Mos
	}
635 751533a2 Phil Davis
	if (is_ipaddrv6($ipaddr)) {
636 31495068 stilez
		return 6;
637 47593ac6 Seth Mos
	}
638
	return false;
639
}
640
641 22b5abac Seth Mos
/* returns true if $ipaddr is a valid IPv6 address */
642 47593ac6 Seth Mos
function is_ipaddrv6($ipaddr) {
643 751533a2 Phil Davis
	if (!is_string($ipaddr) || empty($ipaddr)) {
644 b5b5bcc0 Ermal
		return false;
645 751533a2 Phil Davis
	}
646 55909a9a Ermal
	if (strstr($ipaddr, "%") && is_linklocal($ipaddr)) {
647
		$tmpip = explode("%", $ipaddr);
648
		$ipaddr = $tmpip[0];
649
	}
650 1e5da31d Ermal
	return Net_IPv6::checkIPv6($ipaddr);
651 47593ac6 Seth Mos
}
652
653
/* returns true if $ipaddr is a valid dotted IPv4 address */
654
function is_ipaddrv4($ipaddr) {
655 c3b3e9c7 stilez
	if (!is_string($ipaddr) || empty($ipaddr) || ip2long($ipaddr) === FALSE) {
656 5b237745 Scott Ullrich
		return false;
657 751533a2 Phil Davis
	}
658 c3b3e9c7 stilez
	return true;
659 5b237745 Scott Ullrich
}
660
661 a2fd89dd stilez
/* returns 4 or 6 respectively (== TRUE) if $ipaddr is a valid IPv4 or IPv6 linklocal address
662 f38d984b Renato Botelho
   returns '' if not a valid linklocal address */
663 19341491 Renato Botelho
function is_linklocal($ipaddr) {
664 f38d984b Renato Botelho
	if (is_ipaddrv4($ipaddr)) {
665
		// input is IPv4
666
		// test if it's 169.254.x.x per rfc3927 2.1
667
		$ip4 = explode(".", $ipaddr);
668
		if ($ip4[0] == '169' && $ip4[1] == '254') {
669
			return 4;
670 a2fd89dd stilez
		}
671 f38d984b Renato Botelho
	} elseif (Net_IPv6::getAddressType($ipaddr) == NET_IPV6_LOCAL_LINK) {
672
		return 6;
673 a2fd89dd stilez
	}
674
	return '';
675 19341491 Renato Botelho
}
676 3f5f7ad3 smos
677 bd6ff328 Renato Botelho
/* returns scope of a linklocal address */
678
function get_ll_scope($addr) {
679 751533a2 Phil Davis
	if (!is_linklocal($addr) || !strstr($addr, "%")) {
680 bd6ff328 Renato Botelho
		return "";
681 751533a2 Phil Davis
	}
682 bd6ff328 Renato Botelho
	list ($ll, $scope) = explode("%", $addr);
683
	return $scope;
684
}
685
686 3f5f7ad3 smos
/* returns true if $ipaddr is a valid literal IPv6 address */
687
function is_literalipaddrv6($ipaddr) {
688 a2fd89dd stilez
	if (substr($ipaddr,0,1) == '[' && substr($ipaddr,-1,1) == ']') {
689
		// if it's data wrapped in "[ ... ]" then test if middle part is valid IPv6
690
		return is_ipaddrv6(substr($ipaddr,1,-1));
691 751533a2 Phil Davis
	}
692 a2fd89dd stilez
	return false;
693 3f5f7ad3 smos
}
694
695 a2fd89dd stilez
/* returns true if $iport is a valid IPv4:port or [Literal IPv6]:port
696 751533a2 Phil Davis
	false - not valid
697
	true (numeric 4 or 6) - if valid, gives type of address */
698 4a8a90ff jim-p
function is_ipaddrwithport($ipport) {
699 31495068 stilez
	$c = strrpos($ipport, ":");
700 751533a2 Phil Davis
	if ($c === false) {
701 31495068 stilez
		return false;  // can't split at final colon if no colon exists
702 751533a2 Phil Davis
	}
703
704
	if (!is_port(substr($ipport, $c + 1))) {
705 31495068 stilez
		return false;  // no valid port after last colon
706 751533a2 Phil Davis
	}
707 31495068 stilez
708
	$ip = substr($ipport, 0, $c);  // else is text before last colon a valid IP
709 751533a2 Phil Davis
	if (is_literalipaddrv6($ip)) {
710 31495068 stilez
		return 6;
711 751533a2 Phil Davis
	} elseif (is_ipaddrv4($ip)) {
712 31495068 stilez
		return 4;
713 751533a2 Phil Davis
	} else {
714 4a8a90ff jim-p
		return false;
715 751533a2 Phil Davis
	}
716 4a8a90ff jim-p
}
717
718 d3a2337a jim-p
function is_hostnamewithport($hostport) {
719
	$parts = explode(":", $hostport);
720 a2fd89dd stilez
	// no need to validate with is_string(); if it's not a string then explode won't return 2 parts anyway
721
	if (count($parts) == 2) {
722
		return is_hostname($parts[0]) && is_port($parts[1]);
723 d3a2337a jim-p
	}
724 a2fd89dd stilez
	return false;
725 d3a2337a jim-p
}
726
727 87f0be87 Chris Buechler
/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */
728 5b237745 Scott Ullrich
function is_ipaddroralias($ipaddr) {
729 1e578a7f Ermal Lu?i
	global $config;
730 87f0be87 Chris Buechler
731 1e578a7f Ermal Lu?i
	if (is_alias($ipaddr)) {
732
		if (is_array($config['aliases']['alias'])) {
733
			foreach ($config['aliases']['alias'] as $alias) {
734 751533a2 Phil Davis
				if ($alias['name'] == $ipaddr && !preg_match("/port/i", $alias['type'])) {
735 1e578a7f Ermal Lu?i
					return true;
736 751533a2 Phil Davis
				}
737 1e578a7f Ermal Lu?i
			}
738 5bbd08e1 Warren Baker
		}
739 1e578a7f Ermal Lu?i
		return false;
740 751533a2 Phil Davis
	} else {
741 87f0be87 Chris Buechler
		return is_ipaddr($ipaddr);
742 751533a2 Phil Davis
	}
743 87f0be87 Chris Buechler
744 5b237745 Scott Ullrich
}
745
746 a5e2a35f stilez
/* returns true if $subnet is a valid IPv4 or IPv6 subnet in CIDR format
747 751533a2 Phil Davis
	false - if not a valid subnet
748
	true (numeric 4 or 6) - if valid, gives type of subnet */
749 5b237745 Scott Ullrich
function is_subnet($subnet) {
750 a5e2a35f stilez
	if (is_string($subnet) && preg_match('/^(?:([0-9.]{7,15})|([0-9a-f:]{2,39}))\/(\d{1,3})$/i', $subnet, $parts)) {
751 751533a2 Phil Davis
		if (is_ipaddrv4($parts[1]) && $parts[3] <= 32) {
752 a5e2a35f stilez
			return 4;
753 751533a2 Phil Davis
		}
754
		if (is_ipaddrv6($parts[2]) && $parts[3] <= 128) {
755 a5e2a35f stilez
			return 6;
756 751533a2 Phil Davis
		}
757 b1b42a06 Warren Baker
	}
758
	return false;
759
}
760
761 a5e2a35f stilez
/* same as is_subnet() but accepts IPv4 only */
762 b1b42a06 Warren Baker
function is_subnetv4($subnet) {
763 a5e2a35f stilez
	return (is_subnet($subnet) == 4);
764 5b237745 Scott Ullrich
}
765
766 a5e2a35f stilez
/* same as is_subnet() but accepts IPv6 only */
767 fdb9c1db Warren Baker
function is_subnetv6($subnet) {
768 a5e2a35f stilez
	return (is_subnet($subnet) == 6);
769 fdb9c1db Warren Baker
}
770
771 5b237745 Scott Ullrich
/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */
772
function is_subnetoralias($subnet) {
773
	global $aliastable;
774 98bbf05a Scott Ullrich
775 751533a2 Phil Davis
	if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet])) {
776 5b237745 Scott Ullrich
		return true;
777 751533a2 Phil Davis
	} else {
778 5b237745 Scott Ullrich
		return is_subnet($subnet);
779 751533a2 Phil Davis
	}
780 5b237745 Scott Ullrich
}
781
782 cafe9038 stilez
/* Get number of addresses in an IPv4/IPv6 subnet (represented as a string)
783
   optional $exact=true forces error (0) to be returned if it can't be represented exactly
784
   Exact result not possible above PHP_MAX_INT which is about 2^31 addresses on x32 or 2^63 on x64
785 4402b5cb stilez
   Returns 0 for bad data or if cannot represent size as an INT when $exact is set. */
786
function subnet_size($subnet, $exact=false) {
787
	$parts = explode("/", $subnet);
788 0987677a stilez
	$iptype = is_ipaddr($parts[0]);
789
	if (count($parts) == 2 && $iptype) {
790
		return subnet_size_by_netmask($iptype, $parts[1], $exact);
791 4402b5cb stilez
	}
792
	return 0;
793
}
794
795 cafe9038 stilez
/* Get number of addresses in an IPv4/IPv6 subnet (represented numerically as IP type + bits)
796
   optional $exact=true forces error (0) to be returned if it can't be represented exactly
797
   Hard to think where we might need to count exactly a huge subnet but an overflow detection option is probably sensible
798
   Returns 0 for bad data or if cannot represent size as an INT when $exact is set. */
799 4402b5cb stilez
function subnet_size_by_netmask($iptype, $bits, $exact=false) {
800
	if (!is_numericint($bits)) {
801
		return 0;
802
	} elseif ($iptype == 4 && $bits <= 32) {
803
		$snsize = 32 - $bits;
804
	} elseif ($iptype == 6 && $bits <= 128) {
805
		$snsize = 128 - $bits;
806 19b802f4 Phil Davis
	} else {
807 b17ac4f7 stilez
		return 0;
808
	}
809 4402b5cb stilez
810 4f7956ad Steve Beaver
	// 2**N returns an exact result as an INT if possible, and a float/double if not.
811 4402b5cb stilez
	// Detect this switch, rather than comparing $result<=PHP_MAX_INT or $bits >=8*PHP_INT_SIZE as it's (probably) easier to get completely reliable
812
	$result = 2 ** $snsize;
813 4f7956ad Steve Beaver
814 4402b5cb stilez
	if ($exact && !is_int($result)) {
815
		//exact required but can't represent result exactly as an INT
816
		return 0;
817
	} else {
818
		// result ok, will be an INT where possible (guaranteed up to 2^31 addresses on x32/x64) and a float for 'huge' subnets
819
		return $result;
820
	}
821 b17ac4f7 stilez
}
822
823 2208be8b Chris Buechler
/* function used by pfblockerng */
824
function subnetv4_expand($subnet) {
825
	$result = array();
826
	list ($ip, $bits) = explode("/", $subnet);
827
	$net = ip2long($ip);
828
	$mask = (0xffffffff << (32 - $bits));
829
	$net &= $mask;
830
	$size = round(exp(log(2) * (32 - $bits)));
831
	for ($i = 0; $i < $size; $i += 1) {
832
		$result[] = long2ip($net | $i);
833
	}
834
	return $result;
835
}
836
837 e8d5be8e stilez
/* find out whether two IPv4/IPv6 CIDR subnets overlap.
838
   Note: CIDR overlap implies one is identical or included so largest sn will be the same */
839 b17ac4f7 stilez
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
840 94eb702f stilez
	if (is_ipaddrv4($subnet1)) {
841 e8d5be8e stilez
		return check_subnetsv4_overlap($subnet1, $bits1, $subnet2, $bits2);
842 751533a2 Phil Davis
	} else {
843 e8d5be8e stilez
		return check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2);
844 751533a2 Phil Davis
	}
845 b17ac4f7 stilez
}
846
847 e8d5be8e stilez
/* find out whether two IPv4 CIDR subnets overlap.
848
   Note: CIDR overlap means sn1/sn2 are identical or one is included in other. So sn using largest $bits will be the same  */
849
function check_subnetsv4_overlap($subnet1, $bits1, $subnet2, $bits2) {
850 62512efa stilez
	$largest_sn = min($bits1, $bits2);
851 e8d5be8e stilez
	$subnetv4_start1 = gen_subnetv4($subnet1, $largest_sn);
852 9f4a788f stilez
	$subnetv4_start2 = gen_subnetv4($subnet2, $largest_sn);
853 4f7956ad Steve Beaver
854 9d3e8723 Phil Davis
	if ($subnetv4_start1 == '' || $subnetv4_start2 == '') {
855 e8d5be8e stilez
		// One or both args is not a valid IPv4 subnet
856
		//FIXME: needs to return "bad data" not true/false if bad. For now return false, best we can do until fixed
857
		return false;
858
	}
859
	return ($subnetv4_start1 == $subnetv4_start2);
860
}
861 b17ac4f7 stilez
862 e8d5be8e stilez
/* find out whether two IPv6 CIDR subnets overlap.
863
   Note: CIDR overlap means sn1/sn2 are identical or one is included in other. So sn using largest $bits will be the same  */
864 9f8266cd stilez
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
865 62512efa stilez
	$largest_sn = min($bits1, $bits2);
866 9f8266cd stilez
	$subnetv6_start1 = gen_subnetv6($subnet1, $largest_sn);
867 9f4a788f stilez
	$subnetv6_start2 = gen_subnetv6($subnet2, $largest_sn);
868 4f7956ad Steve Beaver
869 9d3e8723 Phil Davis
	if ($subnetv6_start1 == '' || $subnetv6_start2 == '') {
870 e8d5be8e stilez
		// One or both args is not a valid IPv6 subnet
871
		//FIXME: needs to return "bad data" not true/false if bad. For now return false, best we can do until fixed
872
		return false;
873
	}
874
	return ($subnetv6_start1 == $subnetv6_start2);
875 b17ac4f7 stilez
}
876 4c62c1ff Renato Botelho
877
/* return all PTR zones for a IPv6 network */
878
function get_v6_ptr_zones($subnet, $bits) {
879
	$result = array();
880
881
	if (!is_ipaddrv6($subnet)) {
882
		return $result;
883
	}
884
885
	if (!is_numericint($bits) || $bits > 128) {
886
		return $result;
887
	}
888
889
	/*
890
	 * Find a small nibble boundary subnet mask
891
	 * e.g. a /29 will create 8 /32 PTR zones
892
	 */
893
	$small_sn = $bits;
894
	while ($small_sn % 4 != 0) {
895
		$small_sn++;
896
	}
897
898
	/* Get network prefix */
899
	$small_subnet = Net_IPv6::getNetmask($subnet, $bits);
900
901
	/*
902
	 * While small network is part of bigger one, increase 4-bit in last
903
	 * digit to get next small network
904
	 */
905
	while (Net_IPv6::isInNetmask($small_subnet, $subnet, $bits)) {
906
		/* Get a pure hex value */
907
		$unpacked = unpack('H*hex', inet_pton($small_subnet));
908
		/* Create PTR record using $small_sn / 4 chars */
909
		$result[] = implode('.', array_reverse(str_split(substr(
910
		    $unpacked['hex'], 0, $small_sn / 4)))).'.ip6.arpa';
911
912
		/* Detect what part of IP should be increased */
913
		$change_part = (int) ($small_sn / 16);
914
		if ($small_sn % 16 == 0) {
915
			$change_part--;
916
		}
917
918 53904d09 Renato Botelho
		/* Increase 1 to desired part */
919 4c62c1ff Renato Botelho
		$parts = explode(":", Net_IPv6::uncompress($small_subnet));
920 53904d09 Renato Botelho
		$parts[$change_part]++;
921 4c62c1ff Renato Botelho
		$small_subnet = implode(":", $parts);
922
	}
923
924
	return $result;
925
}
926 b17ac4f7 stilez
927
/* return true if $addr is in $subnet, false if not */
928 086cf944 Phil Davis
function ip_in_subnet($addr, $subnet) {
929 751533a2 Phil Davis
	if (is_ipaddrv6($addr) && is_subnetv6($subnet)) {
930 b17ac4f7 stilez
		return (Net_IPv6::isInNetmask($addr, $subnet));
931 0c5dd854 Renato Botelho
	} else if (is_ipaddrv4($addr) && is_subnetv4($subnet)) {
932 b17ac4f7 stilez
		list($ip, $mask) = explode('/', $subnet);
933
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
934
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
935
	}
936 0c5dd854 Renato Botelho
	return false;
937 b17ac4f7 stilez
}
938
939 6bcbd862 Phil Davis
/* returns true if $hostname is just a valid hostname (top part without any of the domain part) */
940
function is_unqualified_hostname($hostname) {
941 751533a2 Phil Davis
	if (!is_string($hostname)) {
942 6bcbd862 Phil Davis
		return false;
943 751533a2 Phil Davis
	}
944 6bcbd862 Phil Davis
945 8fdddd51 Chris Buechler
	if (preg_match('/^(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])$/i', $hostname)) {
946 6bcbd862 Phil Davis
		return true;
947 751533a2 Phil Davis
	} else {
948 6bcbd862 Phil Davis
		return false;
949 751533a2 Phil Davis
	}
950 6bcbd862 Phil Davis
}
951
952
/* returns true if $hostname is a valid hostname, with or without being a fully-qualified domain name. */
953 0edcccc3 Daniel Seebald
function is_hostname($hostname, $allow_wildcard=false) {
954 751533a2 Phil Davis
	if (!is_string($hostname)) {
955 5b237745 Scott Ullrich
		return false;
956 751533a2 Phil Davis
	}
957 98bbf05a Scott Ullrich
958 cc882a8b Daniel Seebald
	if (is_domain($hostname, $allow_wildcard)) {
959 5454fd1b Phil Davis
		if ((substr_count($hostname, ".") == 1) && ($hostname[strlen($hostname)-1] == ".")) {
960
			/* Only a single dot at the end like "test." - hosts cannot be directly in the root domain. */
961
			return false;
962
		} else {
963
			return true;
964
		}
965 751533a2 Phil Davis
	} else {
966 5b237745 Scott Ullrich
		return false;
967 751533a2 Phil Davis
	}
968 5b237745 Scott Ullrich
}
969
970
/* returns true if $domain is a valid domain name */
971 0edcccc3 Daniel Seebald
function is_domain($domain, $allow_wildcard=false) {
972 751533a2 Phil Davis
	if (!is_string($domain)) {
973 5b237745 Scott Ullrich
		return false;
974 751533a2 Phil Davis
	}
975 0edcccc3 Daniel Seebald
	if ($allow_wildcard) {
976
		$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';
977
	} else {
978
		$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';
979
	}
980
981
	if (preg_match($domain_regex, $domain)) {
982 5b237745 Scott Ullrich
		return true;
983 751533a2 Phil Davis
	} else {
984 5b237745 Scott Ullrich
		return false;
985 751533a2 Phil Davis
	}
986 5b237745 Scott Ullrich
}
987
988
/* returns true if $macaddr is a valid MAC address */
989 80e7011f jim-p
function is_macaddr($macaddr, $partial=false) {
990 c982fdbc Luiz Otavio O Souza
	$values = explode(":", $macaddr);
991 80e7011f jim-p
992
	/* Verify if the MAC address has a proper amount of parts for either a partial or full match. */
993
	if ($partial) {
994
		if ((count($values) < 1) || (count($values) > 6)) {
995
			return false;
996
		}
997
	} elseif (count($values) != 6) {
998 c982fdbc Luiz Otavio O Souza
		return false;
999
	}
1000 80e7011f jim-p
	for ($i = 0; $i < count($values); $i++) {
1001 c982fdbc Luiz Otavio O Souza
		if (ctype_xdigit($values[$i]) == false)
1002
			return false;
1003
		if (hexdec($values[$i]) < 0 || hexdec($values[$i]) > 255)
1004
			return false;
1005
	}
1006
1007
	return true;
1008 5b237745 Scott Ullrich
}
1009
1010 e1f5381f Phil Davis
/*
1011
	If $return_message is true then
1012
		returns a text message about the reason that the name is invalid.
1013
		the text includes the type of "thing" that is being checked, passed in $object. (e.g. "alias", "gateway group", "schedule")
1014
	else
1015
		returns true if $name is a valid name for an alias
1016
		returns false if $name is not a valid name for an alias
1017
1018
	Aliases cannot be:
1019
		bad chars: anything except a-z 0-9 and underscore
1020
		bad names: empty string, pure numeric, pure underscore
1021
		reserved words: pre-defined service/protocol/port names which should not be ambiguous, and the words "port" and  "pass" */
1022
1023
function is_validaliasname($name, $return_message = false, $object = "alias") {
1024 beeef1f0 Bill Marquette
	/* Array of reserved words */
1025 0c2badde Colin Smith
	$reserved = array("port", "pass");
1026 4ad9a1e7 stilez
1027 751533a2 Phil Davis
	if (!is_string($name) || strlen($name) >= 32 || preg_match('/(^_*$|^\d*$|[^a-z0-9_])/i', $name)) {
1028 e1f5381f Phil Davis
		if ($return_message) {
1029
			return sprintf(gettext('The %1$s name must be less than 32 characters long, may not consist of only numbers, may not consist of only underscores, and may only contain the following characters: %2$s'), $object, 'a-z, A-Z, 0-9, _');
1030
		} else {
1031
			return false;
1032
		}
1033 751533a2 Phil Davis
	}
1034 e1f5381f Phil Davis
	if (in_array($name, $reserved, true)) {
1035
		if ($return_message) {
1036
			return sprintf(gettext('The %1$s name must not be either of the reserved words %2$s or %3$s.'), $object, "'port'", "'pass'");
1037
		} else {
1038
			return false;
1039
		}
1040 751533a2 Phil Davis
	}
1041 e1f5381f Phil Davis
	if (getprotobyname($name)) {
1042
		if ($return_message) {
1043
			return sprintf(gettext('The %1$s name must not be a well-known IP protocol name such as TCP, UDP, ICMP etc.'), $object);
1044
		} else {
1045
			return false;
1046
		}
1047
	}
1048
	if (getservbyname($name, "tcp") || getservbyname($name, "udp")) {
1049
		if ($return_message) {
1050
			return sprintf(gettext('The %1$s name must not be a well-known TCP or UDP port name such as ssh, smtp, pop3, tftp, http, openvpn etc.'), $object);
1051
		} else {
1052
			return false;
1053
		}
1054
	}
1055
	if ($return_message) {
1056
		return sprintf(gettext("The %1$s name is valid."), $object);
1057
	} else {
1058
		return true;
1059
	}
1060
}
1061
1062
/* returns a text message indicating if the alias name is valid, or the reason it is not valid. */
1063
function invalidaliasnamemsg($name, $object = "alias") {
1064
	return is_validaliasname($name, true, $object);
1065 5b237745 Scott Ullrich
}
1066
1067
/* returns true if $port is a valid TCP/UDP port */
1068
function is_port($port) {
1069 751533a2 Phil Davis
	if (ctype_digit($port) && ((intval($port) >= 1) && (intval($port) <= 65535))) {
1070 75106235 PiBa-NL
		return true;
1071 751533a2 Phil Davis
	}
1072
	if (getservbyname($port, "tcp") || getservbyname($port, "udp")) {
1073 9060f420 Renato Botelho
		return true;
1074 751533a2 Phil Davis
	}
1075 75106235 PiBa-NL
	return false;
1076 5b237745 Scott Ullrich
}
1077
1078 5a1eebc7 Scott Ullrich
/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */
1079
function is_portrange($portrange) {
1080 5bbd08e1 Warren Baker
	$ports = explode(":", $portrange);
1081 5a1eebc7 Scott Ullrich
1082 e371f8b9 whjvenyl
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
1083 5a1eebc7 Scott Ullrich
}
1084
1085 1e578a7f Ermal Lu?i
/* returns true if $port is a valid port number or an alias thereof */
1086
function is_portoralias($port) {
1087
	global $config;
1088
1089 5bbd08e1 Warren Baker
	if (is_alias($port)) {
1090
		if (is_array($config['aliases']['alias'])) {
1091
			foreach ($config['aliases']['alias'] as $alias) {
1092 751533a2 Phil Davis
				if ($alias['name'] == $port && preg_match("/port/i", $alias['type'])) {
1093 5bbd08e1 Warren Baker
					return true;
1094
				}
1095
			}
1096 751533a2 Phil Davis
		}
1097
		return false;
1098
	} else {
1099 5bbd08e1 Warren Baker
		return is_port($port);
1100 751533a2 Phil Davis
	}
1101 1e578a7f Ermal Lu?i
}
1102
1103 d9f33a7f Renato Botelho
/* create ranges of sequential port numbers (200:215) and remove duplicates */
1104 f6622167 NOYB
function group_ports($ports, $kflc = false) {
1105 751533a2 Phil Davis
	if (!is_array($ports) || empty($ports)) {
1106 d9f33a7f Renato Botelho
		return;
1107 751533a2 Phil Davis
	}
1108 d9f33a7f Renato Botelho
1109
	$uniq = array();
1110 f6622167 NOYB
	$comments = array();
1111 d9f33a7f Renato Botelho
	foreach ($ports as $port) {
1112 f6622167 NOYB
		if (($kflc) && (strpos($port, '#') === 0)) {	// Keep Full Line Comments (lines beginning with #).
1113
			$comments[] = $port;
1114
		} else if (is_portrange($port)) {
1115 d9f33a7f Renato Botelho
			list($begin, $end) = explode(":", $port);
1116
			if ($begin > $end) {
1117
				$aux = $begin;
1118
				$begin = $end;
1119
				$end = $aux;
1120
			}
1121 751533a2 Phil Davis
			for ($i = $begin; $i <= $end; $i++) {
1122
				if (!in_array($i, $uniq)) {
1123 d9f33a7f Renato Botelho
					$uniq[] = $i;
1124 751533a2 Phil Davis
				}
1125
			}
1126 d9f33a7f Renato Botelho
		} else if (is_port($port)) {
1127 751533a2 Phil Davis
			if (!in_array($port, $uniq)) {
1128 d9f33a7f Renato Botelho
				$uniq[] = $port;
1129 751533a2 Phil Davis
			}
1130 d9f33a7f Renato Botelho
		}
1131
	}
1132
	sort($uniq, SORT_NUMERIC);
1133
1134
	$result = array();
1135
	foreach ($uniq as $idx => $port) {
1136
		if ($idx == 0) {
1137
			$result[] = $port;
1138
			continue;
1139
		}
1140
1141
		$last = end($result);
1142 751533a2 Phil Davis
		if (is_portrange($last)) {
1143 d9f33a7f Renato Botelho
			list($begin, $end) = explode(":", $last);
1144 751533a2 Phil Davis
		} else {
1145 d9f33a7f Renato Botelho
			$begin = $end = $last;
1146 751533a2 Phil Davis
		}
1147 d9f33a7f Renato Botelho
1148
		if ($port == ($end+1)) {
1149
			$end++;
1150
			$result[count($result)-1] = "{$begin}:{$end}";
1151
		} else {
1152
			$result[] = $port;
1153
		}
1154
	}
1155
1156 f6622167 NOYB
	return array_merge($comments, $result);
1157 d9f33a7f Renato Botelho
}
1158
1159 b8014f9d Scott Ullrich
/* returns true if $val is a valid shaper bandwidth value */
1160
function is_valid_shaperbw($val) {
1161 eaa37259 Ermal Luçi
	return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val));
1162 b8014f9d Scott Ullrich
}
1163
1164 54404519 Renato Botelho
/* returns true if $test is in the range between $start and $end */
1165
function is_inrange_v4($test, $start, $end) {
1166 8c48089f Renato Botelho
	if (!is_ipaddrv4($test) || !is_ipaddrv4($start) || !is_ipaddrv4($end)) {
1167 54404519 Renato Botelho
		return false;
1168 751533a2 Phil Davis
	}
1169 8c48089f Renato Botelho
1170
	if (ip2ulong($test) <= ip2ulong($end) &&
1171
	    ip2ulong($test) >= ip2ulong($start)) {
1172
		return true;
1173
	}
1174
1175
	return false;
1176 54404519 Renato Botelho
}
1177
1178 41b4867e Renato Botelho
/* returns true if $test is in the range between $start and $end */
1179
function is_inrange_v6($test, $start, $end) {
1180 8c48089f Renato Botelho
	if (!is_ipaddrv6($test) || !is_ipaddrv6($start) || !is_ipaddrv6($end)) {
1181 41b4867e Renato Botelho
		return false;
1182 751533a2 Phil Davis
	}
1183 8c48089f Renato Botelho
1184
	if (inet_pton($test) <= inet_pton($end) &&
1185
	    inet_pton($test) >= inet_pton($start)) {
1186
		return true;
1187
	}
1188
1189
	return false;
1190 41b4867e Renato Botelho
}
1191
1192 da6cb29e Renato Botelho
/* returns true if $test is in the range between $start and $end */
1193
function is_inrange($test, $start, $end) {
1194
	return is_ipaddrv6($test) ? is_inrange_v6($test, $start, $end) : is_inrange_v4($test, $start, $end);
1195
}
1196
1197 ce94deb0 Luiz Otavio O Souza
function get_configured_vip_list($family = 'all', $type = VIP_ALL) {
1198 abcb2bed Ermal Lu?i
	global $config;
1199
1200 2a5960b0 Luiz Otavio O Souza
	$list = array();
1201 d9901ff4 Chris Buechler
	if (!is_array($config['virtualip']['vip']) || empty($config['virtualip']['vip'])) {
1202 2a5960b0 Luiz Otavio O Souza
		return ($list);
1203 d9901ff4 Chris Buechler
	}
1204 76153238 Luiz Otavio O Souza
1205
	$viparr = &$config['virtualip']['vip'];
1206
	foreach ($viparr as $vip) {
1207 ce94deb0 Luiz Otavio O Souza
1208
		if ($type == VIP_CARP) {
1209
			if ($vip['mode'] != "carp")
1210
				continue;
1211
		} elseif ($type == VIP_IPALIAS) {
1212
			if ($vip['mode'] != "ipalias")
1213
				continue;
1214
		} else {
1215
			if ($vip['mode'] != "carp" && $vip['mode'] != "ipalias")
1216
				continue;
1217 d9901ff4 Chris Buechler
		}
1218 2a5960b0 Luiz Otavio O Souza
1219
		if ($family == 'all' ||
1220
		    ($family == 'inet' && is_ipaddrv4($vip['subnet'])) ||
1221
		    ($family == 'inet6' && is_ipaddrv6($vip['subnet']))) {
1222
			$list["_vip{$vip['uniqid']}"] = $vip['subnet'];
1223 4e322e2c Phil Davis
		}
1224 2a5960b0 Luiz Otavio O Souza
	}
1225
	return ($list);
1226
}
1227
1228
function get_configured_vip($vipinterface = '') {
1229
1230
	return (get_configured_vip_detail($vipinterface, 'all', 'vip'));
1231
}
1232
1233
function get_configured_vip_interface($vipinterface = '') {
1234
1235
	return (get_configured_vip_detail($vipinterface, 'all', 'iface'));
1236
}
1237
1238
function get_configured_vip_ipv4($vipinterface = '') {
1239
1240
	return (get_configured_vip_detail($vipinterface, 'inet', 'ip'));
1241
}
1242 76153238 Luiz Otavio O Souza
1243 2a5960b0 Luiz Otavio O Souza
function get_configured_vip_ipv6($vipinterface = '') {
1244
1245
	return (get_configured_vip_detail($vipinterface, 'inet6', 'ip'));
1246
}
1247
1248
function get_configured_vip_subnetv4($vipinterface = '') {
1249
1250
	return (get_configured_vip_detail($vipinterface, 'inet', 'subnet'));
1251
}
1252
1253
function get_configured_vip_subnetv6($vipinterface = '') {
1254
1255
	return (get_configured_vip_detail($vipinterface, 'inet6', 'subnet'));
1256
}
1257
1258
function get_configured_vip_detail($vipinterface = '', $family = 'inet', $what = 'ip') {
1259
	global $config;
1260
1261
	if (empty($vipinterface) || !is_array($config['virtualip']['vip']) ||
1262
	    empty($config['virtualip']['vip'])) {
1263
		return (NULL);
1264
	}
1265
1266
	$viparr = &$config['virtualip']['vip'];
1267
	foreach ($viparr as $vip) {
1268 d9901ff4 Chris Buechler
		if ($vip['mode'] != "carp" && $vip['mode'] != "ipalias") {
1269 76153238 Luiz Otavio O Souza
			continue;
1270 d9901ff4 Chris Buechler
		}
1271 76153238 Luiz Otavio O Souza
1272 d9901ff4 Chris Buechler
		if ($vipinterface != "_vip{$vip['uniqid']}") {
1273 76153238 Luiz Otavio O Souza
			continue;
1274 d9901ff4 Chris Buechler
		}
1275 76153238 Luiz Otavio O Souza
1276
		switch ($what) {
1277
			case 'subnet':
1278 2a5960b0 Luiz Otavio O Souza
				if ($family == 'inet' && is_ipaddrv4($vip['subnet']))
1279
					return ($vip['subnet_bits']);
1280
				else if ($family == 'inet6' && is_ipaddrv6($vip['subnet']))
1281
					return ($vip['subnet_bits']);
1282 76153238 Luiz Otavio O Souza
				break;
1283
			case 'iface':
1284 2a5960b0 Luiz Otavio O Souza
				return ($vip['interface']);
1285 76153238 Luiz Otavio O Souza
				break;
1286
			case 'vip':
1287 2a5960b0 Luiz Otavio O Souza
				return ($vip);
1288 76153238 Luiz Otavio O Souza
				break;
1289
			case 'ip':
1290
			default:
1291 d9901ff4 Chris Buechler
				if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1292 2a5960b0 Luiz Otavio O Souza
					return ($vip['subnet']);
1293 d9901ff4 Chris Buechler
				} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1294 2a5960b0 Luiz Otavio O Souza
					return ($vip['subnet']);
1295 d9901ff4 Chris Buechler
				}
1296 76153238 Luiz Otavio O Souza
				break;
1297 5bbd08e1 Warren Baker
		}
1298 76153238 Luiz Otavio O Souza
		break;
1299 5bbd08e1 Warren Baker
	}
1300 abcb2bed Ermal Lu?i
1301 e0e28fdf Luiz Otavio O Souza
	return (NULL);
1302 e6c60013 Renato Botelho
}
1303 67b0902f pierrepomes
1304 88bc2760 Erik Fonnesbeck
/* comparison function for sorting by the order in which interfaces are normally created */
1305
function compare_interface_friendly_names($a, $b) {
1306 751533a2 Phil Davis
	if ($a == $b) {
1307 88bc2760 Erik Fonnesbeck
		return 0;
1308 751533a2 Phil Davis
	} else if ($a == 'wan') {
1309 88bc2760 Erik Fonnesbeck
		return -1;
1310 751533a2 Phil Davis
	} else if ($b == 'wan') {
1311 88bc2760 Erik Fonnesbeck
		return 1;
1312 751533a2 Phil Davis
	} else if ($a == 'lan') {
1313 88bc2760 Erik Fonnesbeck
		return -1;
1314 751533a2 Phil Davis
	} else if ($b == 'lan') {
1315 88bc2760 Erik Fonnesbeck
		return 1;
1316 751533a2 Phil Davis
	}
1317 88bc2760 Erik Fonnesbeck
1318
	return strnatcmp($a, $b);
1319
}
1320
1321 c8abe1d4 Ermal Luçi
/* return the configured interfaces list. */
1322 3ad5e089 Ermal Luçi
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
1323 c8abe1d4 Ermal Luçi
	global $config;
1324
1325
	$iflist = array();
1326 14f49fd0 Erik Fonnesbeck
1327 c8abe1d4 Ermal Luçi
	/* if list */
1328 751533a2 Phil Davis
	foreach ($config['interfaces'] as $if => $ifdetail) {
1329
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1330 42c9d20e Ermal Luçi
			continue;
1331 751533a2 Phil Davis
		}
1332
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1333 c8abe1d4 Ermal Luçi
			$iflist[$if] = $if;
1334 751533a2 Phil Davis
		}
1335 42c9d20e Ermal Luçi
	}
1336 c8abe1d4 Ermal Luçi
1337
	return $iflist;
1338
}
1339
1340 bb34737f Ermal Lu?i
/* return the configured interfaces list. */
1341
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
1342 8735afe8 Erik Fonnesbeck
	global $config;
1343 bb34737f Ermal Lu?i
1344 8735afe8 Erik Fonnesbeck
	$iflist = array();
1345 bb34737f Ermal Lu?i
1346 8735afe8 Erik Fonnesbeck
	/* if list */
1347 751533a2 Phil Davis
	foreach ($config['interfaces'] as $if => $ifdetail) {
1348
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1349 8735afe8 Erik Fonnesbeck
			continue;
1350 751533a2 Phil Davis
		}
1351 8735afe8 Erik Fonnesbeck
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1352 bb34737f Ermal Lu?i
			$tmpif = get_real_interface($if);
1353 751533a2 Phil Davis
			if (!empty($tmpif)) {
1354 bb34737f Ermal Lu?i
				$iflist[$tmpif] = $if;
1355 751533a2 Phil Davis
			}
1356 bb34737f Ermal Lu?i
		}
1357 8735afe8 Erik Fonnesbeck
	}
1358 bb34737f Ermal Lu?i
1359 8735afe8 Erik Fonnesbeck
	return $iflist;
1360 bb34737f Ermal Lu?i
}
1361
1362 c8abe1d4 Ermal Luçi
/* return the configured interfaces list with their description. */
1363 3ad5e089 Ermal Luçi
function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) {
1364 a42d1da2 Scott Ullrich
	global $config;
1365 c8abe1d4 Ermal Luçi
1366 a42d1da2 Scott Ullrich
	$iflist = array();
1367 c8abe1d4 Ermal Luçi
1368 a42d1da2 Scott Ullrich
	/* if list */
1369 751533a2 Phil Davis
	foreach ($config['interfaces'] as $if => $ifdetail) {
1370
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1371 8735afe8 Erik Fonnesbeck
			continue;
1372 751533a2 Phil Davis
		}
1373 47c8b036 Ermal Lu?i
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1374 751533a2 Phil Davis
			if (empty($ifdetail['descr'])) {
1375 8e74cb8d Ermal Luçi
				$iflist[$if] = strtoupper($if);
1376 751533a2 Phil Davis
			} else {
1377 44b0ec83 Scott Ullrich
				$iflist[$if] = strtoupper($ifdetail['descr']);
1378 751533a2 Phil Davis
			}
1379 0e218dc1 Ermal Luçi
		}
1380 42c9d20e Ermal Luçi
	}
1381 c8abe1d4 Ermal Luçi
1382 a42d1da2 Scott Ullrich
	return $iflist;
1383 c8abe1d4 Ermal Luçi
}
1384
1385 4fe9c2dc Scott Ullrich
/*
1386
 *   get_configured_ip_addresses() - Return a list of all configured
1387 2a5960b0 Luiz Otavio O Souza
 *   IPv4 addresses.
1388 4fe9c2dc Scott Ullrich
 *
1389
 */
1390
function get_configured_ip_addresses() {
1391 5dbd619f smos
	global $config;
1392 a1e4e2a7 Ermal
1393 751533a2 Phil Davis
	if (!function_exists('get_interface_ip')) {
1394 a1e4e2a7 Ermal
		require_once("interfaces.inc");
1395 751533a2 Phil Davis
	}
1396 4fe9c2dc Scott Ullrich
	$ip_array = array();
1397
	$interfaces = get_configured_interface_list();
1398 a1e4e2a7 Ermal
	if (is_array($interfaces)) {
1399 751533a2 Phil Davis
		foreach ($interfaces as $int) {
1400 d9114ce0 Scott Ullrich
			$ipaddr = get_interface_ip($int);
1401
			$ip_array[$int] = $ipaddr;
1402
		}
1403 4fe9c2dc Scott Ullrich
	}
1404 2a5960b0 Luiz Otavio O Souza
	$interfaces = get_configured_vip_list('inet');
1405 751533a2 Phil Davis
	if (is_array($interfaces)) {
1406
		foreach ($interfaces as $int => $ipaddr) {
1407 d9114ce0 Scott Ullrich
			$ip_array[$int] = $ipaddr;
1408 751533a2 Phil Davis
		}
1409
	}
1410 5dbd619f smos
1411
	/* pppoe server */
1412 a1e4e2a7 Ermal
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
1413 751533a2 Phil Davis
		foreach ($config['pppoes']['pppoe'] as $pppoe) {
1414 5dbd619f smos
			if ($pppoe['mode'] == "server") {
1415 751533a2 Phil Davis
				if (is_ipaddr($pppoe['localip'])) {
1416 5dbd619f smos
					$int = "pppoes". $pppoe['pppoeid'];
1417
					$ip_array[$int] = $pppoe['localip'];
1418
				}
1419
			}
1420
		}
1421
	}
1422 a1e4e2a7 Ermal
1423 4fe9c2dc Scott Ullrich
	return $ip_array;
1424
}
1425 c8abe1d4 Ermal Luçi
1426 e6f7e0be smos
/*
1427
 *   get_configured_ipv6_addresses() - Return a list of all configured
1428 2a5960b0 Luiz Otavio O Souza
 *   IPv6 addresses.
1429 e6f7e0be smos
 *
1430
 */
1431 6a53de6f NewEraCracker
function get_configured_ipv6_addresses($linklocal_fallback = false) {
1432 e6f7e0be smos
	require_once("interfaces.inc");
1433
	$ipv6_array = array();
1434
	$interfaces = get_configured_interface_list();
1435 751533a2 Phil Davis
	if (is_array($interfaces)) {
1436
		foreach ($interfaces as $int) {
1437 6a53de6f NewEraCracker
			$ipaddrv6 = get_interface_ipv6($int, false, $linklocal_fallback);
1438 e6f7e0be smos
			$ipv6_array[$int] = $ipaddrv6;
1439
		}
1440
	}
1441 2a5960b0 Luiz Otavio O Souza
	$interfaces = get_configured_vip_list('inet6');
1442 751533a2 Phil Davis
	if (is_array($interfaces)) {
1443
		foreach ($interfaces as $int => $ipaddrv6) {
1444 e6f7e0be smos
			$ipv6_array[$int] = $ipaddrv6;
1445 751533a2 Phil Davis
		}
1446
	}
1447 e6f7e0be smos
	return $ipv6_array;
1448
}
1449
1450 36f546e9 Scott Ullrich
/*
1451
 *   get_interface_list() - Return a list of all physical interfaces
1452
 *   along with MAC and status.
1453
 *
1454
 *   $mode = "active" - use ifconfig -lu
1455
 *           "media"  - use ifconfig to check physical connection
1456
 *			status (much slower)
1457
 */
1458
function get_interface_list($mode = "active", $keyby = "physical", $vfaces = "") {
1459 86a5e1a8 Renato Botelho
	global $config;
1460 65bed2d2 Scott Ullrich
	$upints = array();
1461 86a5e1a8 Renato Botelho
	/* get a list of virtual interface types */
1462 751533a2 Phil Davis
	if (!$vfaces) {
1463 086cf944 Phil Davis
		$vfaces = array(
1464 9ce38409 Scott Ullrich
				'bridge',
1465
				'ppp',
1466 27c0c7c6 Ermal Lu?i
				'pppoe',
1467
				'pptp',
1468
				'l2tp',
1469 9ce38409 Scott Ullrich
				'sl',
1470
				'gif',
1471 613571ea Ermal Luçi
				'gre',
1472 9ce38409 Scott Ullrich
				'faith',
1473
				'lo',
1474
				'ng',
1475 27616d6e Seth Mos
				'_vlan',
1476 7c53bc7b Erik Fonnesbeck
				'_wlan',
1477 9ce38409 Scott Ullrich
				'pflog',
1478 a42d1da2 Scott Ullrich
				'plip',
1479 9ce38409 Scott Ullrich
				'pfsync',
1480
				'enc',
1481
				'tun',
1482 1fb2bf25 Ermal Lu?i
				'lagg',
1483 1fd35e95 Ermal
				'vip',
1484
				'ipfw'
1485 9ce38409 Scott Ullrich
		);
1486 36f546e9 Scott Ullrich
	}
1487 751533a2 Phil Davis
	switch ($mode) {
1488
		case "active":
1489
			$upints = pfSense_interface_listget(IFF_UP);
1490
			break;
1491
		case "media":
1492
			$intlist = pfSense_interface_listget();
1493
			$ifconfig = "";
1494
			exec("/sbin/ifconfig -a", $ifconfig);
1495
			$regexp = '/(' . implode('|', $intlist) . '):\s/';
1496
			$ifstatus = preg_grep('/status:/', $ifconfig);
1497
			foreach ($ifstatus as $status) {
1498
				$int = array_shift($intlist);
1499
				if (stristr($status, "active")) {
1500
					$upints[] = $int;
1501
				}
1502
			}
1503
			break;
1504
		default:
1505
			$upints = pfSense_interface_listget();
1506
			break;
1507 20203646 Colin Smith
	}
1508 86a5e1a8 Renato Botelho
	/* build interface list with netstat */
1509
	$linkinfo = "";
1510
	exec("/usr/bin/netstat -inW -f link | awk '{ print $1, $4 }'", $linkinfo);
1511
	array_shift($linkinfo);
1512 89d1f0f2 Scott Ullrich
	/* build ip address list with netstat */
1513 767a716e Scott Ullrich
	$ipinfo = "";
1514 89d1f0f2 Scott Ullrich
	exec("/usr/bin/netstat -inW -f inet | awk '{ print $1, $4 }'", $ipinfo);
1515
	array_shift($ipinfo);
1516 751533a2 Phil Davis
	foreach ($linkinfo as $link) {
1517 89d1f0f2 Scott Ullrich
		$friendly = "";
1518 5bbd08e1 Warren Baker
		$alink = explode(" ", $link);
1519
		$ifname = rtrim(trim($alink[0]), '*');
1520
		/* trim out all numbers before checking for vfaces */
1521 494be6e8 Ermal Lu?i
		if (!in_array(array_shift(preg_split('/\d/', $ifname)), $vfaces) &&
1522 751533a2 Phil Davis
		    !stristr($ifname, "_vlan") && !stristr($ifname, "_wlan")) {
1523 20203646 Colin Smith
			$toput = array(
1524
					"mac" => trim($alink[1]),
1525
					"up" => in_array($ifname, $upints)
1526
				);
1527 751533a2 Phil Davis
			foreach ($ipinfo as $ip) {
1528 89d1f0f2 Scott Ullrich
				$aip = explode(" ", $ip);
1529 751533a2 Phil Davis
				if ($aip[0] == $ifname) {
1530 89d1f0f2 Scott Ullrich
					$toput['ipaddr'] = $aip[1];
1531
				}
1532
			}
1533 72993196 Ermal
			if (is_array($config['interfaces'])) {
1534 751533a2 Phil Davis
				foreach ($config['interfaces'] as $name => $int) {
1535
					if ($int['if'] == $ifname) {
1536
						$friendly = $name;
1537
					}
1538
				}
1539 20203646 Colin Smith
			}
1540 751533a2 Phil Davis
			switch ($keyby) {
1541 20203646 Colin Smith
			case "physical":
1542 751533a2 Phil Davis
				if ($friendly != "") {
1543 89d1f0f2 Scott Ullrich
					$toput['friendly'] = $friendly;
1544
				}
1545 a296c95d Seth Mos
				$dmesg_arr = array();
1546
				exec("/sbin/dmesg |grep $ifname | head -n1", $dmesg_arr);
1547
				preg_match_all("/<(.*?)>/i", $dmesg_arr[0], $dmesg);
1548
				$toput['dmesg'] = $dmesg[1][0];
1549 20203646 Colin Smith
				$iflist[$ifname] = $toput;
1550 3154d7ed Colin Smith
				break;
1551 4aca19b3 Scott Ullrich
			case "ppp":
1552 86a5e1a8 Renato Botelho
1553 20203646 Colin Smith
			case "friendly":
1554 751533a2 Phil Davis
				if ($friendly != "") {
1555 89d1f0f2 Scott Ullrich
					$toput['if'] = $ifname;
1556
					$iflist[$friendly] = $toput;
1557
				}
1558 3154d7ed Colin Smith
				break;
1559
			}
1560 5bbd08e1 Warren Baker
		}
1561
	}
1562
	return $iflist;
1563 5b237745 Scott Ullrich
}
1564
1565 2b4d37de Ermal Lu?i
/****f* util/log_error
1566
* NAME
1567
*   log_error  - Sends a string to syslog.
1568
* INPUTS
1569
*   $error     - string containing the syslog message.
1570
* RESULT
1571
*   null
1572
******/
1573
function log_error($error) {
1574 5bbd08e1 Warren Baker
	global $g;
1575
	$page = $_SERVER['SCRIPT_NAME'];
1576 866b1d61 jim-p
	if (empty($page)) {
1577
		$files = get_included_files();
1578
		$page = basename($files[0]);
1579
	}
1580 0d0cb047 jim-p
	syslog(LOG_ERR, "$page: $error");
1581 751533a2 Phil Davis
	if ($g['debug']) {
1582 5bbd08e1 Warren Baker
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1583 751533a2 Phil Davis
	}
1584 5bbd08e1 Warren Baker
	return;
1585 2b4d37de Ermal Lu?i
}
1586
1587 3aba1835 Scott Ullrich
/****f* util/log_auth
1588
* NAME
1589 1198abf9 PiBa-NL
*   log_auth   - Sends a string to syslog as LOG_AUTH facility
1590 3aba1835 Scott Ullrich
* INPUTS
1591
*   $error     - string containing the syslog message.
1592
* RESULT
1593
*   null
1594
******/
1595
function log_auth($error) {
1596 5bbd08e1 Warren Baker
	global $g;
1597
	$page = $_SERVER['SCRIPT_NAME'];
1598
	syslog(LOG_AUTH, "$page: $error");
1599 751533a2 Phil Davis
	if ($g['debug']) {
1600 5bbd08e1 Warren Baker
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1601 751533a2 Phil Davis
	}
1602 5bbd08e1 Warren Baker
	return;
1603 3aba1835 Scott Ullrich
}
1604
1605 83bc3749 Ermal Lu?i
/****f* util/exec_command
1606
 * NAME
1607
 *   exec_command - Execute a command and return a string of the result.
1608
 * INPUTS
1609
 *   $command   - String of the command to be executed.
1610
 * RESULT
1611
 *   String containing the command's result.
1612
 * NOTES
1613
 *   This function returns the command's stdout and stderr.
1614
 ******/
1615
function exec_command($command) {
1616 5bbd08e1 Warren Baker
	$output = array();
1617 873c1701 Renato Botelho
	exec($command . ' 2>&1', $output);
1618 5bbd08e1 Warren Baker
	return(implode("\n", $output));
1619 83bc3749 Ermal Lu?i
}
1620
1621 e00ad357 Renato Botelho
/* wrapper for exec()
1622 f0b41548 stilez
   Executes in background or foreground.
1623
   For background execution, returns PID of background process to allow calling code control */
1624
function mwexec($command, $nologentry = false, $clearsigmask = false, $background = false) {
1625 5b237745 Scott Ullrich
	global $g;
1626 f0b41548 stilez
	$retval = 0;
1627 435a418f Ermal
1628 5b237745 Scott Ullrich
	if ($g['debug']) {
1629 751533a2 Phil Davis
		if (!$_SERVER['REMOTE_ADDR']) {
1630 f0b41548 stilez
			echo "mwexec(): $command" . ($background ? " [BG]":"") . "\n";
1631 751533a2 Phil Davis
		}
1632 f9db3cda Seth Mos
	}
1633 b61e8960 jim-p
	if ($clearsigmask) {
1634
		$oldset = array();
1635
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1636
	}
1637 f0b41548 stilez
1638 2b1f6ed2 stilez
	if ($background) {
1639
		// start background process and return PID
1640
		$retval = exec("/usr/bin/nohup $command > /dev/null 2>&1 & echo $!");
1641
	} else {
1642
		// run in foreground, and (optionally) log if nonzero return
1643
		$outputarray = array();
1644 f0b41548 stilez
		exec("$command 2>&1", $outputarray, $retval);
1645 4e322e2c Phil Davis
		if (($retval <> 0) && (!$nologentry || isset($config['system']['developerspew']))) {
1646 f812b883 stilez
			log_error(sprintf(gettext("The command '%1\$s' returned exit code '%2\$d', the output was '%3\$s' "), $command, $retval, implode(" ", $outputarray)));
1647 4e322e2c Phil Davis
		}
1648 f0b41548 stilez
	}
1649
1650 b61e8960 jim-p
	if ($clearsigmask) {
1651
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1652
	}
1653 435a418f Ermal
1654 98bbf05a Scott Ullrich
	return $retval;
1655 5b237745 Scott Ullrich
}
1656
1657
/* wrapper for exec() in background */
1658 b61e8960 jim-p
function mwexec_bg($command, $clearsigmask = false) {
1659 f0b41548 stilez
	return mwexec($command, false, $clearsigmask, true);
1660 5b237745 Scott Ullrich
}
1661
1662 d96a39ba Phil Davis
/*	unlink a file, or pattern-match of a file, if it exists
1663
	if the file/path contains glob() compatible wildcards, all matching files will be unlinked
1664
	any warning/errors are suppressed (e.g. no matching files to delete)
1665
	If there are matching file(s) and they were all unlinked OK, then return true.
1666
	Otherwise return false (the requested file(s) did not exist, or could not be deleted)
1667
	This allows the caller to know if they were the one to successfully delete the file(s).
1668
*/
1669 5b237745 Scott Ullrich
function unlink_if_exists($fn) {
1670 336cb718 Scott Ullrich
	$to_do = glob($fn);
1671 a85ad858 stilez
	if (is_array($to_do) && count($to_do) > 0) {
1672 d96a39ba Phil Davis
		// Returns an array of true/false indicating if each unlink worked
1673
		$results = @array_map("unlink", $to_do);
1674
		// If there is no false in the array, then all went well
1675
		$result = !in_array(false, $results, true);
1676 336cb718 Scott Ullrich
	} else {
1677 d96a39ba Phil Davis
		$result = @unlink($fn);
1678 336cb718 Scott Ullrich
	}
1679 d96a39ba Phil Davis
	return $result;
1680 5b237745 Scott Ullrich
}
1681
/* make a global alias table (for faster lookups) */
1682 918a884d Bill Marquette
function alias_make_table($config) {
1683
	global $aliastable;
1684 98bbf05a Scott Ullrich
1685 5b237745 Scott Ullrich
	$aliastable = array();
1686 98bbf05a Scott Ullrich
1687 5b237745 Scott Ullrich
	if (is_array($config['aliases']['alias'])) {
1688
		foreach ($config['aliases']['alias'] as $alias) {
1689 751533a2 Phil Davis
			if ($alias['name']) {
1690 5b237745 Scott Ullrich
				$aliastable[$alias['name']] = $alias['address'];
1691 751533a2 Phil Davis
			}
1692 5b237745 Scott Ullrich
		}
1693
	}
1694
}
1695 5ffa3389 Ermal
1696 5b237745 Scott Ullrich
/* check if an alias exists */
1697
function is_alias($name) {
1698
	global $aliastable;
1699 98bbf05a Scott Ullrich
1700 5b237745 Scott Ullrich
	return isset($aliastable[$name]);
1701 b8014f9d Scott Ullrich
}
1702 27ff8a3c Scott Ullrich
1703 5ffa3389 Ermal
function alias_get_type($name) {
1704 86a5e1a8 Renato Botelho
	global $config;
1705
1706 5ffa3389 Ermal
	if (is_array($config['aliases']['alias'])) {
1707
		foreach ($config['aliases']['alias'] as $alias) {
1708 751533a2 Phil Davis
			if ($name == $alias['name']) {
1709 5ffa3389 Ermal
				return $alias['type'];
1710 751533a2 Phil Davis
			}
1711 5ffa3389 Ermal
		}
1712
	}
1713
1714 86a5e1a8 Renato Botelho
	return "";
1715 5ffa3389 Ermal
}
1716
1717 5b237745 Scott Ullrich
/* expand a host or network alias, if necessary */
1718
function alias_expand($name) {
1719 2ec7ab35 Chris Buechler
	global $config, $aliastable;
1720
	$urltable_prefix = "/var/db/aliastables/";
1721
	$urltable_filename = $urltable_prefix . $name . ".txt";
1722 98bbf05a Scott Ullrich
1723 751533a2 Phil Davis
	if (isset($aliastable[$name])) {
1724 a97a77a2 Phil Davis
		// alias names cannot be strictly numeric. redmine #4289
1725
		if (is_numericint($name)) {
1726
			return null;
1727
		}
1728 2ec7ab35 Chris Buechler
		// make sure if it's a ports alias, it actually exists. redmine #5845
1729
		foreach ($config['aliases']['alias'] as $alias) {
1730
			if ($alias['name'] == $name) {
1731
				if ($alias['type'] == "urltable_ports") {
1732
					if (is_URL($alias['url']) && file_exists($urltable_filename) && filesize($urltable_filename)) {
1733
						return "\${$name}";
1734
					} else {
1735
						return null;
1736
					}
1737
				}
1738
			}
1739
		}
1740 4335dc87 Bill Marquette
		return "\${$name}";
1741 751533a2 Phil Davis
	} else if (is_ipaddr($name) || is_subnet($name) || is_port($name) || is_portrange($name)) {
1742 57989da5 Scott Ullrich
		return "{$name}";
1743 751533a2 Phil Davis
	} else {
1744 5b237745 Scott Ullrich
		return null;
1745 751533a2 Phil Davis
	}
1746 5b237745 Scott Ullrich
}
1747
1748 c7de8be4 jim-p
function alias_expand_urltable($name) {
1749
	global $config;
1750
	$urltable_prefix = "/var/db/aliastables/";
1751
	$urltable_filename = $urltable_prefix . $name . ".txt";
1752
1753 5ffa3389 Ermal
	if (is_array($config['aliases']['alias'])) {
1754
		foreach ($config['aliases']['alias'] as $alias) {
1755 dd042c51 Renato Botelho
			if (preg_match("/urltable/i", $alias['type']) && ($alias['name'] == $name)) {
1756 e5581024 Chris Buechler
				if (is_URL($alias["url"]) && file_exists($urltable_filename)) {
1757
					if (!filesize($urltable_filename)) {
1758
						// file exists, but is empty, try to sync
1759
						send_event("service sync alias {$name}");
1760
					}
1761 5ffa3389 Ermal
					return $urltable_filename;
1762 5b2b1f4e Ermal LUÇI
				} else {
1763
					send_event("service sync alias {$name}");
1764
					break;
1765 751533a2 Phil Davis
				}
1766 5ffa3389 Ermal
			}
1767 c7de8be4 jim-p
		}
1768
	}
1769
	return null;
1770
}
1771
1772 5b237745 Scott Ullrich
/* obtain MAC address given an IP address by looking at the ARP table */
1773
function arp_get_mac_by_ip($ip) {
1774 873c1701 Renato Botelho
	mwexec("/sbin/ping -c 1 -t 1 " . escapeshellarg($ip), true);
1775 767a716e Scott Ullrich
	$arpoutput = "";
1776 873c1701 Renato Botelho
	exec("/usr/sbin/arp -n " . escapeshellarg($ip), $arpoutput);
1777 98bbf05a Scott Ullrich
1778 5b237745 Scott Ullrich
	if ($arpoutput[0]) {
1779
		$arpi = explode(" ", $arpoutput[0]);
1780
		$macaddr = $arpi[3];
1781 751533a2 Phil Davis
		if (is_macaddr($macaddr)) {
1782 5b237745 Scott Ullrich
			return $macaddr;
1783 751533a2 Phil Davis
		} else {
1784 5b237745 Scott Ullrich
			return false;
1785 751533a2 Phil Davis
		}
1786 5b237745 Scott Ullrich
	}
1787 98bbf05a Scott Ullrich
1788 5b237745 Scott Ullrich
	return false;
1789
}
1790
1791 98bbf05a Scott Ullrich
/* return a fieldname that is safe for xml usage */
1792
function xml_safe_fieldname($fieldname) {
1793 4f3fc80d Renato Botelho
	$replace = array(
1794
	    '/', '-', ' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
1795
	    '_', '+', '=', '{', '}', '[', ']', '|', '/', '<', '>', '?',
1796
	    ':', ',', '.', '\'', '\\'
1797
	);
1798 ddce8ef2 Colin Smith
	return strtolower(str_replace($replace, "", $fieldname));
1799 98bbf05a Scott Ullrich
}
1800
1801 805b9ab6 Ermal
function mac_format($clientmac) {
1802 86a5e1a8 Renato Botelho
	global $config, $cpzone;
1803 4129df39 Scott Ullrich
1804 86a5e1a8 Renato Botelho
	$mac = explode(":", $clientmac);
1805
	$mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1806 4129df39 Scott Ullrich
1807 751533a2 Phil Davis
	switch ($mac_format) {
1808
		case 'singledash':
1809
			return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1810 4129df39 Scott Ullrich
1811 751533a2 Phil Davis
		case 'ietf':
1812
			return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1813 4129df39 Scott Ullrich
1814 751533a2 Phil Davis
		case 'cisco':
1815
			return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1816 4129df39 Scott Ullrich
1817 751533a2 Phil Davis
		case 'unformatted':
1818
			return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1819 4129df39 Scott Ullrich
1820 751533a2 Phil Davis
		default:
1821
			return $clientmac;
1822 86a5e1a8 Renato Botelho
	}
1823 4129df39 Scott Ullrich
}
1824
1825 979cd6db Scott Ullrich
function resolve_retry($hostname, $retries = 5) {
1826
1827 751533a2 Phil Davis
	if (is_ipaddr($hostname)) {
1828 5bbd08e1 Warren Baker
		return $hostname;
1829 751533a2 Phil Davis
	}
1830 979cd6db Scott Ullrich
1831 86a5e1a8 Renato Botelho
	for ($i = 0; $i < $retries; $i++) {
1832 6c4f3b54 Seth Mos
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1833 86a5e1a8 Renato Botelho
		$ip = gethostbyname($hostname);
1834 979cd6db Scott Ullrich
1835 5bbd08e1 Warren Baker
		if ($ip && $ip != $hostname) {
1836
			/* success */
1837
			return $ip;
1838
		}
1839 979cd6db Scott Ullrich
1840 5bbd08e1 Warren Baker
		sleep(1);
1841
	}
1842 979cd6db Scott Ullrich
1843 5bbd08e1 Warren Baker
	return false;
1844 979cd6db Scott Ullrich
}
1845
1846 44bfd1fa Scott Ullrich
function format_bytes($bytes) {
1847 4eac105f Phil Davis
	if ($bytes >= 1099511627776) {
1848 7b512ab3 Phil Davis
		return sprintf("%.2f TiB", $bytes/1099511627776);
1849 4eac105f Phil Davis
	} else if ($bytes >= 1073741824) {
1850 7b512ab3 Phil Davis
		return sprintf("%.2f GiB", $bytes/1073741824);
1851 44bfd1fa Scott Ullrich
	} else if ($bytes >= 1048576) {
1852 7b512ab3 Phil Davis
		return sprintf("%.2f MiB", $bytes/1048576);
1853 44bfd1fa Scott Ullrich
	} else if ($bytes >= 1024) {
1854 7b512ab3 Phil Davis
		return sprintf("%.0f KiB", $bytes/1024);
1855 44bfd1fa Scott Ullrich
	} else {
1856 10ae204f Stephen Beaver
		return sprintf("%d B", $bytes);
1857 44bfd1fa Scott Ullrich
	}
1858
}
1859
1860 cc2cff0b Luiz Otavio O Souza
function format_number($num, $precision = 3) {
1861
	$units = array('', 'K', 'M', 'G', 'T');
1862
1863
	$i = 0;
1864
	while ($num > 1000 && $i < count($units)) {
1865
		$num /= 1000;
1866
		$i++;
1867
	}
1868 92130da3 Luiz Otavio O Souza
	$num = round($num, $precision);
1869 cc2cff0b Luiz Otavio O Souza
1870
	return ("$num {$units[$i]}");
1871
}
1872
1873 4f7956ad Steve Beaver
function update_filter_reload_status($text, $new=false) {
1874 5bbd08e1 Warren Baker
	global $g;
1875 2b4d37de Ermal Lu?i
1876 4f7956ad Steve Beaver
	if ($new) {
1877
		file_put_contents("{$g['varrun_path']}/filter_reload_status", $text  . PHP_EOL);
1878
	} else {
1879
		file_put_contents("{$g['varrun_path']}/filter_reload_status", $text  . PHP_EOL, FILE_APPEND);
1880
	}
1881 2b4d37de Ermal Lu?i
}
1882
1883 a2219caf Renato Botelho
/****** util/return_dir_as_array
1884 2b4d37de Ermal Lu?i
 * NAME
1885
 *   return_dir_as_array - Return a directory's contents as an array.
1886
 * INPUTS
1887 a2219caf Renato Botelho
 *   $dir          - string containing the path to the desired directory.
1888
 *   $filter_regex - string containing a regular expression to filter file names. Default empty.
1889 2b4d37de Ermal Lu?i
 * RESULT
1890
 *   $dir_array - array containing the directory's contents. This array will be empty if the path specified is invalid.
1891
 ******/
1892 a2219caf Renato Botelho
function return_dir_as_array($dir, $filter_regex = '') {
1893 5bbd08e1 Warren Baker
	$dir_array = array();
1894
	if (is_dir($dir)) {
1895
		if ($dh = opendir($dir)) {
1896
			while (($file = readdir($dh)) !== false) {
1897 751533a2 Phil Davis
				if (($file == ".") || ($file == "..")) {
1898 a2219caf Renato Botelho
					continue;
1899 751533a2 Phil Davis
				}
1900 a2219caf Renato Botelho
1901 751533a2 Phil Davis
				if (empty($filter_regex) || preg_match($filter_regex, $file)) {
1902 5bbd08e1 Warren Baker
					array_push($dir_array, $file);
1903 751533a2 Phil Davis
				}
1904 5bbd08e1 Warren Baker
			}
1905
			closedir($dh);
1906
		}
1907
	}
1908
	return $dir_array;
1909 2b4d37de Ermal Lu?i
}
1910
1911
function run_plugins($directory) {
1912 5bbd08e1 Warren Baker
	global $config, $g;
1913
1914
	/* process packager manager custom rules */
1915
	$files = return_dir_as_array($directory);
1916
	if (is_array($files)) {
1917
		foreach ($files as $file) {
1918 751533a2 Phil Davis
			if (stristr($file, ".sh") == true) {
1919 5bbd08e1 Warren Baker
				mwexec($directory . $file . " start");
1920 086cf944 Phil Davis
			} else if (!is_dir($directory . "/" . $file) && stristr($file, ".inc")) {
1921 5bbd08e1 Warren Baker
				require_once($directory . "/" . $file);
1922 751533a2 Phil Davis
			}
1923 2990acf8 Scott Ullrich
		}
1924 5bbd08e1 Warren Baker
	}
1925 2b4d37de Ermal Lu?i
}
1926
1927
/*
1928
 *    safe_mkdir($path, $mode = 0755)
1929
 *    create directory if it doesn't already exist and isn't a file!
1930
 */
1931 6c07db48 Phil Davis
function safe_mkdir($path, $mode = 0755) {
1932 5bbd08e1 Warren Baker
	global $g;
1933 2b4d37de Ermal Lu?i
1934 5bbd08e1 Warren Baker
	if (!is_file($path) && !is_dir($path)) {
1935
		return @mkdir($path, $mode, true);
1936
	} else {
1937
		return false;
1938
	}
1939 2b4d37de Ermal Lu?i
}
1940
1941 aa4f498d Erik Fonnesbeck
/*
1942
 * get_sysctl($names)
1943
 * Get values of sysctl OID's listed in $names (accepts an array or a single
1944
 * name) and return an array of key/value pairs set for those that exist
1945
 */
1946
function get_sysctl($names) {
1947 751533a2 Phil Davis
	if (empty($names)) {
1948 aa4f498d Erik Fonnesbeck
		return array();
1949 751533a2 Phil Davis
	}
1950 aa4f498d Erik Fonnesbeck
1951
	if (is_array($names)) {
1952
		$name_list = array();
1953
		foreach ($names as $name) {
1954
			$name_list[] = escapeshellarg($name);
1955
		}
1956 751533a2 Phil Davis
	} else {
1957 aa4f498d Erik Fonnesbeck
		$name_list = array(escapeshellarg($names));
1958 751533a2 Phil Davis
	}
1959 aa4f498d Erik Fonnesbeck
1960
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1961
	$values = array();
1962
	foreach ($output as $line) {
1963
		$line = explode(": ", $line, 2);
1964 751533a2 Phil Davis
		if (count($line) == 2) {
1965 aa4f498d Erik Fonnesbeck
			$values[$line[0]] = $line[1];
1966 751533a2 Phil Davis
		}
1967 aa4f498d Erik Fonnesbeck
	}
1968
1969
	return $values;
1970
}
1971
1972 ff23363d Renato Botelho
/*
1973
 * get_single_sysctl($name)
1974
 * Wrapper for get_sysctl() to simplify read of a single sysctl value
1975
 * return the value for sysctl $name or empty string if it doesn't exist
1976
 */
1977
function get_single_sysctl($name) {
1978 751533a2 Phil Davis
	if (empty($name)) {
1979 ff23363d Renato Botelho
		return "";
1980 751533a2 Phil Davis
	}
1981 ff23363d Renato Botelho
1982
	$value = get_sysctl($name);
1983 751533a2 Phil Davis
	if (empty($value) || !isset($value[$name])) {
1984 ff23363d Renato Botelho
		return "";
1985 751533a2 Phil Davis
	}
1986 ff23363d Renato Botelho
1987
	return $value[$name];
1988
}
1989
1990 aa4f498d Erik Fonnesbeck
/*
1991
 * set_sysctl($value_list)
1992
 * Set sysctl OID's listed as key/value pairs and return
1993
 * an array with keys set for those that succeeded
1994
 */
1995
function set_sysctl($values) {
1996 751533a2 Phil Davis
	if (empty($values)) {
1997 aa4f498d Erik Fonnesbeck
		return array();
1998 751533a2 Phil Davis
	}
1999 aa4f498d Erik Fonnesbeck
2000
	$value_list = array();
2001
	foreach ($values as $key => $value) {
2002
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
2003
	}
2004
2005
	exec("/sbin/sysctl -i " . implode(" ", $value_list), $output, $success);
2006
2007
	/* Retry individually if failed (one or more read-only) */
2008
	if ($success <> 0 && count($value_list) > 1) {
2009
		foreach ($value_list as $value) {
2010
			exec("/sbin/sysctl -i " . $value, $output);
2011
		}
2012
	}
2013
2014
	$ret = array();
2015
	foreach ($output as $line) {
2016
		$line = explode(": ", $line, 2);
2017 751533a2 Phil Davis
		if (count($line) == 2) {
2018 aa4f498d Erik Fonnesbeck
			$ret[$line[0]] = true;
2019 751533a2 Phil Davis
		}
2020 aa4f498d Erik Fonnesbeck
	}
2021
2022
	return $ret;
2023
}
2024
2025 82f75815 Renato Botelho
/*
2026
 * set_single_sysctl($name, $value)
2027
 * Wrapper to set_sysctl() to make it simple to set only one sysctl
2028 751533a2 Phil Davis
 * returns boolean meaning if it succeeded
2029 82f75815 Renato Botelho
 */
2030
function set_single_sysctl($name, $value) {
2031 751533a2 Phil Davis
	if (empty($name)) {
2032 82f75815 Renato Botelho
		return false;
2033 751533a2 Phil Davis
	}
2034 82f75815 Renato Botelho
2035
	$result = set_sysctl(array($name => $value));
2036
2037 751533a2 Phil Davis
	if (!isset($result[$name]) || $result[$name] != $value) {
2038 82f75815 Renato Botelho
		return false;
2039 751533a2 Phil Davis
	}
2040 82f75815 Renato Botelho
2041
	return true;
2042
}
2043
2044 2b4d37de Ermal Lu?i
/*
2045
 *     get_memory()
2046
 *     returns an array listing the amount of
2047
 *     memory installed in the hardware
2048 517fb89e Phil Davis
 *     [0] net memory available for the OS (FreeBSD) after some is taken by BIOS, video or whatever - e.g. 235 MBytes
2049
 *     [1] real (actual) memory of the system, should be the size of the RAM card/s - e.g. 256 MBytes
2050 2b4d37de Ermal Lu?i
 */
2051
function get_memory() {
2052 971de1f9 Renato Botelho
	$physmem = get_single_sysctl("hw.physmem");
2053
	$realmem = get_single_sysctl("hw.realmem");
2054 5cd73772 Ermal
	/* convert from bytes to megabytes */
2055 086cf944 Phil Davis
	return array(($physmem/1048576), ($realmem/1048576));
2056 2b4d37de Ermal Lu?i
}
2057
2058
function mute_kernel_msgs() {
2059 6fa9f38c Renato Botelho
	global $g, $config;
2060 dc61252a Renato Botelho
2061 751533a2 Phil Davis
	if ($config['system']['enableserial']) {
2062 86a5e1a8 Renato Botelho
		return;
2063 751533a2 Phil Davis
	}
2064 5bbd08e1 Warren Baker
	exec("/sbin/conscontrol mute on");
2065 2b4d37de Ermal Lu?i
}
2066
2067
function unmute_kernel_msgs() {
2068 6fa9f38c Renato Botelho
	global $g;
2069 dc61252a Renato Botelho
2070 5bbd08e1 Warren Baker
	exec("/sbin/conscontrol mute off");
2071 2b4d37de Ermal Lu?i
}
2072
2073
function start_devd() {
2074 505e3e0e Renato Botelho
	global $g;
2075
2076 751533a2 Phil Davis
	/* Use the undocumented -q options of devd to quiet its log spamming */
2077 505e3e0e Renato Botelho
	$_gb = exec("/sbin/devd -q -f /etc/{$g['product_name']}-devd.conf");
2078 5bbd08e1 Warren Baker
	sleep(1);
2079 a7f79eda Ermal LUÇI
	unset($_gb);
2080 2b4d37de Ermal Lu?i
}
2081
2082 66bcba1b Ermal
function is_interface_vlan_mismatch() {
2083 5bbd08e1 Warren Baker
	global $config, $g;
2084 66bcba1b Ermal
2085 5bbd08e1 Warren Baker
	if (is_array($config['vlans']['vlan'])) {
2086
		foreach ($config['vlans']['vlan'] as $vlan) {
2087 2915acf8 Chris Buechler
			if (substr($vlan['if'], 0, 4) == "lagg") {
2088
				return false;
2089
			}
2090 751533a2 Phil Davis
			if (does_interface_exist($vlan['if']) == false) {
2091 66bcba1b Ermal
				return true;
2092 751533a2 Phil Davis
			}
2093 5bbd08e1 Warren Baker
		}
2094
	}
2095 66bcba1b Ermal
2096
	return false;
2097
}
2098
2099 2b4d37de Ermal Lu?i
function is_interface_mismatch() {
2100 857da904 Scott Ullrich
	global $config, $g;
2101 2b4d37de Ermal Lu?i
2102 857da904 Scott Ullrich
	$do_assign = false;
2103
	$i = 0;
2104 e0a45ce0 Erik Fonnesbeck
	$missing_interfaces = array();
2105 72993196 Ermal
	if (is_array($config['interfaces'])) {
2106 857da904 Scott Ullrich
		foreach ($config['interfaces'] as $ifname => $ifcfg) {
2107 fd863e5c Erik Fonnesbeck
			if (preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_wlan/i", $ifcfg['if'])) {
2108 857da904 Scott Ullrich
				// Do not check these interfaces.
2109
				$i++;
2110
				continue;
2111 751533a2 Phil Davis
			} else if (does_interface_exist($ifcfg['if']) == false) {
2112 e0a45ce0 Erik Fonnesbeck
				$missing_interfaces[] = $ifcfg['if'];
2113 72993196 Ermal
				$do_assign = true;
2114 751533a2 Phil Davis
			} else {
2115 857da904 Scott Ullrich
				$i++;
2116 751533a2 Phil Davis
			}
2117 857da904 Scott Ullrich
		}
2118 72993196 Ermal
	}
2119 2b4d37de Ermal Lu?i
2120 751533a2 Phil Davis
	if (file_exists("{$g['tmp_path']}/assign_complete")) {
2121 e0a45ce0 Erik Fonnesbeck
		$do_assign = false;
2122 751533a2 Phil Davis
	}
2123 e0a45ce0 Erik Fonnesbeck
2124 751533a2 Phil Davis
	if (!empty($missing_interfaces) && $do_assign) {
2125 e0a45ce0 Erik Fonnesbeck
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
2126 751533a2 Phil Davis
	} else {
2127 e0a45ce0 Erik Fonnesbeck
		@unlink("{$g['tmp_path']}/missing_interfaces");
2128 751533a2 Phil Davis
	}
2129 2b4d37de Ermal Lu?i
2130 857da904 Scott Ullrich
	return $do_assign;
2131 2b4d37de Ermal Lu?i
}
2132
2133 6e8f7b53 Ermal Lu?i
/* sync carp entries to other firewalls */
2134
function carp_sync_client() {
2135 e14d1c01 Ermal Lu?i
	global $g;
2136 0ae6daf8 Ermal
	send_event("filter sync");
2137 6e8f7b53 Ermal Lu?i
}
2138
2139 6dc88d53 Ermal Luci
/****f* util/isAjax
2140
 * NAME
2141
 *   isAjax - reports if the request is driven from prototype
2142
 * INPUTS
2143
 *   none
2144
 * RESULT
2145
 *   true/false
2146
 ******/
2147
function isAjax() {
2148 5bbd08e1 Warren Baker
	return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
2149 6dc88d53 Ermal Luci
}
2150
2151 dad2b40e Tim Allender
/****f* util/timeout
2152
 * NAME
2153
 *   timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space.
2154
 * INPUTS
2155
 *   optional, seconds to wait before timeout. Default 9 seconds.
2156
 * RESULT
2157
 *   returns 1 char of user input or null if no input.
2158
 ******/
2159
function timeout($timer = 9) {
2160 751533a2 Phil Davis
	while (!isset($key)) {
2161
		if ($timer >= 9) {
2162 6c07db48 Phil Davis
			echo chr(8) . chr(8) . ($timer == 9 ? chr(32) : null) . "{$timer}";
2163 751533a2 Phil Davis
		} else {
2164
			echo chr(8). "{$timer}";
2165
		}
2166 dad2b40e Tim Allender
		`/bin/stty -icanon min 0 time 25`;
2167
		$key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`);
2168
		`/bin/stty icanon`;
2169 751533a2 Phil Davis
		if ($key == '') {
2170 dad2b40e Tim Allender
			unset($key);
2171 751533a2 Phil Davis
		}
2172 dad2b40e Tim Allender
		$timer--;
2173 751533a2 Phil Davis
		if ($timer == 0) {
2174 dad2b40e Tim Allender
			break;
2175 751533a2 Phil Davis
		}
2176 dad2b40e Tim Allender
	}
2177 86a5e1a8 Renato Botelho
	return $key;
2178 dad2b40e Tim Allender
}
2179 6dc88d53 Ermal Luci
2180 fdf3af3f Scott Ullrich
/****f* util/msort
2181
 * NAME
2182
 *   msort - sort array
2183
 * INPUTS
2184
 *   $array to be sorted, field to sort by, direction of sort
2185
 * RESULT
2186
 *   returns newly sorted array
2187
 ******/
2188 6c07db48 Phil Davis
function msort($array, $id = "id", $sort_ascending = true) {
2189 4a8bc5a2 Scott Ullrich
	$temp_array = array();
2190 751533a2 Phil Davis
	while (count($array)>0) {
2191 4a8bc5a2 Scott Ullrich
		$lowest_id = 0;
2192 6c07db48 Phil Davis
		$index = 0;
2193 4a8bc5a2 Scott Ullrich
		foreach ($array as $item) {
2194
			if (isset($item[$id])) {
2195
				if ($array[$lowest_id][$id]) {
2196
					if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
2197
						$lowest_id = $index;
2198
					}
2199
				}
2200
			}
2201
			$index++;
2202
		}
2203
		$temp_array[] = $array[$lowest_id];
2204 086cf944 Phil Davis
		$array = array_merge(array_slice($array, 0, $lowest_id), array_slice($array, $lowest_id + 1));
2205 4a8bc5a2 Scott Ullrich
	}
2206
	if ($sort_ascending) {
2207
		return $temp_array;
2208
	} else {
2209 86a5e1a8 Renato Botelho
		return array_reverse($temp_array);
2210 4a8bc5a2 Scott Ullrich
	}
2211
}
2212
2213 5e9dd72a sullrich
/****f* util/is_URL
2214
 * NAME
2215
 *   is_URL
2216
 * INPUTS
2217
 *   string to check
2218
 * RESULT
2219
 *   Returns true if item is a URL
2220
 ******/
2221
function is_URL($url) {
2222
	$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
2223 751533a2 Phil Davis
	if ($match) {
2224 86a5e1a8 Renato Botelho
		return true;
2225 751533a2 Phil Davis
	}
2226 5e9dd72a sullrich
	return false;
2227
}
2228
2229 ab94ba00 Ermal Lu?i
function is_file_included($file = "") {
2230
	$files = get_included_files();
2231 751533a2 Phil Davis
	if (in_array($file, $files)) {
2232 ab94ba00 Ermal Lu?i
		return true;
2233 751533a2 Phil Davis
	}
2234 86a5e1a8 Renato Botelho
2235 ab94ba00 Ermal Lu?i
	return false;
2236
}
2237
2238 f2cc3344 Renato Botelho
/*
2239
 * Replace a value on a deep associative array using regex
2240
 */
2241
function array_replace_values_recursive($data, $match, $replace) {
2242 751533a2 Phil Davis
	if (empty($data)) {
2243 f2cc3344 Renato Botelho
		return $data;
2244 751533a2 Phil Davis
	}
2245 f2cc3344 Renato Botelho
2246 751533a2 Phil Davis
	if (is_string($data)) {
2247 f2cc3344 Renato Botelho
		$data = preg_replace("/{$match}/", $replace, $data);
2248 751533a2 Phil Davis
	} else if (is_array($data)) {
2249
		foreach ($data as $k => $v) {
2250 f2cc3344 Renato Botelho
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
2251 751533a2 Phil Davis
		}
2252
	}
2253 f2cc3344 Renato Botelho
2254
	return $data;
2255
}
2256
2257 0d90fcaf jim-p
/*
2258
	This function was borrowed from a comment on PHP.net at the following URL:
2259
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
2260
 */
2261 5bbd08e1 Warren Baker
function array_merge_recursive_unique($array0, $array1) {
2262
2263
	$arrays = func_get_args();
2264
	$remains = $arrays;
2265 0d90fcaf jim-p
2266 5bbd08e1 Warren Baker
	// We walk through each arrays and put value in the results (without
2267
	// considering previous value).
2268
	$result = array();
2269 0d90fcaf jim-p
2270 5bbd08e1 Warren Baker
	// loop available array
2271 751533a2 Phil Davis
	foreach ($arrays as $array) {
2272 0d90fcaf jim-p
2273 5bbd08e1 Warren Baker
		// The first remaining array is $array. We are processing it. So
2274 751533a2 Phil Davis
		// we remove it from remaining arrays.
2275 86a5e1a8 Renato Botelho
		array_shift($remains);
2276 0d90fcaf jim-p
2277 5bbd08e1 Warren Baker
		// We don't care non array param, like array_merge since PHP 5.0.
2278 751533a2 Phil Davis
		if (is_array($array)) {
2279 5bbd08e1 Warren Baker
			// Loop values
2280 751533a2 Phil Davis
			foreach ($array as $key => $value) {
2281
				if (is_array($value)) {
2282 5bbd08e1 Warren Baker
					// we gather all remaining arrays that have such key available
2283
					$args = array();
2284 751533a2 Phil Davis
					foreach ($remains as $remain) {
2285
						if (array_key_exists($key, $remain)) {
2286 5bbd08e1 Warren Baker
							array_push($args, $remain[$key]);
2287
						}
2288
					}
2289
2290 751533a2 Phil Davis
					if (count($args) > 2) {
2291 5bbd08e1 Warren Baker
						// put the recursion
2292
						$result[$key] = call_user_func_array(__FUNCTION__, $args);
2293
					} else {
2294 751533a2 Phil Davis
						foreach ($value as $vkey => $vval) {
2295 5bbd08e1 Warren Baker
							$result[$key][$vkey] = $vval;
2296
						}
2297
					}
2298
				} else {
2299
					// simply put the value
2300
					$result[$key] = $value;
2301
				}
2302
			}
2303
		}
2304
	}
2305
	return $result;
2306 0d90fcaf jim-p
}
2307
2308 f898c1a9 jim-p
2309 9a456170 Darren Embry
/*
2310
 * converts a string like "a,b,c,d"
2311
 * into an array like array("a" => "b", "c" => "d")
2312
 */
2313
function explode_assoc($delimiter, $string) {
2314
	$array = explode($delimiter, $string);
2315
	$result = array();
2316
	$numkeys = floor(count($array) / 2);
2317
	for ($i = 0; $i < $numkeys; $i += 1) {
2318
		$result[$array[$i * 2]] = $array[$i * 2 + 1];
2319
	}
2320
	return $result;
2321
}
2322
2323 94bd7fb3 Renato Botelho
/* Try to change a static route, if it doesn't exist, add it */
2324
function route_add_or_change($args) {
2325
	global $config;
2326
2327
	if (empty($args)) {
2328
		return false;
2329
	}
2330
2331
	/* First, try to add it */
2332
	$_gb = exec(escapeshellcmd("/sbin/route add " . $args), $output, $rc);
2333
2334
	if (isset($config['system']['route-debug'])) {
2335
		$mt = microtime();
2336
		log_error("ROUTING debug: $mt - ADD RC={$rc} - $args");
2337
	}
2338
2339
	if ($rc != 0) {
2340
		/* If it fails, try to change it */
2341
		$_gb = exec(escapeshellcmd("/sbin/route change " . $args),
2342
		    $output, $rc);
2343
2344
		if (isset($config['system']['route-debug'])) {
2345
			$mt = microtime();
2346
			log_error("ROUTING debug: $mt - CHG RC={$rc} - $args");
2347
		}
2348
	}
2349
2350
	return ($rc == 0);
2351
}
2352
2353 cf08b49e Phil Davis
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false, $returnenabledroutesonly = false) {
2354 1901463c Renato Botelho
	global $config, $aliastable;
2355 f898c1a9 jim-p
2356
	/* Bail if there are no routes, but return an array always so callers don't have to check. */
2357 751533a2 Phil Davis
	if (!is_array($config['staticroutes']['route'])) {
2358 f898c1a9 jim-p
		return array();
2359 751533a2 Phil Davis
	}
2360 f898c1a9 jim-p
2361 bcab1b07 Ermal
	$allstaticroutes = array();
2362
	$allsubnets = array();
2363 f898c1a9 jim-p
	/* Loop through routes and expand aliases as we find them. */
2364
	foreach ($config['staticroutes']['route'] as $route) {
2365 cf08b49e Phil Davis
		if ($returnenabledroutesonly && isset($route['disabled'])) {
2366
			continue;
2367
		}
2368
2369 f898c1a9 jim-p
		if (is_alias($route['network'])) {
2370 751533a2 Phil Davis
			if (!isset($aliastable[$route['network']])) {
2371 1901463c Renato Botelho
				continue;
2372 751533a2 Phil Davis
			}
2373 1901463c Renato Botelho
2374
			$subnets = preg_split('/\s+/', $aliastable[$route['network']]);
2375 f898c1a9 jim-p
			foreach ($subnets as $net) {
2376 bcab1b07 Ermal
				if (!is_subnet($net)) {
2377 751533a2 Phil Davis
					if (is_ipaddrv4($net)) {
2378 bcab1b07 Ermal
						$net .= "/32";
2379 751533a2 Phil Davis
					} else if (is_ipaddrv6($net)) {
2380 bcab1b07 Ermal
						$net .= "/128";
2381 751533a2 Phil Davis
					} else if ($returnhostnames === false || !is_fqdn($net)) {
2382 bcab1b07 Ermal
						continue;
2383 751533a2 Phil Davis
					}
2384 bcab1b07 Ermal
				}
2385 f898c1a9 jim-p
				$temproute = $route;
2386
				$temproute['network'] = $net;
2387
				$allstaticroutes[] = $temproute;
2388
				$allsubnets[] = $net;
2389
			}
2390
		} elseif (is_subnet($route['network'])) {
2391
			$allstaticroutes[] = $route;
2392
			$allsubnets[] = $route['network'];
2393
		}
2394
	}
2395 751533a2 Phil Davis
	if ($returnsubnetsonly) {
2396 f898c1a9 jim-p
		return $allsubnets;
2397 751533a2 Phil Davis
	} else {
2398 f898c1a9 jim-p
		return $allstaticroutes;
2399 751533a2 Phil Davis
	}
2400 f898c1a9 jim-p
}
2401 a0539faa Darren Embry
2402
/****f* util/get_alias_list
2403
 * NAME
2404
 *   get_alias_list - Provide a list of aliases.
2405
 * INPUTS
2406
 *   $type          - Optional, can be a string or array specifying what type(s) of aliases you need.
2407
 * RESULT
2408
 *   Array containing list of aliases.
2409
 *   If $type is unspecified, all aliases are returned.
2410
 *   If $type is a string, all aliases of the type specified in $type are returned.
2411
 *   If $type is an array, all aliases of any type specified in any element of $type are returned.
2412
 */
2413
function get_alias_list($type = null) {
2414
	global $config;
2415
	$result = array();
2416
	if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) {
2417
		foreach ($config['aliases']['alias'] as $alias) {
2418
			if ($type === null) {
2419
				$result[] = $alias['name'];
2420 751533a2 Phil Davis
			} else if (is_array($type)) {
2421 a0539faa Darren Embry
				if (in_array($alias['type'], $type)) {
2422
					$result[] = $alias['name'];
2423
				}
2424 751533a2 Phil Davis
			} else if ($type === $alias['type']) {
2425 a0539faa Darren Embry
				$result[] = $alias['name'];
2426
			}
2427
		}
2428 86a5e1a8 Renato Botelho
	}
2429 a0539faa Darren Embry
	return $result;
2430
}
2431
2432 4dfd930e Darren Embry
/* returns an array consisting of every element of $haystack that is not equal to $needle. */
2433
function array_exclude($needle, $haystack) {
2434
	$result = array();
2435
	if (is_array($haystack)) {
2436
		foreach ($haystack as $thing) {
2437
			if ($needle !== $thing) {
2438
				$result[] = $thing;
2439
			}
2440
		}
2441
	}
2442
	return $result;
2443
}
2444
2445 77a341a4 Renato Botelho
/* Define what is preferred, IPv4 or IPv6 */
2446
function prefer_ipv4_or_ipv6() {
2447
	global $config;
2448
2449 751533a2 Phil Davis
	if (isset($config['system']['prefer_ipv4'])) {
2450 77a341a4 Renato Botelho
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv4");
2451 751533a2 Phil Davis
	} else {
2452 77a341a4 Renato Botelho
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv6");
2453 751533a2 Phil Davis
	}
2454 77a341a4 Renato Botelho
}
2455
2456 111bea0d Renato Botelho
/* Redirect to page passing parameters via POST */
2457
function post_redirect($page, $params) {
2458 751533a2 Phil Davis
	if (!is_array($params)) {
2459 111bea0d Renato Botelho
		return;
2460 751533a2 Phil Davis
	}
2461 111bea0d Renato Botelho
2462
	print "<html><body><form action=\"{$page}\" name=\"formredir\" method=\"post\">\n";
2463
	foreach ($params as $key => $value) {
2464
		print "<input type=\"hidden\" name=\"{$key}\" value=\"{$value}\" />\n";
2465
	}
2466 8fd9052f Colin Fleming
	print "</form>\n";
2467
	print "<script type=\"text/javascript\">\n";
2468
	print "//<![CDATA[\n";
2469
	print "document.formredir.submit();\n";
2470
	print "//]]>\n";
2471
	print "</script>\n";
2472 111bea0d Renato Botelho
	print "</body></html>\n";
2473
}
2474
2475 ea20169a jim-p
/* Locate disks that can be queried for S.M.A.R.T. data. */
2476
function get_smart_drive_list() {
2477
	$disk_list = explode(" ", get_single_sysctl("kern.disks"));
2478
	foreach ($disk_list as $id => $disk) {
2479
		// We only want certain kinds of disks for S.M.A.R.T.
2480 a68c6785 Phil Davis
		// 1 is a match, 0 is no match, False is any problem processing the regex
2481
		if (preg_match("/^(ad|da|ada).*[0-9]{1,2}$/", $disk) !== 1) {
2482 ea20169a jim-p
			unset($disk_list[$id]);
2483
		}
2484
	}
2485
	sort($disk_list);
2486
	return $disk_list;
2487
}
2488
2489 77a8a7d6 Steve Beaver
// Validate a network address
2490
//	$addr: the address to validate
2491
//	$type: IPV4|IPV6|IPV4V6
2492 c393f1d1 Steve Beaver
//	$label: the label used by the GUI to display this value. Required to compose an error message
2493 77a8a7d6 Steve Beaver
//	$err_msg: pointer to the callers error message array so that error messages can be added to it here
2494
//	$alias: are aliases permitted for this address?
2495 24eb39e2 Phil Davis
// Returns:
2496 74999ad8 Phil Davis
//	IPV4 - if $addr is a valid IPv4 address
2497
//	IPV6 - if $addr is a valid IPv6 address
2498
//	ALIAS - if $alias=true and $addr is an alias
2499 24eb39e2 Phil Davis
//	false - otherwise
2500
2501 77a8a7d6 Steve Beaver
function validateipaddr(&$addr, $type, $label, &$err_msg, $alias=false) {
2502
	switch ($type) {
2503
		case IPV4:
2504
			if (is_ipaddrv4($addr)) {
2505 dc938839 Phil Davis
				return IPV4;
2506 77a8a7d6 Steve Beaver
			} else if ($alias) {
2507
				if (is_alias($addr)) {
2508 dc938839 Phil Davis
					return ALIAS;
2509 77a8a7d6 Steve Beaver
				} else {
2510 bb9747b2 Phil Davis
					$err_msg[] = sprintf(gettext("%s must be a valid IPv4 address or alias."), $label);
2511 77a8a7d6 Steve Beaver
					return false;
2512
				}
2513
			} else {
2514 bb9747b2 Phil Davis
				$err_msg[] = sprintf(gettext("%s must be a valid IPv4 address."), $label);
2515 77a8a7d6 Steve Beaver
				return false;
2516
			}
2517
		break;
2518
		case IPV6:
2519
			if (is_ipaddrv6($addr)) {
2520
				$addr = strtolower($addr);
2521 dc938839 Phil Davis
				return IPV6;
2522 aa2b8133 Phil Davis
			} else if ($alias) {
2523 77a8a7d6 Steve Beaver
				if (is_alias($addr)) {
2524 dc938839 Phil Davis
					return ALIAS;
2525 77a8a7d6 Steve Beaver
				} else {
2526 bb9747b2 Phil Davis
					$err_msg[] = sprintf(gettext("%s must be a valid IPv6 address or alias."), $label);
2527 77a8a7d6 Steve Beaver
					return false;
2528
				}
2529
			} else {
2530 bb9747b2 Phil Davis
				$err_msg[] = sprintf(gettext("%s must be a valid IPv6 address."), $label);
2531 77a8a7d6 Steve Beaver
				return false;
2532
			}
2533
		break;
2534
		case IPV4V6:
2535
			if (is_ipaddrv6($addr)) {
2536
				$addr = strtolower($addr);
2537 dc938839 Phil Davis
				return IPV6;
2538 77a8a7d6 Steve Beaver
			} else if (is_ipaddrv4($addr)) {
2539 dc938839 Phil Davis
				return IPV4;
2540 aa2b8133 Phil Davis
			} else if ($alias) {
2541 77a8a7d6 Steve Beaver
				if (is_alias($addr)) {
2542 dc938839 Phil Davis
					return ALIAS;
2543 77a8a7d6 Steve Beaver
				} else {
2544 bb9747b2 Phil Davis
					$err_msg[] = sprintf(gettext("%s must be a valid IPv4 or IPv6 address or alias."), $label);
2545 77a8a7d6 Steve Beaver
					return false;
2546
				}
2547
			} else {
2548 bb9747b2 Phil Davis
				$err_msg[] = sprintf(gettext("%s must be a valid IPv4 or IPv6 address."), $label);
2549 77a8a7d6 Steve Beaver
				return false;
2550
			}
2551
		break;
2552
	}
2553
2554
	return false;
2555
}
2556 7be23d53 marjohn56
2557 f4bbec8b Phil Davis
/* format a string to look (more) like the expected DUID format:
2558
 * 1) Replace any "-" with ":"
2559
 * 2) If the user inputs 14 components, then add the expected "0e:00:" to the front.
2560
 *    This is convenience, because the actual DUID (which is reported in logs) is the last 14 components.
2561
 * 3) If any components are input with just a single char (hex digit hopefully), put a "0" in front.
2562
 *
2563
 * The final result should be closer to:
2564
 *
2565
 * "0e:00:00:01:00:01:nn:nn:nn:nn:nn:nn:nn:nn:nn:nn"
2566
 *
2567
 * This function does not validate the input. is_duid() will do validation.
2568
*/
2569
function format_duid($dhcp6duid) {
2570 febfd592 Phil Davis
	$values = explode(":", strtolower(str_replace("-", ":", $dhcp6duid)));
2571 f4bbec8b Phil Davis
	if (count($values) == 14) {
2572 febfd592 Phil Davis
		array_unshift($values, "0e", "00");
2573 f4bbec8b Phil Davis
	}
2574 fd2e503a Phil Davis
2575
	array_walk($values, function(&$value) {
2576 4f3fc80d Renato Botelho
		$value = str_pad($value, 2, '0', STR_PAD_LEFT);
2577 fd2e503a Phil Davis
	});
2578
2579 febfd592 Phil Davis
	return implode(":", $values);
2580 f4bbec8b Phil Davis
}
2581
2582
/* returns true if $dhcp6duid is a valid duid entry */
2583 7be23d53 marjohn56
function is_duid($dhcp6duid) {
2584
	$values = explode(":", $dhcp6duid);
2585
	if (count($values) != 16 || strlen($dhcp6duid) != 47) {
2586
		return false;
2587
	}
2588
	for ($i = 0; $i < 16; $i++) {
2589
		if (ctype_xdigit($values[$i]) == false)
2590
			return false;
2591
		if (hexdec($values[$i]) < 0 || hexdec($values[$i]) > 255)
2592
			return false;
2593
	}
2594
	return true;
2595
}
2596
2597
/* Write the DHCP6 DUID file */
2598
function write_dhcp6_duid($duidstring) {
2599
	// Create the hex array from the dhcp6duid config entry and write to file
2600
	global $g;
2601 4f3fc80d Renato Botelho
2602
	if(!is_duid($duidstring)) {
2603 7be23d53 marjohn56
		log_error(gettext("Error: attempting to write DUID file - Invalid DUID detected"));
2604
		return false;
2605
	}
2606
	$temp = str_replace(":","",$duidstring);
2607
	$duid_binstring = pack("H*",$temp);
2608
	if ($fd = fopen("{$g['vardb_path']}/dhcp6c_duid", "wb")) {
2609
		fwrite($fd, $duid_binstring);
2610
		fclose($fd);
2611
		return true;
2612
	}
2613
	log_error(gettext("Error: attempting to write DUID file - File write error"));
2614
	return false;
2615
}
2616 2acedbbf marjohn56
2617 9e08a2bd marjohn56
/* returns duid string from 'vardb_path']}/dhcp6c_duid' */
2618 4f3fc80d Renato Botelho
function get_duid_from_file() {
2619 9e08a2bd marjohn56
	global $g;
2620 4f3fc80d Renato Botelho
2621 2acedbbf marjohn56
	$duid_ASCII = "";
2622 9e08a2bd marjohn56
	$count = 0;
2623 a271ed3d Renato Botelho
2624
	if (file_exists("{$g['vardb_path']}/dhcp6c_duid") &&
2625
	    ($fd = fopen("{$g['vardb_path']}/dhcp6c_duid", "r"))) {
2626 9e08a2bd marjohn56
		if(filesize("{$g['vardb_path']}/dhcp6c_duid")==16) {
2627 4f3fc80d Renato Botelho
			$buffer = fread($fd,16);
2628 2acedbbf marjohn56
			while($count < 16) {
2629
				$duid_ASCII .= bin2hex($buffer[$count]);
2630 9e08a2bd marjohn56
				$count++;
2631
				if($count < 16) {
2632
					$duid_ASCII .= ":";
2633
				}
2634
			}
2635
		}
2636
		fclose($fd);
2637
	}
2638
	//if no file or error with read then the string returns blanked DUID string
2639 2acedbbf marjohn56
	if(!is_duid($duid_ASCII)) {
2640 9e08a2bd marjohn56
		return "--:--:--:--:--:--:--:--:--:--:--:--:--:--:--:--";
2641
	}
2642 4f3fc80d Renato Botelho
	return($duid_ASCII);
2643 9e08a2bd marjohn56
}
2644 eb295a1b Ermal
?>