Project

General

Profile

Download (98.3 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 38809d47 Renato Botelho do Couto
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8 8f585441 Luiz Souza
 * Copyright (c) 2014-2021 Rubicon Communications, LLC (Netgate)
9 ac24dc24 Renato Botelho
 * All rights reserved.
10
 *
11
 * originally part of m0n0wall (http://m0n0.ch/wall)
12 c5d81585 Renato Botelho
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
13 ac24dc24 Renato Botelho
 * All rights reserved.
14
 *
15 b12ea3fb Renato Botelho
 * Licensed under the Apache License, Version 2.0 (the "License");
16
 * you may not use this file except in compliance with the License.
17
 * You may obtain a copy of the License at
18 ac24dc24 Renato Botelho
 *
19 b12ea3fb Renato Botelho
 * http://www.apache.org/licenses/LICENSE-2.0
20 ac24dc24 Renato Botelho
 *
21 b12ea3fb Renato Botelho
 * Unless required by applicable law or agreed to in writing, software
22
 * distributed under the License is distributed on an "AS IS" BASIS,
23
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
 * See the License for the specific language governing permissions and
25
 * limitations under the License.
26 995df6c3 Stephen Beaver
 */
27 523855b0 Scott Ullrich
28 820562e8 NewEraCracker
define('VIP_ALL', 1);
29
define('VIP_CARP', 2);
30
define('VIP_IPALIAS', 3);
31 ce94deb0 Luiz Otavio O Souza
32 5b237745 Scott Ullrich
/* kill a process by pid file */
33 66491555 PiBa-NL
function killbypid($pidfile, $waitfor = 0) {
34
	return sigkillbypid($pidfile, "TERM", $waitfor);
35 5b237745 Scott Ullrich
}
36
37 c4594e36 Phil Davis
function isvalidpid($pidfile) {
38 0e604b3a Ermal
	$output = "";
39 c4594e36 Phil Davis
	if (file_exists($pidfile)) {
40 b885c8cf Renato Botelho
		exec("/bin/pgrep -qnF {$pidfile} 2>/dev/null", $output, $retval);
41 c4594e36 Phil Davis
		return (intval($retval) == 0);
42
	}
43
	return false;
44 53aca1fd Scott Ullrich
}
45
46 6dc3a5c2 Ermal Lu?i
function is_process_running($process) {
47 01d4b621 Ermal
	$output = "";
48 050e18cf Viktor G
	if (!empty($process)) {
49
		exec("/bin/pgrep -anx " . escapeshellarg($process), $output, $retval);
50
		return (intval($retval) == 0);
51
	}
52
	return false;
53 6dc3a5c2 Ermal Lu?i
}
54
55 53aca1fd Scott Ullrich
function isvalidproc($proc) {
56 ba8495f0 Ermal
	return is_process_running($proc);
57 53aca1fd Scott Ullrich
}
58
59 66491555 PiBa-NL
/* sigkill a process by pid file, and wait for it to terminate or remove the .pid file for $waitfor seconds */
60 53aca1fd Scott Ullrich
/* return 1 for success and 0 for a failure */
61 66491555 PiBa-NL
function sigkillbypid($pidfile, $sig, $waitfor = 0) {
62 0b8b5069 Renato Botelho
	if (isvalidpid($pidfile)) {
63 66491555 PiBa-NL
		$result = mwexec("/bin/pkill " . escapeshellarg("-{$sig}") .
64 0b8b5069 Renato Botelho
		    " -F {$pidfile}", true);
65 66491555 PiBa-NL
		$waitcounter = $waitfor * 10;
66
		while(isvalidpid($pidfile) && $waitcounter > 0) {
67
			$waitcounter = $waitcounter - 1;
68
			usleep(100000);
69
		}
70
		return $result;
71 751533a2 Phil Davis
	}
72 ba8495f0 Ermal
73 53aca1fd Scott Ullrich
	return 0;
74
}
75
76
/* kill a process by name */
77
function sigkillbyname($procname, $sig) {
78 751533a2 Phil Davis
	if (isvalidproc($procname)) {
79 873c1701 Renato Botelho
		return mwexec("/usr/bin/killall " . escapeshellarg("-{$sig}") . " " . escapeshellarg($procname), true);
80 751533a2 Phil Davis
	}
81 5b237745 Scott Ullrich
}
82
83
/* kill a process by name */
84
function killbyname($procname) {
85 751533a2 Phil Davis
	if (isvalidproc($procname)) {
86 53aca1fd Scott Ullrich
		mwexec("/usr/bin/killall " . escapeshellarg($procname));
87 751533a2 Phil Davis
	}
88 5b237745 Scott Ullrich
}
89
90 a368a026 Ermal Lu?i
function is_subsystem_dirty($subsystem = "") {
91
	global $g;
92
93 751533a2 Phil Davis
	if ($subsystem == "") {
94 a368a026 Ermal Lu?i
		return false;
95 751533a2 Phil Davis
	}
96 a368a026 Ermal Lu?i
97 751533a2 Phil Davis
	if (file_exists("{$g['varrun_path']}/{$subsystem}.dirty")) {
98 a368a026 Ermal Lu?i
		return true;
99 751533a2 Phil Davis
	}
100 a368a026 Ermal Lu?i
101
	return false;
102
}
103
104
function mark_subsystem_dirty($subsystem = "") {
105
	global $g;
106
107 751533a2 Phil Davis
	if (!file_put_contents("{$g['varrun_path']}/{$subsystem}.dirty", "DIRTY")) {
108 fd7b47b6 Renato Botelho
		log_error(sprintf(gettext("WARNING: Could not mark subsystem: %s dirty"), $subsystem));
109 751533a2 Phil Davis
	}
110 a368a026 Ermal Lu?i
}
111
112
function clear_subsystem_dirty($subsystem = "") {
113
	global $g;
114
115
	@unlink("{$g['varrun_path']}/{$subsystem}.dirty");
116
}
117
118 0027de0a Ermal Lu?i
/* lock configuration file */
119 b6c34bfc Ermal
function lock($lock, $op = LOCK_SH) {
120 42ea8f9a Renato Botelho
	global $g;
121 751533a2 Phil Davis
	if (!$lock) {
122 530e4707 NOYB
		die(gettext("WARNING: A name must be given as parameter to lock() function."));
123 751533a2 Phil Davis
	}
124 6bee76d5 Ermal
	if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) {
125 9e7ef1a5 Scott Ullrich
		@touch("{$g['tmp_path']}/{$lock}.lock");
126 6bee76d5 Ermal
		@chmod("{$g['tmp_path']}/{$lock}.lock", 0666);
127
	}
128 b6c34bfc Ermal
	if ($fp = fopen("{$g['tmp_path']}/{$lock}.lock", "w")) {
129 751533a2 Phil Davis
		if (flock($fp, $op)) {
130 9e7ef1a5 Scott Ullrich
			return $fp;
131 751533a2 Phil Davis
		} else {
132 b6c34bfc Ermal
			fclose($fp);
133 751533a2 Phil Davis
		}
134 9e7ef1a5 Scott Ullrich
	}
135 0027de0a Ermal Lu?i
}
136
137 8171a2c2 Ermal
function try_lock($lock, $timeout = 5) {
138 42ea8f9a Renato Botelho
	global $g;
139 751533a2 Phil Davis
	if (!$lock) {
140 530e4707 NOYB
		die(gettext("WARNING: A name must be given as parameter to try_lock() function."));
141 751533a2 Phil Davis
	}
142 8171a2c2 Ermal
	if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) {
143
		@touch("{$g['tmp_path']}/{$lock}.lock");
144
		@chmod("{$g['tmp_path']}/{$lock}.lock", 0666);
145
	}
146
	if ($fp = fopen("{$g['tmp_path']}/{$lock}.lock", "w")) {
147
		$trycounter = 0;
148 751533a2 Phil Davis
		while (!flock($fp, LOCK_EX | LOCK_NB)) {
149 8171a2c2 Ermal
			if ($trycounter >= $timeout) {
150
				fclose($fp);
151
				return NULL;
152
			}
153
			sleep(1);
154
			$trycounter++;
155
		}
156
157
		return $fp;
158
	}
159
160
	return NULL;
161
}
162
163 0027de0a Ermal Lu?i
/* unlock configuration file */
164
function unlock($cfglckkey = 0) {
165 42ea8f9a Renato Botelho
	global $g;
166 9e7ef1a5 Scott Ullrich
	flock($cfglckkey, LOCK_UN);
167 cb6fd90b Ermal Lu?i
	fclose($cfglckkey);
168 9e7ef1a5 Scott Ullrich
	return;
169 0027de0a Ermal Lu?i
}
170
171 8171a2c2 Ermal
/* unlock forcefully configuration file */
172
function unlock_force($lock) {
173
	global $g;
174
175
	@unlink("{$g['tmp_path']}/{$lock}.lock");
176
}
177
178 0ae6daf8 Ermal
function send_event($cmd) {
179
	global $g;
180
181 751533a2 Phil Davis
	if (!isset($g['event_address'])) {
182 1015b3a9 Warren Baker
		$g['event_address'] = "unix:///var/run/check_reload_status";
183 751533a2 Phil Davis
	}
184 86a5e1a8 Renato Botelho
185 838feb14 Ermal
	$try = 0;
186
	while ($try < 3) {
187
		$fd = @fsockopen($g['event_address']);
188
		if ($fd) {
189
			fwrite($fd, $cmd);
190
			$resp = fread($fd, 4096);
191 751533a2 Phil Davis
			if ($resp != "OK\n") {
192 838feb14 Ermal
				log_error("send_event: sent {$cmd} got {$resp}");
193 751533a2 Phil Davis
			}
194 838feb14 Ermal
			fclose($fd);
195
			$try = 3;
196 751533a2 Phil Davis
		} else if (!is_process_running("check_reload_status")) {
197 838feb14 Ermal
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
198 751533a2 Phil Davis
		}
199 838feb14 Ermal
		$try++;
200 0ae6daf8 Ermal
	}
201
}
202
203
function send_multiple_events($cmds) {
204 1015b3a9 Warren Baker
	global $g;
205 0ae6daf8 Ermal
206 751533a2 Phil Davis
	if (!isset($g['event_address'])) {
207 1015b3a9 Warren Baker
		$g['event_address'] = "unix:///var/run/check_reload_status";
208 751533a2 Phil Davis
	}
209 86a5e1a8 Renato Botelho
210 751533a2 Phil Davis
	if (!is_array($cmds)) {
211 0ae6daf8 Ermal
		return;
212 751533a2 Phil Davis
	}
213 6e1f456f Ermal
214 915089b7 Ermal
	while ($try < 3) {
215
		$fd = @fsockopen($g['event_address']);
216
		if ($fd) {
217
			foreach ($cmds as $cmd) {
218
				fwrite($fd, $cmd);
219
				$resp = fread($fd, 4096);
220 751533a2 Phil Davis
				if ($resp != "OK\n") {
221 915089b7 Ermal
					log_error("send_event: sent {$cmd} got {$resp}");
222 751533a2 Phil Davis
				}
223 915089b7 Ermal
			}
224
			fclose($fd);
225
			$try = 3;
226 751533a2 Phil Davis
		} else if (!is_process_running("check_reload_status")) {
227 915089b7 Ermal
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
228 751533a2 Phil Davis
		}
229 915089b7 Ermal
		$try++;
230
	}
231 0ae6daf8 Ermal
}
232
233 1ab56363 Ermal Lu?i
function is_module_loaded($module_name) {
234 302c005e Ermal
	$module_name = str_replace(".ko", "", $module_name);
235
	$running = 0;
236 ec25f18a Renato Botelho
	$_gb = exec("/sbin/kldstat -qn {$module_name} 2>&1", $_gb, $running);
237 751533a2 Phil Davis
	if (intval($running) == 0) {
238 1ab56363 Ermal Lu?i
		return true;
239 751533a2 Phil Davis
	} else {
240 1ab56363 Ermal Lu?i
		return false;
241 751533a2 Phil Davis
	}
242 1ab56363 Ermal Lu?i
}
243
244 4caa9574 stilez
/* validate non-negative numeric string, or equivalent numeric variable */
245
function is_numericint($arg) {
246 751533a2 Phil Davis
	return (((is_int($arg) && $arg >= 0) || (is_string($arg) && strlen($arg) > 0 && ctype_digit($arg))) ? true : false);
247 4caa9574 stilez
}
248
249 751533a2 Phil Davis
/* Generate the (human readable) ipv4 or ipv6 subnet address (i.e., netmask, or subnet start IP)
250 e89d2995 stilez
   given an (human readable) ipv4 or ipv6 host address and subnet bit count */
251 5b237745 Scott Ullrich
function gen_subnet($ipaddr, $bits) {
252 751533a2 Phil Davis
	if (($sn = gen_subnetv6($ipaddr, $bits)) == '') {
253 e89d2995 stilez
		$sn = gen_subnetv4($ipaddr, $bits);  // try to avoid rechecking IPv4/v6
254 751533a2 Phil Davis
	}
255 e89d2995 stilez
	return $sn;
256
}
257 3bad4691 Renato Botelho
258 e89d2995 stilez
/* same as gen_subnet() but accepts IPv4 only */
259
function gen_subnetv4($ipaddr, $bits) {
260
	if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) {
261 751533a2 Phil Davis
		if ($bits == 0) {
262 e89d2995 stilez
			return '0.0.0.0';  // avoids <<32
263 751533a2 Phil Davis
		}
264 e89d2995 stilez
		return long2ip(ip2long($ipaddr) & ((0xFFFFFFFF << (32 - $bits)) & 0xFFFFFFFF));
265
	}
266
	return "";
267 22b5abac Seth Mos
}
268
269 e89d2995 stilez
/* same as gen_subnet() but accepts IPv6 only */
270 22b5abac Seth Mos
function gen_subnetv6($ipaddr, $bits) {
271 751533a2 Phil Davis
	if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) {
272 587995fb Phil Davis
		return text_to_compressed_ip6(Net_IPv6::getNetmask($ipaddr, $bits));
273 751533a2 Phil Davis
	}
274 e89d2995 stilez
	return "";
275 5b237745 Scott Ullrich
}
276
277 3bad4691 Renato Botelho
/* Generate the (human readable) ipv4 or ipv6 subnet end address (i.e., highest address, end IP, or IPv4 broadcast address)
278 e89d2995 stilez
   given an (human readable) ipv4 or ipv6 host address and subnet bit count. */
279 5b237745 Scott Ullrich
function gen_subnet_max($ipaddr, $bits) {
280 751533a2 Phil Davis
	if (($sn = gen_subnetv6_max($ipaddr, $bits)) == '') {
281 e89d2995 stilez
		$sn = gen_subnetv4_max($ipaddr, $bits);  // try to avoid rechecking IPv4/v6
282 751533a2 Phil Davis
	}
283 e89d2995 stilez
	return $sn;
284
}
285 98bbf05a Scott Ullrich
286 e89d2995 stilez
/* same as gen_subnet_max() but validates IPv4 only */
287
function gen_subnetv4_max($ipaddr, $bits) {
288
	if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) {
289 751533a2 Phil Davis
		if ($bits == 32) {
290 e89d2995 stilez
			return $ipaddr;
291 751533a2 Phil Davis
		}
292 c18ba6bf Phil Davis
		return long2ip32(ip2long($ipaddr) | (~gen_subnet_mask_long($bits) & 0xFFFFFFFF));
293 e89d2995 stilez
	}
294
	return "";
295 5b237745 Scott Ullrich
}
296
297 e89d2995 stilez
/* same as gen_subnet_max() but validates IPv6 only */
298 c75a8185 Seth Mos
function gen_subnetv6_max($ipaddr, $bits) {
299 e89d2995 stilez
	if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) {
300 de645734 Renato Botelho
		$endip_bin = substr(ip6_to_bin($ipaddr), 0, $bits) . str_repeat('1', 128 - $bits);
301
		return bin_to_compressed_ip6($endip_bin);
302 e89d2995 stilez
	}
303
	return "";
304 c75a8185 Seth Mos
}
305
306 5b237745 Scott Ullrich
/* returns a subnet mask (long given a bit count) */
307
function gen_subnet_mask_long($bits) {
308
	$sm = 0;
309
	for ($i = 0; $i < $bits; $i++) {
310
		$sm >>= 1;
311
		$sm |= 0x80000000;
312
	}
313
	return $sm;
314
}
315
316
/* same as above but returns a string */
317
function gen_subnet_mask($bits) {
318
	return long2ip(gen_subnet_mask_long($bits));
319
}
320
321 31b15180 jim-p
/* Convert a prefix length to an IPv6 address-like mask notation. Very rare but at least ntp needs it. See #4463 */
322
function gen_subnet_mask_v6($bits) {
323
	/* Binary representation of the prefix length */
324
	$bin = str_repeat('1', $bits);
325
	/* Pad right with zeroes to reach the full address length */
326
	$bin = str_pad($bin, 128, '0', STR_PAD_RIGHT);
327
	/* Convert back to an IPv6 address style notation */
328 de645734 Renato Botelho
	return bin_to_ip6($bin);
329 31b15180 jim-p
}
330
331 ce9dc198 stilez
/* Convert long int to IPv4 address
332 f8a6c824 Chris Buechler
   Returns '' if not valid IPv4 (including if any bits >32 are non-zero) */
333 96033063 Erik Fonnesbeck
function long2ip32($ip) {
334 f8a6c824 Chris Buechler
	return long2ip($ip & 0xFFFFFFFF);
335
}
336 96033063 Erik Fonnesbeck
337 ce9dc198 stilez
/* Convert IPv4 address to long int, truncated to 32-bits to avoid sign extension on 64-bit platforms.
338
   Returns '' if not valid IPv4. */
339 96033063 Erik Fonnesbeck
function ip2long32($ip) {
340 f8a6c824 Chris Buechler
	return (ip2long($ip) & 0xFFFFFFFF);
341 96033063 Erik Fonnesbeck
}
342
343 ce9dc198 stilez
/* Convert IPv4 address to unsigned long int.
344
   Returns '' if not valid IPv4. */
345 ecd1f2d9 jim-p
function ip2ulong($ip) {
346 f8a6c824 Chris Buechler
	return sprintf("%u", ip2long32($ip));
347 ecd1f2d9 jim-p
}
348
349 de645734 Renato Botelho
/*
350
 * Convert IPv6 address to binary
351
 *
352
 * Obtained from: pear-Net_IPv6
353
 */
354
function ip6_to_bin($ip) {
355
	$binstr = '';
356
357
	$ip = Net_IPv6::removeNetmaskSpec($ip);
358
	$ip = Net_IPv6::Uncompress($ip);
359
360
	$parts = explode(':', $ip);
361
362
	foreach ( $parts as $v ) {
363
364
		$str     = base_convert($v, 16, 2);
365
		$binstr .= str_pad($str, 16, '0', STR_PAD_LEFT);
366
367
	}
368
369
	return $binstr;
370
}
371
372
/*
373
 * Convert IPv6 binary to uncompressed address
374
 *
375
 * Obtained from: pear-Net_IPv6
376
 */
377
function bin_to_ip6($bin) {
378
	$ip = "";
379
380
	if (strlen($bin) < 128) {
381
		$bin = str_pad($bin, 128, '0', STR_PAD_LEFT);
382
	}
383
384
	$parts = str_split($bin, "16");
385
386
	foreach ( $parts as $v ) {
387
		$str = base_convert($v, 2, 16);
388
		$ip .= $str.":";
389
	}
390
391
	$ip = substr($ip, 0, -1);
392
393
	return $ip;
394
}
395
396
/*
397
 * Convert IPv6 binary to compressed address
398
 */
399
function bin_to_compressed_ip6($bin) {
400 587995fb Phil Davis
	return text_to_compressed_ip6(bin_to_ip6($bin));
401
}
402
403
/*
404
 * Convert textual IPv6 address string to compressed address
405
 */
406
function text_to_compressed_ip6($text) {
407
	// Force re-compression by passing parameter 2 (force) true.
408
	// This ensures that supposedly-compressed formats are uncompressed
409
	// first then re-compressed into strictly correct form.
410
	// e.g. 2001:0:0:4:0:0:0:1
411
	// 2001::4:0:0:0:1 is a strictly-incorrect compression,
412
	// but maybe the user entered it like that.
413
	// The "force" parameter will ensure it is returned as:
414
	// 2001:0:0:4::1
415
	return Net_IPv6::compress($text, true);
416 de645734 Renato Botelho
}
417
418 ecd1f2d9 jim-p
/* Find out how many IPs are contained within a given IP range
419
 *  e.g. 192.168.0.0 to 192.168.0.255 returns 256
420
 */
421 bb67ac32 Phil Davis
function ip_range_size_v4($startip, $endip) {
422
	if (is_ipaddrv4($startip) && is_ipaddrv4($endip)) {
423 ecd1f2d9 jim-p
		// Operate as unsigned long because otherwise it wouldn't work
424
		//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
425
		return abs(ip2ulong($startip) - ip2ulong($endip)) + 1;
426
	}
427
	return -1;
428
}
429
430
/* Find the smallest possible subnet mask which can contain a given number of IPs
431
 *  e.g. 512 IPs can fit in a /23, but 513 IPs need a /22
432
 */
433 bb67ac32 Phil Davis
function find_smallest_cidr_v4($number) {
434 ecd1f2d9 jim-p
	$smallest = 1;
435
	for ($b=32; $b > 0; $b--) {
436 086cf944 Phil Davis
		$smallest = ($number <= pow(2, $b)) ? $b : $smallest;
437 ecd1f2d9 jim-p
	}
438
	return (32-$smallest);
439
}
440
441
/* Return the previous IP address before the given address */
442 aa181833 Phil Davis
function ip_before($ip, $offset = 1) {
443
	return long2ip32(ip2long($ip) - $offset);
444 ecd1f2d9 jim-p
}
445
446
/* Return the next IP address after the given address */
447 aa181833 Phil Davis
function ip_after($ip, $offset = 1) {
448
	return long2ip32(ip2long($ip) + $offset);
449 ecd1f2d9 jim-p
}
450
451
/* Return true if the first IP is 'before' the second */
452
function ip_less_than($ip1, $ip2) {
453
	// Compare as unsigned long because otherwise it wouldn't work when
454
	//   crossing over from 127.255.255.255 / 128.0.0.0 barrier
455
	return ip2ulong($ip1) < ip2ulong($ip2);
456
}
457
458
/* Return true if the first IP is 'after' the second */
459
function ip_greater_than($ip1, $ip2) {
460
	// Compare as unsigned long because otherwise it wouldn't work
461
	//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
462
	return ip2ulong($ip1) > ip2ulong($ip2);
463
}
464
465 b17ac4f7 stilez
/* compare two IP addresses */
466
function ipcmp($a, $b) {
467 0c3fff67 jim-p
	if (is_subnet($a)) {
468
		list($a, $amask) = explode('/', $a);
469
	}
470
	if (is_subnet($b)) {
471
		list($b, $bmask) = explode('/', $b);
472
	}
473 751533a2 Phil Davis
	if (ip_less_than($a, $b)) {
474 b17ac4f7 stilez
		return -1;
475 751533a2 Phil Davis
	} else if (ip_greater_than($a, $b)) {
476 b17ac4f7 stilez
		return 1;
477 751533a2 Phil Davis
	} else {
478 b17ac4f7 stilez
		return 0;
479 751533a2 Phil Davis
	}
480 b17ac4f7 stilez
}
481
482 bb67ac32 Phil Davis
/* Convert a range of IPv4 addresses to an array of individual addresses. */
483
/* Note: IPv6 ranges are not yet supported here. */
484
function ip_range_to_address_array($startip, $endip, $max_size = 5000) {
485
	if (!is_ipaddrv4($startip) || !is_ipaddrv4($endip)) {
486
		return false;
487
	}
488
489
	if (ip_greater_than($startip, $endip)) {
490
		// Swap start and end so we can process sensibly.
491
		$temp = $startip;
492
		$startip = $endip;
493
		$endip = $temp;
494
	}
495
496 751533a2 Phil Davis
	if (ip_range_size_v4($startip, $endip) > $max_size) {
497 bb67ac32 Phil Davis
		return false;
498 751533a2 Phil Davis
	}
499
500 bb67ac32 Phil Davis
	// Container for IP addresses within this range.
501
	$rangeaddresses = array();
502
	$end_int = ip2ulong($endip);
503
	for ($ip_int = ip2ulong($startip); $ip_int <= $end_int; $ip_int++) {
504
		$rangeaddresses[] = long2ip($ip_int);
505
	}
506
507
	return $rangeaddresses;
508
}
509
510 4f3fc80d Renato Botelho
/*
511
 * Convert an IPv4 or IPv6 IP range to an array of subnets which can contain the range.
512
 * Algorithm and embodying code PD'ed by Stilez - enjoy as you like :-)
513
 *
514
 * Documented on pfsense dev list 19-20 May 2013. Summary:
515
 *
516
 * The algorithm looks at patterns of 0's and 1's in the least significant bit(s), whether IPv4 or IPv6.
517
 * These are all that needs checking to identify a _guaranteed_ correct, minimal and optimal subnet array.
518
 *
519
 * As a result, string/binary pattern matching of the binary IP is very efficient. It uses just 2 pattern-matching rules
520
 * to chop off increasingly larger subnets at both ends that can't be part of larger subnets, until nothing's left.
521
 *
522
 * (a) If any range has EITHER low bit 1 (in startip) or 0 (in endip), that end-point is _always guaranteed_ to be optimally
523
 * represented by its own 'single IP' CIDR; the remaining range then shrinks by one IP up or down, causing the new end-point's
524
 * low bit to change from 1->0 (startip) or 0->1 (endip). Only one edge case needs checking: if a range contains exactly 2
525
 * adjacent IPs of this format, then the two IPs themselves are required to span it, and we're done.
526
 * 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
527
 * low bits can now be ignored.
528
 *
529
 * (b) If any range has BOTH startip and endip ending in some number of 0's and 1's respectively, these low bits can
530
 * *always* be ignored and "bit-shifted" for subnet spanning. So provided we remember the bits we've place-shifted, we can
531
 * _always_ right-shift and chop off those bits, leaving a smaller range that has EITHER startip ending in 1 or endip ending
532
 * in 0 (ie can now apply (a) again) or the entire range has vanished and we're done.
533
 * We then loop to redo (a) again on the remaining (place shifted) range until after a few loops, the remaining (place shifted)
534
 * range 'vanishes' by meeting the exit criteria of (a) or (b), and we're done.
535
 */
536 ed516fa7 stilez
function ip_range_to_subnet_array($ip1, $ip2) {
537 bb67ac32 Phil Davis
538 ed516fa7 stilez
	if (is_ipaddrv4($ip1) && is_ipaddrv4($ip2)) {
539
		$proto = 'ipv4';  // for clarity
540
		$bits = 32;
541
		$ip1bin = decbin(ip2long32($ip1));
542
		$ip2bin = decbin(ip2long32($ip2));
543
	} elseif (is_ipaddrv6($ip1) && is_ipaddrv6($ip2)) {
544
		$proto = 'ipv6';
545
		$bits = 128;
546 de645734 Renato Botelho
		$ip1bin = ip6_to_bin($ip1);
547
		$ip2bin = ip6_to_bin($ip2);
548 19b802f4 Phil Davis
	} else {
549 ed516fa7 stilez
		return array();
550 19b802f4 Phil Davis
	}
551 ecd1f2d9 jim-p
552 ed516fa7 stilez
	// it's *crucial* that binary strings are guaranteed the expected length;  do this for certainty even though for IPv6 it's redundant
553
	$ip1bin = str_pad($ip1bin, $bits, '0', STR_PAD_LEFT);
554
	$ip2bin = str_pad($ip2bin, $bits, '0', STR_PAD_LEFT);
555 ecd1f2d9 jim-p
556 19b802f4 Phil Davis
	if ($ip1bin == $ip2bin) {
557 ed516fa7 stilez
		return array($ip1 . '/' . $bits); // exit if ip1=ip2 (trivial case)
558 19b802f4 Phil Davis
	}
559
560
	if ($ip1bin > $ip2bin) {
561 ed516fa7 stilez
		list ($ip1bin, $ip2bin) = array($ip2bin, $ip1bin);  // swap if needed (ensures ip1 < ip2)
562 19b802f4 Phil Davis
	}
563 ecd1f2d9 jim-p
564 ed516fa7 stilez
	$rangesubnets = array();
565
	$netsize = 0;
566
567
	do {
568
		// at loop start, $ip1 is guaranteed strictly less than $ip2 (important for edge case trapping and preventing accidental binary wrapround)
569
		// which means the assignments $ip1 += 1 and $ip2 -= 1 will always be "binary-wrapround-safe"
570
571
		// 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)
572 19b802f4 Phil Davis
573 ed516fa7 stilez
		if (substr($ip1bin, -1, 1) == '1') {
574
			// the start ip must be in a separate one-IP cidr range
575
			$new_subnet_ip = substr($ip1bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize);
576
			$rangesubnets[$new_subnet_ip] = $bits - $netsize;
577
			$n = strrpos($ip1bin, '0');  //can't be all 1's
578
			$ip1bin = ($n == 0 ? '' : substr($ip1bin, 0, $n)) . '1' . str_repeat('0', $bits - $n - 1);  // BINARY VERSION OF $ip1 += 1
579 19b802f4 Phil Davis
		}
580 ed516fa7 stilez
581
		// 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)
582 19b802f4 Phil Davis
583 ed516fa7 stilez
		if (substr($ip2bin, -1, 1) == '0') {
584
			// the end ip must be in a separate one-IP cidr range
585
			$new_subnet_ip = substr($ip2bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize);
586
			$rangesubnets[$new_subnet_ip] = $bits - $netsize;
587
			$n = strrpos($ip2bin, '1');  //can't be all 0's
588
			$ip2bin = ($n == 0 ? '' : substr($ip2bin, 0, $n)) . '0' . str_repeat('1', $bits - $n - 1);  // BINARY VERSION OF $ip2 -= 1
589
			// already checked for the edge case where end = start+1 and start ends in 0x1, above, so it's safe
590 ecd1f2d9 jim-p
		}
591
592 19b802f4 Phil Davis
		// this is the only edge case arising from increment/decrement.
593 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)
594 19b802f4 Phil Davis
595
		if ($ip2bin < $ip1bin) {
596 ed516fa7 stilez
			continue;
597 19b802f4 Phil Davis
		}
598 ed516fa7 stilez
599
		// step #3 the start and end ip MUST now end in '0's and '1's respectively
600
		// so we have a non-trivial range AND the last N bits are no longer important for CIDR purposes.
601
602
		$shift = $bits - max(strrpos($ip1bin, '0'), strrpos($ip2bin, '1'));  // num of low bits which are '0' in ip1 and '1' in ip2
603
		$ip1bin = str_repeat('0', $shift) . substr($ip1bin, 0, $bits - $shift);
604
		$ip2bin = str_repeat('0', $shift) . substr($ip2bin, 0, $bits - $shift);
605
		$netsize += $shift;
606
		if ($ip1bin == $ip2bin) {
607
			// we're done.
608
			$new_subnet_ip = substr($ip1bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize);
609
			$rangesubnets[$new_subnet_ip] = $bits - $netsize;
610
			continue;
611 ecd1f2d9 jim-p
		}
612 19b802f4 Phil Davis
613 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.
614
	} while ($ip1bin < $ip2bin);
615 ecd1f2d9 jim-p
616 ed516fa7 stilez
	// subnets are ordered by bit size. Re sort by IP ("naturally") and convert back to IPv4/IPv6
617 ecd1f2d9 jim-p
618 ed516fa7 stilez
	ksort($rangesubnets, SORT_STRING);
619
	$out = array();
620 ecd1f2d9 jim-p
621 ed516fa7 stilez
	foreach ($rangesubnets as $ip => $netmask) {
622
		if ($proto == 'ipv4') {
623
			$i = str_split($ip, 8);
624 19b802f4 Phil Davis
			$out[] = implode('.', array(bindec($i[0]), bindec($i[1]), bindec($i[2]), bindec($i[3]))) . '/' . $netmask;
625
		} else {
626 de645734 Renato Botelho
			$out[] = bin_to_compressed_ip6($ip) . '/' . $netmask;
627 19b802f4 Phil Davis
		}
628 ecd1f2d9 jim-p
	}
629
630 ed516fa7 stilez
	return $out;
631 ecd1f2d9 jim-p
}
632
633 bb67ac32 Phil Davis
/* returns true if $range is a valid pair of IPv4 or IPv6 addresses separated by a "-"
634 751533a2 Phil Davis
	false - if not a valid pair
635
	true (numeric 4 or 6) - if valid, gives type of addresses */
636 ecd1f2d9 jim-p
function is_iprange($range) {
637
	if (substr_count($range, '-') != 1) {
638
		return false;
639
	}
640
	list($ip1, $ip2) = explode ('-', $range);
641 751533a2 Phil Davis
	if (is_ipaddrv4($ip1) && is_ipaddrv4($ip2)) {
642 bb67ac32 Phil Davis
		return 4;
643 751533a2 Phil Davis
	}
644
	if (is_ipaddrv6($ip1) && is_ipaddrv6($ip2)) {
645 bb67ac32 Phil Davis
		return 6;
646 751533a2 Phil Davis
	}
647 bb67ac32 Phil Davis
	return false;
648 ecd1f2d9 jim-p
}
649
650 31495068 stilez
/* returns true if $ipaddr is a valid dotted IPv4 address or a IPv6
651 751533a2 Phil Davis
	false - not valid
652
	true (numeric 4 or 6) - if valid, gives type of address */
653 5b237745 Scott Ullrich
function is_ipaddr($ipaddr) {
654 751533a2 Phil Davis
	if (is_ipaddrv4($ipaddr)) {
655 31495068 stilez
		return 4;
656 47593ac6 Seth Mos
	}
657 751533a2 Phil Davis
	if (is_ipaddrv6($ipaddr)) {
658 31495068 stilez
		return 6;
659 47593ac6 Seth Mos
	}
660
	return false;
661
}
662
663 22b5abac Seth Mos
/* returns true if $ipaddr is a valid IPv6 address */
664 47593ac6 Seth Mos
function is_ipaddrv6($ipaddr) {
665 751533a2 Phil Davis
	if (!is_string($ipaddr) || empty($ipaddr)) {
666 b5b5bcc0 Ermal
		return false;
667 751533a2 Phil Davis
	}
668 2f87470c Luiz Souza
	/*
669
	 * While Net_IPv6::checkIPv6() considers IPv6/mask a valid IPv6,
670
	 * is_ipaddrv6() needs to be more strict to keep the compatibility
671
	 * with is_ipaddrv4().
672
	 */
673
	if (strstr($ipaddr, "/")) {
674
		return false;
675
	}
676 55909a9a Ermal
	if (strstr($ipaddr, "%") && is_linklocal($ipaddr)) {
677
		$tmpip = explode("%", $ipaddr);
678
		$ipaddr = $tmpip[0];
679
	}
680 1e5da31d Ermal
	return Net_IPv6::checkIPv6($ipaddr);
681 47593ac6 Seth Mos
}
682
683 90fd68c6 Viktor G
function is_ipaddrv6_v4map($ipaddr) {
684
	/* check RFC4291 par 2.2.2 format, ex: fd00::1.2.3.4
685
	 * see https://redmine.pfsense.org/issues/11446 */
686
	if (is_ipaddrv6($ipaddr) && preg_match('/^[0-9a-f:]{2,30}[0-9.]{7,15}$/i', $ipaddr)) {
687
		return true;
688
	}
689
	return false;
690
}
691
692 47593ac6 Seth Mos
/* returns true if $ipaddr is a valid dotted IPv4 address */
693
function is_ipaddrv4($ipaddr) {
694 c3b3e9c7 stilez
	if (!is_string($ipaddr) || empty($ipaddr) || ip2long($ipaddr) === FALSE) {
695 5b237745 Scott Ullrich
		return false;
696 751533a2 Phil Davis
	}
697 c3b3e9c7 stilez
	return true;
698 5b237745 Scott Ullrich
}
699
700 b6ea9c61 Viktor G
function is_mcast($ipaddr) {
701
	if (is_mcastv4($ipaddr)) {
702
		return 4;
703
	}
704
	if (is_mcastv6($ipaddr)) {
705
		return 6;
706
	}
707
	return false;
708
}
709
710
function is_mcastv4($ipaddr) {
711
	if (!is_ipaddrv4($ipaddr) ||
712
	    !preg_match('/^2(?:2[4-9]|3\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d?|0)){3}$/', $ipaddr)) {
713
		return false;
714
	}
715
	return true;
716
}
717
718
function is_mcastv6($ipaddr) {
719
	if (!is_ipaddrv6($ipaddr) || !preg_match('/^ff.+$/', $ipaddr)) {
720
		return false;
721
	}
722
	return true;
723
}
724
725 a2fd89dd stilez
/* returns 4 or 6 respectively (== TRUE) if $ipaddr is a valid IPv4 or IPv6 linklocal address
726 f38d984b Renato Botelho
   returns '' if not a valid linklocal address */
727 19341491 Renato Botelho
function is_linklocal($ipaddr) {
728 f38d984b Renato Botelho
	if (is_ipaddrv4($ipaddr)) {
729
		// input is IPv4
730
		// test if it's 169.254.x.x per rfc3927 2.1
731
		$ip4 = explode(".", $ipaddr);
732
		if ($ip4[0] == '169' && $ip4[1] == '254') {
733
			return 4;
734 a2fd89dd stilez
		}
735 f38d984b Renato Botelho
	} elseif (Net_IPv6::getAddressType($ipaddr) == NET_IPV6_LOCAL_LINK) {
736
		return 6;
737 a2fd89dd stilez
	}
738
	return '';
739 19341491 Renato Botelho
}
740 3f5f7ad3 smos
741 bd6ff328 Renato Botelho
/* returns scope of a linklocal address */
742
function get_ll_scope($addr) {
743 751533a2 Phil Davis
	if (!is_linklocal($addr) || !strstr($addr, "%")) {
744 bd6ff328 Renato Botelho
		return "";
745 751533a2 Phil Davis
	}
746 bd6ff328 Renato Botelho
	list ($ll, $scope) = explode("%", $addr);
747
	return $scope;
748
}
749
750 3f5f7ad3 smos
/* returns true if $ipaddr is a valid literal IPv6 address */
751
function is_literalipaddrv6($ipaddr) {
752 a2fd89dd stilez
	if (substr($ipaddr,0,1) == '[' && substr($ipaddr,-1,1) == ']') {
753
		// if it's data wrapped in "[ ... ]" then test if middle part is valid IPv6
754
		return is_ipaddrv6(substr($ipaddr,1,-1));
755 751533a2 Phil Davis
	}
756 a2fd89dd stilez
	return false;
757 3f5f7ad3 smos
}
758
759 a2fd89dd stilez
/* returns true if $iport is a valid IPv4:port or [Literal IPv6]:port
760 751533a2 Phil Davis
	false - not valid
761
	true (numeric 4 or 6) - if valid, gives type of address */
762 4a8a90ff jim-p
function is_ipaddrwithport($ipport) {
763 31495068 stilez
	$c = strrpos($ipport, ":");
764 751533a2 Phil Davis
	if ($c === false) {
765 31495068 stilez
		return false;  // can't split at final colon if no colon exists
766 751533a2 Phil Davis
	}
767
768
	if (!is_port(substr($ipport, $c + 1))) {
769 31495068 stilez
		return false;  // no valid port after last colon
770 751533a2 Phil Davis
	}
771 31495068 stilez
772
	$ip = substr($ipport, 0, $c);  // else is text before last colon a valid IP
773 751533a2 Phil Davis
	if (is_literalipaddrv6($ip)) {
774 31495068 stilez
		return 6;
775 751533a2 Phil Davis
	} elseif (is_ipaddrv4($ip)) {
776 31495068 stilez
		return 4;
777 751533a2 Phil Davis
	} else {
778 4a8a90ff jim-p
		return false;
779 751533a2 Phil Davis
	}
780 4a8a90ff jim-p
}
781
782 d3a2337a jim-p
function is_hostnamewithport($hostport) {
783
	$parts = explode(":", $hostport);
784 a2fd89dd stilez
	// no need to validate with is_string(); if it's not a string then explode won't return 2 parts anyway
785
	if (count($parts) == 2) {
786
		return is_hostname($parts[0]) && is_port($parts[1]);
787 d3a2337a jim-p
	}
788 a2fd89dd stilez
	return false;
789 d3a2337a jim-p
}
790
791 87f0be87 Chris Buechler
/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */
792 5b237745 Scott Ullrich
function is_ipaddroralias($ipaddr) {
793 1e578a7f Ermal Lu?i
	global $config;
794 87f0be87 Chris Buechler
795 1e578a7f Ermal Lu?i
	if (is_alias($ipaddr)) {
796
		if (is_array($config['aliases']['alias'])) {
797
			foreach ($config['aliases']['alias'] as $alias) {
798 751533a2 Phil Davis
				if ($alias['name'] == $ipaddr && !preg_match("/port/i", $alias['type'])) {
799 1e578a7f Ermal Lu?i
					return true;
800 751533a2 Phil Davis
				}
801 1e578a7f Ermal Lu?i
			}
802 5bbd08e1 Warren Baker
		}
803 1e578a7f Ermal Lu?i
		return false;
804 751533a2 Phil Davis
	} else {
805 87f0be87 Chris Buechler
		return is_ipaddr($ipaddr);
806 751533a2 Phil Davis
	}
807 87f0be87 Chris Buechler
808 5b237745 Scott Ullrich
}
809
810 a5e2a35f stilez
/* returns true if $subnet is a valid IPv4 or IPv6 subnet in CIDR format
811 751533a2 Phil Davis
	false - if not a valid subnet
812
	true (numeric 4 or 6) - if valid, gives type of subnet */
813 5b237745 Scott Ullrich
function is_subnet($subnet) {
814 35c60e99 Viktor G
	if (is_string($subnet) && preg_match('/^(?:([0-9.]{7,15})|([0-9a-f:]{2,39}|[0-9a-f:]{2,30}[0-9.]{7,15}))\/(\d{1,3})$/i', $subnet, $parts)) {
815 751533a2 Phil Davis
		if (is_ipaddrv4($parts[1]) && $parts[3] <= 32) {
816 a5e2a35f stilez
			return 4;
817 751533a2 Phil Davis
		}
818
		if (is_ipaddrv6($parts[2]) && $parts[3] <= 128) {
819 a5e2a35f stilez
			return 6;
820 751533a2 Phil Davis
		}
821 b1b42a06 Warren Baker
	}
822
	return false;
823
}
824
825 905bd44e PiBa-NL
function is_v4($ip_or_subnet) {
826
	return is_ipaddrv4($ip_or_subnet) || is_subnetv4($ip_or_subnet);
827
}
828
829
function is_v6($ip_or_subnet) {
830
	return is_ipaddrv6($ip_or_subnet) || is_subnetv6($ip_or_subnet);
831
}
832
833 a5e2a35f stilez
/* same as is_subnet() but accepts IPv4 only */
834 b1b42a06 Warren Baker
function is_subnetv4($subnet) {
835 a5e2a35f stilez
	return (is_subnet($subnet) == 4);
836 5b237745 Scott Ullrich
}
837
838 a5e2a35f stilez
/* same as is_subnet() but accepts IPv6 only */
839 fdb9c1db Warren Baker
function is_subnetv6($subnet) {
840 a5e2a35f stilez
	return (is_subnet($subnet) == 6);
841 fdb9c1db Warren Baker
}
842
843 5b237745 Scott Ullrich
/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */
844
function is_subnetoralias($subnet) {
845
	global $aliastable;
846 98bbf05a Scott Ullrich
847 751533a2 Phil Davis
	if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet])) {
848 5b237745 Scott Ullrich
		return true;
849 751533a2 Phil Davis
	} else {
850 5b237745 Scott Ullrich
		return is_subnet($subnet);
851 751533a2 Phil Davis
	}
852 5b237745 Scott Ullrich
}
853
854 cafe9038 stilez
/* Get number of addresses in an IPv4/IPv6 subnet (represented as a string)
855
   optional $exact=true forces error (0) to be returned if it can't be represented exactly
856
   Exact result not possible above PHP_MAX_INT which is about 2^31 addresses on x32 or 2^63 on x64
857 4402b5cb stilez
   Returns 0 for bad data or if cannot represent size as an INT when $exact is set. */
858
function subnet_size($subnet, $exact=false) {
859
	$parts = explode("/", $subnet);
860 0987677a stilez
	$iptype = is_ipaddr($parts[0]);
861
	if (count($parts) == 2 && $iptype) {
862
		return subnet_size_by_netmask($iptype, $parts[1], $exact);
863 4402b5cb stilez
	}
864
	return 0;
865
}
866
867 cafe9038 stilez
/* Get number of addresses in an IPv4/IPv6 subnet (represented numerically as IP type + bits)
868
   optional $exact=true forces error (0) to be returned if it can't be represented exactly
869
   Hard to think where we might need to count exactly a huge subnet but an overflow detection option is probably sensible
870
   Returns 0 for bad data or if cannot represent size as an INT when $exact is set. */
871 4402b5cb stilez
function subnet_size_by_netmask($iptype, $bits, $exact=false) {
872
	if (!is_numericint($bits)) {
873
		return 0;
874
	} elseif ($iptype == 4 && $bits <= 32) {
875
		$snsize = 32 - $bits;
876
	} elseif ($iptype == 6 && $bits <= 128) {
877
		$snsize = 128 - $bits;
878 19b802f4 Phil Davis
	} else {
879 b17ac4f7 stilez
		return 0;
880
	}
881 4402b5cb stilez
882 4f7956ad Steve Beaver
	// 2**N returns an exact result as an INT if possible, and a float/double if not.
883 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
884
	$result = 2 ** $snsize;
885 4f7956ad Steve Beaver
886 4402b5cb stilez
	if ($exact && !is_int($result)) {
887
		//exact required but can't represent result exactly as an INT
888
		return 0;
889
	} else {
890
		// result ok, will be an INT where possible (guaranteed up to 2^31 addresses on x32/x64) and a float for 'huge' subnets
891
		return $result;
892
	}
893 b17ac4f7 stilez
}
894
895 2208be8b Chris Buechler
/* function used by pfblockerng */
896
function subnetv4_expand($subnet) {
897
	$result = array();
898
	list ($ip, $bits) = explode("/", $subnet);
899
	$net = ip2long($ip);
900
	$mask = (0xffffffff << (32 - $bits));
901
	$net &= $mask;
902
	$size = round(exp(log(2) * (32 - $bits)));
903
	for ($i = 0; $i < $size; $i += 1) {
904
		$result[] = long2ip($net | $i);
905
	}
906
	return $result;
907
}
908
909 e8d5be8e stilez
/* find out whether two IPv4/IPv6 CIDR subnets overlap.
910
   Note: CIDR overlap implies one is identical or included so largest sn will be the same */
911 b17ac4f7 stilez
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
912 94eb702f stilez
	if (is_ipaddrv4($subnet1)) {
913 e8d5be8e stilez
		return check_subnetsv4_overlap($subnet1, $bits1, $subnet2, $bits2);
914 751533a2 Phil Davis
	} else {
915 e8d5be8e stilez
		return check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2);
916 751533a2 Phil Davis
	}
917 b17ac4f7 stilez
}
918
919 e8d5be8e stilez
/* find out whether two IPv4 CIDR subnets overlap.
920
   Note: CIDR overlap means sn1/sn2 are identical or one is included in other. So sn using largest $bits will be the same  */
921
function check_subnetsv4_overlap($subnet1, $bits1, $subnet2, $bits2) {
922 62512efa stilez
	$largest_sn = min($bits1, $bits2);
923 e8d5be8e stilez
	$subnetv4_start1 = gen_subnetv4($subnet1, $largest_sn);
924 9f4a788f stilez
	$subnetv4_start2 = gen_subnetv4($subnet2, $largest_sn);
925 4f7956ad Steve Beaver
926 9d3e8723 Phil Davis
	if ($subnetv4_start1 == '' || $subnetv4_start2 == '') {
927 e8d5be8e stilez
		// One or both args is not a valid IPv4 subnet
928
		//FIXME: needs to return "bad data" not true/false if bad. For now return false, best we can do until fixed
929
		return false;
930
	}
931
	return ($subnetv4_start1 == $subnetv4_start2);
932
}
933 b17ac4f7 stilez
934 e8d5be8e stilez
/* find out whether two IPv6 CIDR subnets overlap.
935
   Note: CIDR overlap means sn1/sn2 are identical or one is included in other. So sn using largest $bits will be the same  */
936 9f8266cd stilez
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
937 62512efa stilez
	$largest_sn = min($bits1, $bits2);
938 9f8266cd stilez
	$subnetv6_start1 = gen_subnetv6($subnet1, $largest_sn);
939 9f4a788f stilez
	$subnetv6_start2 = gen_subnetv6($subnet2, $largest_sn);
940 4f7956ad Steve Beaver
941 9d3e8723 Phil Davis
	if ($subnetv6_start1 == '' || $subnetv6_start2 == '') {
942 e8d5be8e stilez
		// One or both args is not a valid IPv6 subnet
943
		//FIXME: needs to return "bad data" not true/false if bad. For now return false, best we can do until fixed
944
		return false;
945
	}
946
	return ($subnetv6_start1 == $subnetv6_start2);
947 b17ac4f7 stilez
}
948 4c62c1ff Renato Botelho
949
/* return all PTR zones for a IPv6 network */
950
function get_v6_ptr_zones($subnet, $bits) {
951
	$result = array();
952
953
	if (!is_ipaddrv6($subnet)) {
954
		return $result;
955
	}
956
957
	if (!is_numericint($bits) || $bits > 128) {
958
		return $result;
959
	}
960
961
	/*
962
	 * Find a small nibble boundary subnet mask
963
	 * e.g. a /29 will create 8 /32 PTR zones
964
	 */
965
	$small_sn = $bits;
966
	while ($small_sn % 4 != 0) {
967
		$small_sn++;
968
	}
969
970
	/* Get network prefix */
971
	$small_subnet = Net_IPv6::getNetmask($subnet, $bits);
972
973
	/*
974
	 * While small network is part of bigger one, increase 4-bit in last
975
	 * digit to get next small network
976
	 */
977
	while (Net_IPv6::isInNetmask($small_subnet, $subnet, $bits)) {
978
		/* Get a pure hex value */
979
		$unpacked = unpack('H*hex', inet_pton($small_subnet));
980
		/* Create PTR record using $small_sn / 4 chars */
981
		$result[] = implode('.', array_reverse(str_split(substr(
982
		    $unpacked['hex'], 0, $small_sn / 4)))).'.ip6.arpa';
983
984
		/* Detect what part of IP should be increased */
985
		$change_part = (int) ($small_sn / 16);
986
		if ($small_sn % 16 == 0) {
987
			$change_part--;
988
		}
989
990 53904d09 Renato Botelho
		/* Increase 1 to desired part */
991 4c62c1ff Renato Botelho
		$parts = explode(":", Net_IPv6::uncompress($small_subnet));
992 53904d09 Renato Botelho
		$parts[$change_part]++;
993 4c62c1ff Renato Botelho
		$small_subnet = implode(":", $parts);
994
	}
995
996
	return $result;
997
}
998 b17ac4f7 stilez
999
/* return true if $addr is in $subnet, false if not */
1000 086cf944 Phil Davis
function ip_in_subnet($addr, $subnet) {
1001 751533a2 Phil Davis
	if (is_ipaddrv6($addr) && is_subnetv6($subnet)) {
1002 b17ac4f7 stilez
		return (Net_IPv6::isInNetmask($addr, $subnet));
1003 0c5dd854 Renato Botelho
	} else if (is_ipaddrv4($addr) && is_subnetv4($subnet)) {
1004 b17ac4f7 stilez
		list($ip, $mask) = explode('/', $subnet);
1005
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
1006
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
1007
	}
1008 0c5dd854 Renato Botelho
	return false;
1009 b17ac4f7 stilez
}
1010
1011 6bcbd862 Phil Davis
/* returns true if $hostname is just a valid hostname (top part without any of the domain part) */
1012
function is_unqualified_hostname($hostname) {
1013 751533a2 Phil Davis
	if (!is_string($hostname)) {
1014 6bcbd862 Phil Davis
		return false;
1015 751533a2 Phil Davis
	}
1016 6bcbd862 Phil Davis
1017 8fdddd51 Chris Buechler
	if (preg_match('/^(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])$/i', $hostname)) {
1018 6bcbd862 Phil Davis
		return true;
1019 751533a2 Phil Davis
	} else {
1020 6bcbd862 Phil Davis
		return false;
1021 751533a2 Phil Davis
	}
1022 6bcbd862 Phil Davis
}
1023
1024
/* returns true if $hostname is a valid hostname, with or without being a fully-qualified domain name. */
1025 0edcccc3 Daniel Seebald
function is_hostname($hostname, $allow_wildcard=false) {
1026 751533a2 Phil Davis
	if (!is_string($hostname)) {
1027 5b237745 Scott Ullrich
		return false;
1028 751533a2 Phil Davis
	}
1029 98bbf05a Scott Ullrich
1030 cc882a8b Daniel Seebald
	if (is_domain($hostname, $allow_wildcard)) {
1031 5454fd1b Phil Davis
		if ((substr_count($hostname, ".") == 1) && ($hostname[strlen($hostname)-1] == ".")) {
1032
			/* Only a single dot at the end like "test." - hosts cannot be directly in the root domain. */
1033
			return false;
1034
		} else {
1035
			return true;
1036
		}
1037 751533a2 Phil Davis
	} else {
1038 5b237745 Scott Ullrich
		return false;
1039 751533a2 Phil Davis
	}
1040 5b237745 Scott Ullrich
}
1041
1042
/* returns true if $domain is a valid domain name */
1043 8ee5aa03 Viktor Gurov
function is_domain($domain, $allow_wildcard=false, $trailing_dot=true) {
1044 751533a2 Phil Davis
	if (!is_string($domain)) {
1045 5b237745 Scott Ullrich
		return false;
1046 751533a2 Phil Davis
	}
1047 8ee5aa03 Viktor Gurov
	if (!$trailing_dot && ($domain[strlen($domain)-1] == ".")) {
1048
		return false;
1049
	}
1050 0edcccc3 Daniel Seebald
	if ($allow_wildcard) {
1051
		$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';
1052
	} else {
1053
		$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';
1054
	}
1055
1056
	if (preg_match($domain_regex, $domain)) {
1057 5b237745 Scott Ullrich
		return true;
1058 751533a2 Phil Davis
	} else {
1059 5b237745 Scott Ullrich
		return false;
1060 751533a2 Phil Davis
	}
1061 5b237745 Scott Ullrich
}
1062
1063
/* returns true if $macaddr is a valid MAC address */
1064 80e7011f jim-p
function is_macaddr($macaddr, $partial=false) {
1065 c982fdbc Luiz Otavio O Souza
	$values = explode(":", $macaddr);
1066 80e7011f jim-p
1067
	/* Verify if the MAC address has a proper amount of parts for either a partial or full match. */
1068
	if ($partial) {
1069
		if ((count($values) < 1) || (count($values) > 6)) {
1070
			return false;
1071
		}
1072
	} elseif (count($values) != 6) {
1073 c982fdbc Luiz Otavio O Souza
		return false;
1074
	}
1075 80e7011f jim-p
	for ($i = 0; $i < count($values); $i++) {
1076 c982fdbc Luiz Otavio O Souza
		if (ctype_xdigit($values[$i]) == false)
1077
			return false;
1078
		if (hexdec($values[$i]) < 0 || hexdec($values[$i]) > 255)
1079
			return false;
1080
	}
1081
1082
	return true;
1083 5b237745 Scott Ullrich
}
1084
1085 e1f5381f Phil Davis
/*
1086
	If $return_message is true then
1087
		returns a text message about the reason that the name is invalid.
1088
		the text includes the type of "thing" that is being checked, passed in $object. (e.g. "alias", "gateway group", "schedule")
1089
	else
1090
		returns true if $name is a valid name for an alias
1091
		returns false if $name is not a valid name for an alias
1092
1093
	Aliases cannot be:
1094
		bad chars: anything except a-z 0-9 and underscore
1095
		bad names: empty string, pure numeric, pure underscore
1096
		reserved words: pre-defined service/protocol/port names which should not be ambiguous, and the words "port" and  "pass" */
1097
1098
function is_validaliasname($name, $return_message = false, $object = "alias") {
1099 beeef1f0 Bill Marquette
	/* Array of reserved words */
1100 0c2badde Colin Smith
	$reserved = array("port", "pass");
1101 4ad9a1e7 stilez
1102 751533a2 Phil Davis
	if (!is_string($name) || strlen($name) >= 32 || preg_match('/(^_*$|^\d*$|[^a-z0-9_])/i', $name)) {
1103 e1f5381f Phil Davis
		if ($return_message) {
1104
			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, _');
1105
		} else {
1106
			return false;
1107
		}
1108 751533a2 Phil Davis
	}
1109 e1f5381f Phil Davis
	if (in_array($name, $reserved, true)) {
1110
		if ($return_message) {
1111
			return sprintf(gettext('The %1$s name must not be either of the reserved words %2$s or %3$s.'), $object, "'port'", "'pass'");
1112
		} else {
1113
			return false;
1114
		}
1115 751533a2 Phil Davis
	}
1116 e1f5381f Phil Davis
	if (getprotobyname($name)) {
1117
		if ($return_message) {
1118 a2405c1a jim-p
			return sprintf(gettext('The %1$s name must not be an IP protocol name such as TCP, UDP, ICMP etc.'), $object);
1119 e1f5381f Phil Davis
		} else {
1120
			return false;
1121
		}
1122
	}
1123
	if (getservbyname($name, "tcp") || getservbyname($name, "udp")) {
1124
		if ($return_message) {
1125 a2405c1a jim-p
			return sprintf(gettext('The %1$s name must not be a well-known or registered TCP or UDP port name such as ssh, smtp, pop3, tftp, http, openvpn etc.'), $object);
1126 e1f5381f Phil Davis
		} else {
1127
			return false;
1128
		}
1129
	}
1130
	if ($return_message) {
1131
		return sprintf(gettext("The %1$s name is valid."), $object);
1132
	} else {
1133
		return true;
1134
	}
1135
}
1136
1137
/* returns a text message indicating if the alias name is valid, or the reason it is not valid. */
1138
function invalidaliasnamemsg($name, $object = "alias") {
1139
	return is_validaliasname($name, true, $object);
1140 5b237745 Scott Ullrich
}
1141
1142 ee956d19 Renato Botelho
/*
1143
 * returns true if $range is a valid integer range between $min and $max
1144
 * range delimiter can be ':' or '-'
1145
 */
1146
function is_intrange($range, $min, $max) {
1147
	$values = preg_split("/[:-]/", $range);
1148
1149
	if (!is_array($values) || count($values) != 2) {
1150
		return false;
1151
	}
1152
1153
	if (!ctype_digit($values[0]) || !ctype_digit($values[1])) {
1154
		return false;
1155
	}
1156
1157
	$values[0] = intval($values[0]);
1158
	$values[1] = intval($values[1]);
1159
1160
	if ($values[0] >= $values[1]) {
1161
		return false;
1162
	}
1163
1164
	if ($values[0] < $min || $values[1] > $max) {
1165
		return false;
1166
	}
1167
1168
	return true;
1169
}
1170
1171 5b237745 Scott Ullrich
/* returns true if $port is a valid TCP/UDP port */
1172
function is_port($port) {
1173 751533a2 Phil Davis
	if (ctype_digit($port) && ((intval($port) >= 1) && (intval($port) <= 65535))) {
1174 75106235 PiBa-NL
		return true;
1175 751533a2 Phil Davis
	}
1176
	if (getservbyname($port, "tcp") || getservbyname($port, "udp")) {
1177 9060f420 Renato Botelho
		return true;
1178 751533a2 Phil Davis
	}
1179 75106235 PiBa-NL
	return false;
1180 5b237745 Scott Ullrich
}
1181
1182 6df10582 Erik Schaeffer
/* returns true if $port is in use */
1183
function is_port_in_use($port, $proto = "tcp", $ip_version = 4) {
1184
	$port_info = array();
1185
	exec("/usr/bin/netstat --libxo json -an " . escapeshellarg('-' . $ip_version) . " -p " . escapeshellarg($proto), $rawdata, $rc);
1186
	if ($rc == 0) {
1187
		$netstatarr = json_decode(implode(" ", $rawdata), JSON_OBJECT_AS_ARRAY);
1188
		$netstatarr = $netstatarr['statistics']['socket'];
1189
1190
		foreach($netstatarr as $index => $portstats){
1191
			array_push($port_info, $portstats['local']['port']);
1192
		}
1193
	}
1194
1195
	return in_array($port, $port_info);
1196
}
1197
1198 5a1eebc7 Scott Ullrich
/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */
1199
function is_portrange($portrange) {
1200 5bbd08e1 Warren Baker
	$ports = explode(":", $portrange);
1201 5a1eebc7 Scott Ullrich
1202 e371f8b9 whjvenyl
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
1203 5a1eebc7 Scott Ullrich
}
1204
1205 4081ecac Phil Davis
/* returns true if $port is a valid TCP/UDP port number or range ("<port>:<port>") */
1206 593e9fe3 Phil Davis
function is_port_or_range($port) {
1207 4081ecac Phil Davis
	return (is_port($port) || is_portrange($port));
1208
}
1209
1210 fe108b67 Phil Davis
/* returns true if $port is an alias that is a port type */
1211
function is_portalias($port) {
1212 1e578a7f Ermal Lu?i
	global $config;
1213
1214 5bbd08e1 Warren Baker
	if (is_alias($port)) {
1215
		if (is_array($config['aliases']['alias'])) {
1216
			foreach ($config['aliases']['alias'] as $alias) {
1217 751533a2 Phil Davis
				if ($alias['name'] == $port && preg_match("/port/i", $alias['type'])) {
1218 5bbd08e1 Warren Baker
					return true;
1219
				}
1220
			}
1221 751533a2 Phil Davis
		}
1222
	}
1223 fe108b67 Phil Davis
	return false;
1224
}
1225
1226
/* returns true if $port is a valid port number or an alias thereof */
1227
function is_port_or_alias($port) {
1228
	return (is_port($port) || is_portalias($port));
1229 1e578a7f Ermal Lu?i
}
1230
1231 4081ecac Phil Davis
/* returns true if $port is a valid TCP/UDP port number or range ("<port>:<port>") or an alias thereof */
1232 593e9fe3 Phil Davis
function is_port_or_range_or_alias($port) {
1233 fe108b67 Phil Davis
	return (is_port($port) || is_portrange($port) || is_portalias($port));
1234 4081ecac Phil Davis
}
1235
1236 d9f33a7f Renato Botelho
/* create ranges of sequential port numbers (200:215) and remove duplicates */
1237 f6622167 NOYB
function group_ports($ports, $kflc = false) {
1238 751533a2 Phil Davis
	if (!is_array($ports) || empty($ports)) {
1239 d9f33a7f Renato Botelho
		return;
1240 751533a2 Phil Davis
	}
1241 d9f33a7f Renato Botelho
1242
	$uniq = array();
1243 f6622167 NOYB
	$comments = array();
1244 d9f33a7f Renato Botelho
	foreach ($ports as $port) {
1245 f6622167 NOYB
		if (($kflc) && (strpos($port, '#') === 0)) {	// Keep Full Line Comments (lines beginning with #).
1246
			$comments[] = $port;
1247
		} else if (is_portrange($port)) {
1248 d9f33a7f Renato Botelho
			list($begin, $end) = explode(":", $port);
1249
			if ($begin > $end) {
1250
				$aux = $begin;
1251
				$begin = $end;
1252
				$end = $aux;
1253
			}
1254 751533a2 Phil Davis
			for ($i = $begin; $i <= $end; $i++) {
1255
				if (!in_array($i, $uniq)) {
1256 d9f33a7f Renato Botelho
					$uniq[] = $i;
1257 751533a2 Phil Davis
				}
1258
			}
1259 d9f33a7f Renato Botelho
		} else if (is_port($port)) {
1260 751533a2 Phil Davis
			if (!in_array($port, $uniq)) {
1261 d9f33a7f Renato Botelho
				$uniq[] = $port;
1262 751533a2 Phil Davis
			}
1263 d9f33a7f Renato Botelho
		}
1264
	}
1265
	sort($uniq, SORT_NUMERIC);
1266
1267
	$result = array();
1268
	foreach ($uniq as $idx => $port) {
1269
		if ($idx == 0) {
1270
			$result[] = $port;
1271
			continue;
1272
		}
1273
1274
		$last = end($result);
1275 751533a2 Phil Davis
		if (is_portrange($last)) {
1276 d9f33a7f Renato Botelho
			list($begin, $end) = explode(":", $last);
1277 751533a2 Phil Davis
		} else {
1278 d9f33a7f Renato Botelho
			$begin = $end = $last;
1279 751533a2 Phil Davis
		}
1280 d9f33a7f Renato Botelho
1281
		if ($port == ($end+1)) {
1282
			$end++;
1283
			$result[count($result)-1] = "{$begin}:{$end}";
1284
		} else {
1285
			$result[] = $port;
1286
		}
1287
	}
1288
1289 f6622167 NOYB
	return array_merge($comments, $result);
1290 d9f33a7f Renato Botelho
}
1291
1292 b8014f9d Scott Ullrich
/* returns true if $val is a valid shaper bandwidth value */
1293
function is_valid_shaperbw($val) {
1294 eaa37259 Ermal Luçi
	return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val));
1295 b8014f9d Scott Ullrich
}
1296
1297 54404519 Renato Botelho
/* returns true if $test is in the range between $start and $end */
1298
function is_inrange_v4($test, $start, $end) {
1299 8c48089f Renato Botelho
	if (!is_ipaddrv4($test) || !is_ipaddrv4($start) || !is_ipaddrv4($end)) {
1300 54404519 Renato Botelho
		return false;
1301 751533a2 Phil Davis
	}
1302 8c48089f Renato Botelho
1303
	if (ip2ulong($test) <= ip2ulong($end) &&
1304
	    ip2ulong($test) >= ip2ulong($start)) {
1305
		return true;
1306
	}
1307
1308
	return false;
1309 54404519 Renato Botelho
}
1310
1311 41b4867e Renato Botelho
/* returns true if $test is in the range between $start and $end */
1312
function is_inrange_v6($test, $start, $end) {
1313 8c48089f Renato Botelho
	if (!is_ipaddrv6($test) || !is_ipaddrv6($start) || !is_ipaddrv6($end)) {
1314 41b4867e Renato Botelho
		return false;
1315 751533a2 Phil Davis
	}
1316 8c48089f Renato Botelho
1317
	if (inet_pton($test) <= inet_pton($end) &&
1318
	    inet_pton($test) >= inet_pton($start)) {
1319
		return true;
1320
	}
1321
1322
	return false;
1323 41b4867e Renato Botelho
}
1324
1325 da6cb29e Renato Botelho
/* returns true if $test is in the range between $start and $end */
1326
function is_inrange($test, $start, $end) {
1327
	return is_ipaddrv6($test) ? is_inrange_v6($test, $start, $end) : is_inrange_v4($test, $start, $end);
1328
}
1329
1330 ab378cb7 Steve Beaver
function build_vip_list($fif, $family = "all") {
1331
	$list = array('address' => gettext('Interface Address'));
1332
1333
	$viplist = get_configured_vip_list($family);
1334
	foreach ($viplist as $vip => $address) {
1335
		if ($fif == get_configured_vip_interface($vip)) {
1336
			$list[$vip] = "$address";
1337
			if (get_vip_descr($address)) {
1338
				$list[$vip] .= " (". get_vip_descr($address) .")";
1339
			}
1340
		}
1341
	}
1342
1343
	return($list);
1344
}
1345
1346 ce94deb0 Luiz Otavio O Souza
function get_configured_vip_list($family = 'all', $type = VIP_ALL) {
1347 abcb2bed Ermal Lu?i
	global $config;
1348
1349 2a5960b0 Luiz Otavio O Souza
	$list = array();
1350 c6c398c6 jim-p
	if (!is_array($config['virtualip']) ||
1351
	    !is_array($config['virtualip']['vip']) ||
1352
	    empty($config['virtualip']['vip'])) {
1353 2a5960b0 Luiz Otavio O Souza
		return ($list);
1354 d9901ff4 Chris Buechler
	}
1355 76153238 Luiz Otavio O Souza
1356
	$viparr = &$config['virtualip']['vip'];
1357
	foreach ($viparr as $vip) {
1358 ce94deb0 Luiz Otavio O Souza
1359
		if ($type == VIP_CARP) {
1360
			if ($vip['mode'] != "carp")
1361
				continue;
1362
		} elseif ($type == VIP_IPALIAS) {
1363
			if ($vip['mode'] != "ipalias")
1364
				continue;
1365
		} else {
1366
			if ($vip['mode'] != "carp" && $vip['mode'] != "ipalias")
1367
				continue;
1368 d9901ff4 Chris Buechler
		}
1369 2a5960b0 Luiz Otavio O Souza
1370
		if ($family == 'all' ||
1371
		    ($family == 'inet' && is_ipaddrv4($vip['subnet'])) ||
1372
		    ($family == 'inet6' && is_ipaddrv6($vip['subnet']))) {
1373
			$list["_vip{$vip['uniqid']}"] = $vip['subnet'];
1374 4e322e2c Phil Davis
		}
1375 2a5960b0 Luiz Otavio O Souza
	}
1376
	return ($list);
1377
}
1378
1379
function get_configured_vip($vipinterface = '') {
1380
1381
	return (get_configured_vip_detail($vipinterface, 'all', 'vip'));
1382
}
1383
1384
function get_configured_vip_interface($vipinterface = '') {
1385
1386
	return (get_configured_vip_detail($vipinterface, 'all', 'iface'));
1387
}
1388
1389
function get_configured_vip_ipv4($vipinterface = '') {
1390
1391
	return (get_configured_vip_detail($vipinterface, 'inet', 'ip'));
1392
}
1393 76153238 Luiz Otavio O Souza
1394 2a5960b0 Luiz Otavio O Souza
function get_configured_vip_ipv6($vipinterface = '') {
1395
1396
	return (get_configured_vip_detail($vipinterface, 'inet6', 'ip'));
1397
}
1398
1399
function get_configured_vip_subnetv4($vipinterface = '') {
1400
1401
	return (get_configured_vip_detail($vipinterface, 'inet', 'subnet'));
1402
}
1403
1404
function get_configured_vip_subnetv6($vipinterface = '') {
1405
1406
	return (get_configured_vip_detail($vipinterface, 'inet6', 'subnet'));
1407
}
1408
1409
function get_configured_vip_detail($vipinterface = '', $family = 'inet', $what = 'ip') {
1410
	global $config;
1411
1412 c6c398c6 jim-p
	if (empty($vipinterface) ||
1413
	    !is_array($config['virtualip']) ||
1414
	    !is_array($config['virtualip']['vip']) ||
1415 2a5960b0 Luiz Otavio O Souza
	    empty($config['virtualip']['vip'])) {
1416
		return (NULL);
1417
	}
1418
1419
	$viparr = &$config['virtualip']['vip'];
1420
	foreach ($viparr as $vip) {
1421 d9901ff4 Chris Buechler
		if ($vip['mode'] != "carp" && $vip['mode'] != "ipalias") {
1422 76153238 Luiz Otavio O Souza
			continue;
1423 d9901ff4 Chris Buechler
		}
1424 76153238 Luiz Otavio O Souza
1425 d9901ff4 Chris Buechler
		if ($vipinterface != "_vip{$vip['uniqid']}") {
1426 76153238 Luiz Otavio O Souza
			continue;
1427 d9901ff4 Chris Buechler
		}
1428 76153238 Luiz Otavio O Souza
1429
		switch ($what) {
1430
			case 'subnet':
1431 2a5960b0 Luiz Otavio O Souza
				if ($family == 'inet' && is_ipaddrv4($vip['subnet']))
1432
					return ($vip['subnet_bits']);
1433
				else if ($family == 'inet6' && is_ipaddrv6($vip['subnet']))
1434
					return ($vip['subnet_bits']);
1435 76153238 Luiz Otavio O Souza
				break;
1436
			case 'iface':
1437 2a5960b0 Luiz Otavio O Souza
				return ($vip['interface']);
1438 76153238 Luiz Otavio O Souza
				break;
1439
			case 'vip':
1440 2a5960b0 Luiz Otavio O Souza
				return ($vip);
1441 76153238 Luiz Otavio O Souza
				break;
1442
			case 'ip':
1443
			default:
1444 d9901ff4 Chris Buechler
				if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1445 2a5960b0 Luiz Otavio O Souza
					return ($vip['subnet']);
1446 d9901ff4 Chris Buechler
				} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1447 2a5960b0 Luiz Otavio O Souza
					return ($vip['subnet']);
1448 d9901ff4 Chris Buechler
				}
1449 76153238 Luiz Otavio O Souza
				break;
1450 5bbd08e1 Warren Baker
		}
1451 76153238 Luiz Otavio O Souza
		break;
1452 5bbd08e1 Warren Baker
	}
1453 abcb2bed Ermal Lu?i
1454 e0e28fdf Luiz Otavio O Souza
	return (NULL);
1455 e6c60013 Renato Botelho
}
1456 67b0902f pierrepomes
1457 88bc2760 Erik Fonnesbeck
/* comparison function for sorting by the order in which interfaces are normally created */
1458
function compare_interface_friendly_names($a, $b) {
1459 751533a2 Phil Davis
	if ($a == $b) {
1460 88bc2760 Erik Fonnesbeck
		return 0;
1461 751533a2 Phil Davis
	} else if ($a == 'wan') {
1462 88bc2760 Erik Fonnesbeck
		return -1;
1463 751533a2 Phil Davis
	} else if ($b == 'wan') {
1464 88bc2760 Erik Fonnesbeck
		return 1;
1465 751533a2 Phil Davis
	} else if ($a == 'lan') {
1466 88bc2760 Erik Fonnesbeck
		return -1;
1467 751533a2 Phil Davis
	} else if ($b == 'lan') {
1468 88bc2760 Erik Fonnesbeck
		return 1;
1469 751533a2 Phil Davis
	}
1470 88bc2760 Erik Fonnesbeck
1471
	return strnatcmp($a, $b);
1472
}
1473
1474 c8abe1d4 Ermal Luçi
/* return the configured interfaces list. */
1475 80fe8369 Phil Davis
function get_configured_interface_list($withdisabled = false) {
1476 c8abe1d4 Ermal Luçi
	global $config;
1477
1478
	$iflist = array();
1479 14f49fd0 Erik Fonnesbeck
1480 c8abe1d4 Ermal Luçi
	/* if list */
1481 751533a2 Phil Davis
	foreach ($config['interfaces'] as $if => $ifdetail) {
1482
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1483 c8abe1d4 Ermal Luçi
			$iflist[$if] = $if;
1484 751533a2 Phil Davis
		}
1485 42c9d20e Ermal Luçi
	}
1486 c8abe1d4 Ermal Luçi
1487
	return $iflist;
1488
}
1489
1490 bb34737f Ermal Lu?i
/* return the configured interfaces list. */
1491 f625f76c Phil Davis
function get_configured_interface_list_by_realif($withdisabled = false) {
1492 8735afe8 Erik Fonnesbeck
	global $config;
1493 bb34737f Ermal Lu?i
1494 8735afe8 Erik Fonnesbeck
	$iflist = array();
1495 bb34737f Ermal Lu?i
1496 8735afe8 Erik Fonnesbeck
	/* if list */
1497 751533a2 Phil Davis
	foreach ($config['interfaces'] as $if => $ifdetail) {
1498 8735afe8 Erik Fonnesbeck
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1499 bb34737f Ermal Lu?i
			$tmpif = get_real_interface($if);
1500 751533a2 Phil Davis
			if (!empty($tmpif)) {
1501 bb34737f Ermal Lu?i
				$iflist[$tmpif] = $if;
1502 751533a2 Phil Davis
			}
1503 bb34737f Ermal Lu?i
		}
1504 8735afe8 Erik Fonnesbeck
	}
1505 bb34737f Ermal Lu?i
1506 8735afe8 Erik Fonnesbeck
	return $iflist;
1507 bb34737f Ermal Lu?i
}
1508
1509 c8abe1d4 Ermal Luçi
/* return the configured interfaces list with their description. */
1510 f593f80b Phil Davis
function get_configured_interface_with_descr($withdisabled = false) {
1511 1d3510cf Phil Davis
	global $config, $user_settings;
1512 c8abe1d4 Ermal Luçi
1513 a42d1da2 Scott Ullrich
	$iflist = array();
1514 c8abe1d4 Ermal Luçi
1515 a42d1da2 Scott Ullrich
	/* if list */
1516 751533a2 Phil Davis
	foreach ($config['interfaces'] as $if => $ifdetail) {
1517 47c8b036 Ermal Lu?i
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1518 751533a2 Phil Davis
			if (empty($ifdetail['descr'])) {
1519 8e74cb8d Ermal Luçi
				$iflist[$if] = strtoupper($if);
1520 751533a2 Phil Davis
			} else {
1521 44b0ec83 Scott Ullrich
				$iflist[$if] = strtoupper($ifdetail['descr']);
1522 751533a2 Phil Davis
			}
1523 0e218dc1 Ermal Luçi
		}
1524 42c9d20e Ermal Luçi
	}
1525 1d3510cf Phil Davis
1526
	if ($user_settings['webgui']['interfacessort']) {
1527
		asort($iflist);
1528
	}
1529
1530 a42d1da2 Scott Ullrich
	return $iflist;
1531 c8abe1d4 Ermal Luçi
}
1532
1533 4fe9c2dc Scott Ullrich
/*
1534
 *   get_configured_ip_addresses() - Return a list of all configured
1535 2a5960b0 Luiz Otavio O Souza
 *   IPv4 addresses.
1536 4fe9c2dc Scott Ullrich
 *
1537
 */
1538
function get_configured_ip_addresses() {
1539 5dbd619f smos
	global $config;
1540 a1e4e2a7 Ermal
1541 751533a2 Phil Davis
	if (!function_exists('get_interface_ip')) {
1542 a1e4e2a7 Ermal
		require_once("interfaces.inc");
1543 751533a2 Phil Davis
	}
1544 4fe9c2dc Scott Ullrich
	$ip_array = array();
1545
	$interfaces = get_configured_interface_list();
1546 a1e4e2a7 Ermal
	if (is_array($interfaces)) {
1547 751533a2 Phil Davis
		foreach ($interfaces as $int) {
1548 d9114ce0 Scott Ullrich
			$ipaddr = get_interface_ip($int);
1549
			$ip_array[$int] = $ipaddr;
1550
		}
1551 4fe9c2dc Scott Ullrich
	}
1552 2a5960b0 Luiz Otavio O Souza
	$interfaces = get_configured_vip_list('inet');
1553 751533a2 Phil Davis
	if (is_array($interfaces)) {
1554
		foreach ($interfaces as $int => $ipaddr) {
1555 d9114ce0 Scott Ullrich
			$ip_array[$int] = $ipaddr;
1556 751533a2 Phil Davis
		}
1557
	}
1558 5dbd619f smos
1559
	/* pppoe server */
1560 a1e4e2a7 Ermal
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
1561 751533a2 Phil Davis
		foreach ($config['pppoes']['pppoe'] as $pppoe) {
1562 5dbd619f smos
			if ($pppoe['mode'] == "server") {
1563 751533a2 Phil Davis
				if (is_ipaddr($pppoe['localip'])) {
1564 50b84727 Viktor G
					$int = "poes". $pppoe['pppoeid'];
1565 5dbd619f smos
					$ip_array[$int] = $pppoe['localip'];
1566
				}
1567
			}
1568
		}
1569
	}
1570 a1e4e2a7 Ermal
1571 4fe9c2dc Scott Ullrich
	return $ip_array;
1572
}
1573 c8abe1d4 Ermal Luçi
1574 e6f7e0be smos
/*
1575
 *   get_configured_ipv6_addresses() - Return a list of all configured
1576 2a5960b0 Luiz Otavio O Souza
 *   IPv6 addresses.
1577 e6f7e0be smos
 *
1578
 */
1579 6a53de6f NewEraCracker
function get_configured_ipv6_addresses($linklocal_fallback = false) {
1580 e6f7e0be smos
	require_once("interfaces.inc");
1581
	$ipv6_array = array();
1582
	$interfaces = get_configured_interface_list();
1583 751533a2 Phil Davis
	if (is_array($interfaces)) {
1584
		foreach ($interfaces as $int) {
1585 cde28bfa Phil Davis
			$ipaddrv6 = text_to_compressed_ip6(get_interface_ipv6($int, false, $linklocal_fallback));
1586 e6f7e0be smos
			$ipv6_array[$int] = $ipaddrv6;
1587
		}
1588
	}
1589 2a5960b0 Luiz Otavio O Souza
	$interfaces = get_configured_vip_list('inet6');
1590 751533a2 Phil Davis
	if (is_array($interfaces)) {
1591
		foreach ($interfaces as $int => $ipaddrv6) {
1592 cde28bfa Phil Davis
			$ipv6_array[$int] = text_to_compressed_ip6($ipaddrv6);
1593 751533a2 Phil Davis
		}
1594
	}
1595 e6f7e0be smos
	return $ipv6_array;
1596
}
1597
1598 36f546e9 Scott Ullrich
/*
1599
 *   get_interface_list() - Return a list of all physical interfaces
1600
 *   along with MAC and status.
1601
 *
1602
 *   $mode = "active" - use ifconfig -lu
1603
 *           "media"  - use ifconfig to check physical connection
1604
 *			status (much slower)
1605
 */
1606
function get_interface_list($mode = "active", $keyby = "physical", $vfaces = "") {
1607 86a5e1a8 Renato Botelho
	global $config;
1608 65bed2d2 Scott Ullrich
	$upints = array();
1609 86a5e1a8 Renato Botelho
	/* get a list of virtual interface types */
1610 751533a2 Phil Davis
	if (!$vfaces) {
1611 086cf944 Phil Davis
		$vfaces = array(
1612 9ce38409 Scott Ullrich
				'bridge',
1613
				'ppp',
1614 27c0c7c6 Ermal Lu?i
				'pppoe',
1615 50b84727 Viktor G
				'poes',
1616 27c0c7c6 Ermal Lu?i
				'pptp',
1617
				'l2tp',
1618 c150479c Viktor G
				'l2tps',
1619 9ce38409 Scott Ullrich
				'sl',
1620
				'gif',
1621 613571ea Ermal Luçi
				'gre',
1622 9ce38409 Scott Ullrich
				'faith',
1623
				'lo',
1624
				'ng',
1625 27616d6e Seth Mos
				'_vlan',
1626 7c53bc7b Erik Fonnesbeck
				'_wlan',
1627 9ce38409 Scott Ullrich
				'pflog',
1628 a42d1da2 Scott Ullrich
				'plip',
1629 9ce38409 Scott Ullrich
				'pfsync',
1630
				'enc',
1631
				'tun',
1632 1fb2bf25 Ermal Lu?i
				'lagg',
1633 1fd35e95 Ermal
				'vip',
1634 3856366b Renato Botelho do Couto
				'ipfw'
1635 f8cc5da5 Viktor G
		);
1636
	} else {
1637
		$vfaces = array(
1638
				'bridge',
1639 50b84727 Viktor G
				'poes',
1640 f8cc5da5 Viktor G
				'sl',
1641
				'faith',
1642
				'lo',
1643
				'ng',
1644
				'_vlan',
1645
				'_wlan',
1646
				'pflog',
1647
				'plip',
1648
				'pfsync',
1649
				'enc',
1650
				'tun',
1651
				'lagg',
1652
				'vip',
1653
				'ipfw',
1654 c150479c Viktor G
				'l2tps'
1655 9ce38409 Scott Ullrich
		);
1656 36f546e9 Scott Ullrich
	}
1657 751533a2 Phil Davis
	switch ($mode) {
1658
		case "active":
1659
			$upints = pfSense_interface_listget(IFF_UP);
1660
			break;
1661
		case "media":
1662
			$intlist = pfSense_interface_listget();
1663
			$ifconfig = "";
1664
			exec("/sbin/ifconfig -a", $ifconfig);
1665
			$regexp = '/(' . implode('|', $intlist) . '):\s/';
1666
			$ifstatus = preg_grep('/status:/', $ifconfig);
1667
			foreach ($ifstatus as $status) {
1668
				$int = array_shift($intlist);
1669
				if (stristr($status, "active")) {
1670
					$upints[] = $int;
1671
				}
1672
			}
1673
			break;
1674
		default:
1675
			$upints = pfSense_interface_listget();
1676
			break;
1677 20203646 Colin Smith
	}
1678 86a5e1a8 Renato Botelho
	/* build interface list with netstat */
1679
	$linkinfo = "";
1680
	exec("/usr/bin/netstat -inW -f link | awk '{ print $1, $4 }'", $linkinfo);
1681
	array_shift($linkinfo);
1682 89d1f0f2 Scott Ullrich
	/* build ip address list with netstat */
1683 767a716e Scott Ullrich
	$ipinfo = "";
1684 89d1f0f2 Scott Ullrich
	exec("/usr/bin/netstat -inW -f inet | awk '{ print $1, $4 }'", $ipinfo);
1685
	array_shift($ipinfo);
1686 751533a2 Phil Davis
	foreach ($linkinfo as $link) {
1687 89d1f0f2 Scott Ullrich
		$friendly = "";
1688 5bbd08e1 Warren Baker
		$alink = explode(" ", $link);
1689
		$ifname = rtrim(trim($alink[0]), '*');
1690
		/* trim out all numbers before checking for vfaces */
1691 4faf9170 Viktor G
		if (!in_array(array_shift(preg_split('/(\d-)*\d$/', $ifname)), $vfaces) &&
1692 c4865164 Renato Botelho
		    interface_is_vlan($ifname) == NULL &&
1693
		    interface_is_qinq($ifname) == NULL &&
1694 d764f8fc Viktor G
		    !stristr($ifname, "_wlan") &&
1695
		    !stristr($ifname, "_stf")) {
1696 20203646 Colin Smith
			$toput = array(
1697
					"mac" => trim($alink[1]),
1698
					"up" => in_array($ifname, $upints)
1699
				);
1700 751533a2 Phil Davis
			foreach ($ipinfo as $ip) {
1701 89d1f0f2 Scott Ullrich
				$aip = explode(" ", $ip);
1702 751533a2 Phil Davis
				if ($aip[0] == $ifname) {
1703 89d1f0f2 Scott Ullrich
					$toput['ipaddr'] = $aip[1];
1704
				}
1705
			}
1706 72993196 Ermal
			if (is_array($config['interfaces'])) {
1707 751533a2 Phil Davis
				foreach ($config['interfaces'] as $name => $int) {
1708
					if ($int['if'] == $ifname) {
1709
						$friendly = $name;
1710
					}
1711
				}
1712 20203646 Colin Smith
			}
1713 751533a2 Phil Davis
			switch ($keyby) {
1714 20203646 Colin Smith
			case "physical":
1715 751533a2 Phil Davis
				if ($friendly != "") {
1716 89d1f0f2 Scott Ullrich
					$toput['friendly'] = $friendly;
1717
				}
1718 a296c95d Seth Mos
				$dmesg_arr = array();
1719
				exec("/sbin/dmesg |grep $ifname | head -n1", $dmesg_arr);
1720
				preg_match_all("/<(.*?)>/i", $dmesg_arr[0], $dmesg);
1721
				$toput['dmesg'] = $dmesg[1][0];
1722 20203646 Colin Smith
				$iflist[$ifname] = $toput;
1723 3154d7ed Colin Smith
				break;
1724 4aca19b3 Scott Ullrich
			case "ppp":
1725 86a5e1a8 Renato Botelho
1726 20203646 Colin Smith
			case "friendly":
1727 751533a2 Phil Davis
				if ($friendly != "") {
1728 89d1f0f2 Scott Ullrich
					$toput['if'] = $ifname;
1729
					$iflist[$friendly] = $toput;
1730
				}
1731 3154d7ed Colin Smith
				break;
1732
			}
1733 5bbd08e1 Warren Baker
		}
1734
	}
1735
	return $iflist;
1736 5b237745 Scott Ullrich
}
1737
1738 f2286620 Luiz Souza
function get_lagg_interface_list() {
1739
	global $config;
1740
1741
	$plist = array();
1742
	if (isset($config['laggs']['lagg']) && is_array($config['laggs']['lagg'])) {
1743
		foreach ($config['laggs']['lagg'] as $lagg) {
1744
			$lagg['mac'] = get_interface_mac($lagg['laggif']);
1745
			$lagg['islagg'] = true;
1746
			$plist[$lagg['laggif']] = $lagg;
1747
		}
1748
	}
1749
1750
	return ($plist);
1751
}
1752
1753 2b4d37de Ermal Lu?i
/****f* util/log_error
1754
* NAME
1755
*   log_error  - Sends a string to syslog.
1756
* INPUTS
1757
*   $error     - string containing the syslog message.
1758
* RESULT
1759
*   null
1760
******/
1761
function log_error($error) {
1762 5bbd08e1 Warren Baker
	global $g;
1763
	$page = $_SERVER['SCRIPT_NAME'];
1764 866b1d61 jim-p
	if (empty($page)) {
1765
		$files = get_included_files();
1766
		$page = basename($files[0]);
1767
	}
1768 0d0cb047 jim-p
	syslog(LOG_ERR, "$page: $error");
1769 751533a2 Phil Davis
	if ($g['debug']) {
1770 5bbd08e1 Warren Baker
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1771 751533a2 Phil Davis
	}
1772 5bbd08e1 Warren Baker
	return;
1773 2b4d37de Ermal Lu?i
}
1774
1775 3aba1835 Scott Ullrich
/****f* util/log_auth
1776
* NAME
1777 1198abf9 PiBa-NL
*   log_auth   - Sends a string to syslog as LOG_AUTH facility
1778 3aba1835 Scott Ullrich
* INPUTS
1779
*   $error     - string containing the syslog message.
1780
* RESULT
1781
*   null
1782
******/
1783
function log_auth($error) {
1784 5bbd08e1 Warren Baker
	global $g;
1785
	$page = $_SERVER['SCRIPT_NAME'];
1786
	syslog(LOG_AUTH, "$page: $error");
1787 751533a2 Phil Davis
	if ($g['debug']) {
1788 5bbd08e1 Warren Baker
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1789 751533a2 Phil Davis
	}
1790 5bbd08e1 Warren Baker
	return;
1791 3aba1835 Scott Ullrich
}
1792
1793 83bc3749 Ermal Lu?i
/****f* util/exec_command
1794
 * NAME
1795
 *   exec_command - Execute a command and return a string of the result.
1796
 * INPUTS
1797
 *   $command   - String of the command to be executed.
1798
 * RESULT
1799
 *   String containing the command's result.
1800
 * NOTES
1801
 *   This function returns the command's stdout and stderr.
1802
 ******/
1803
function exec_command($command) {
1804 5bbd08e1 Warren Baker
	$output = array();
1805 873c1701 Renato Botelho
	exec($command . ' 2>&1', $output);
1806 5bbd08e1 Warren Baker
	return(implode("\n", $output));
1807 83bc3749 Ermal Lu?i
}
1808
1809 e00ad357 Renato Botelho
/* wrapper for exec()
1810 f0b41548 stilez
   Executes in background or foreground.
1811
   For background execution, returns PID of background process to allow calling code control */
1812
function mwexec($command, $nologentry = false, $clearsigmask = false, $background = false) {
1813 5b237745 Scott Ullrich
	global $g;
1814 f0b41548 stilez
	$retval = 0;
1815 435a418f Ermal
1816 5b237745 Scott Ullrich
	if ($g['debug']) {
1817 751533a2 Phil Davis
		if (!$_SERVER['REMOTE_ADDR']) {
1818 f0b41548 stilez
			echo "mwexec(): $command" . ($background ? " [BG]":"") . "\n";
1819 751533a2 Phil Davis
		}
1820 f9db3cda Seth Mos
	}
1821 b61e8960 jim-p
	if ($clearsigmask) {
1822
		$oldset = array();
1823
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1824
	}
1825 f0b41548 stilez
1826 2b1f6ed2 stilez
	if ($background) {
1827
		// start background process and return PID
1828
		$retval = exec("/usr/bin/nohup $command > /dev/null 2>&1 & echo $!");
1829
	} else {
1830
		// run in foreground, and (optionally) log if nonzero return
1831
		$outputarray = array();
1832 f0b41548 stilez
		exec("$command 2>&1", $outputarray, $retval);
1833 4e322e2c Phil Davis
		if (($retval <> 0) && (!$nologentry || isset($config['system']['developerspew']))) {
1834 f812b883 stilez
			log_error(sprintf(gettext("The command '%1\$s' returned exit code '%2\$d', the output was '%3\$s' "), $command, $retval, implode(" ", $outputarray)));
1835 4e322e2c Phil Davis
		}
1836 f0b41548 stilez
	}
1837
1838 b61e8960 jim-p
	if ($clearsigmask) {
1839
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1840
	}
1841 435a418f Ermal
1842 98bbf05a Scott Ullrich
	return $retval;
1843 5b237745 Scott Ullrich
}
1844
1845
/* wrapper for exec() in background */
1846 b61e8960 jim-p
function mwexec_bg($command, $clearsigmask = false) {
1847 f0b41548 stilez
	return mwexec($command, false, $clearsigmask, true);
1848 5b237745 Scott Ullrich
}
1849
1850 3673b6d0 Renato Botelho do Couto
/*
1851
 * Unlink a file, or pattern-match of a file, if it exists
1852
 *
1853
 * If the file/path contains glob() compatible wildcards, all matching files
1854
 * will be unlinked.
1855
 * Any warning/errors are suppressed (e.g. no matching files to delete)
1856
 * If there are matching file(s) and they were all unlinked OK, then return
1857
 * true.  Otherwise return false (the requested file(s) did not exist, or
1858
 * could not be deleted), this allows the caller to know if they were the one
1859
 * to successfully delete the file(s).
1860
 */
1861 5b237745 Scott Ullrich
function unlink_if_exists($fn) {
1862 336cb718 Scott Ullrich
	$to_do = glob($fn);
1863 a85ad858 stilez
	if (is_array($to_do) && count($to_do) > 0) {
1864 3673b6d0 Renato Botelho do Couto
		// Returns an array of boolean indicating if each unlink worked
1865 d96a39ba Phil Davis
		$results = @array_map("unlink", $to_do);
1866
		// If there is no false in the array, then all went well
1867
		$result = !in_array(false, $results, true);
1868 336cb718 Scott Ullrich
	} else {
1869 d96a39ba Phil Davis
		$result = @unlink($fn);
1870 336cb718 Scott Ullrich
	}
1871 d96a39ba Phil Davis
	return $result;
1872 5b237745 Scott Ullrich
}
1873 3673b6d0 Renato Botelho do Couto
1874 5b237745 Scott Ullrich
/* make a global alias table (for faster lookups) */
1875 dbc1b8ee jim-p
function alias_make_table() {
1876
	global $aliastable, $config;
1877 98bbf05a Scott Ullrich
1878 5b237745 Scott Ullrich
	$aliastable = array();
1879 98bbf05a Scott Ullrich
1880 dbc1b8ee jim-p
	init_config_arr(array('aliases', 'alias'));
1881
	foreach ($config['aliases']['alias'] as $alias) {
1882
		if ($alias['name']) {
1883
			$aliastable[$alias['name']] = $alias['address'];
1884 5b237745 Scott Ullrich
		}
1885
	}
1886
}
1887 5ffa3389 Ermal
1888 5b237745 Scott Ullrich
/* check if an alias exists */
1889
function is_alias($name) {
1890
	global $aliastable;
1891 98bbf05a Scott Ullrich
1892 5b237745 Scott Ullrich
	return isset($aliastable[$name]);
1893 b8014f9d Scott Ullrich
}
1894 27ff8a3c Scott Ullrich
1895 5ffa3389 Ermal
function alias_get_type($name) {
1896 86a5e1a8 Renato Botelho
	global $config;
1897
1898 5ffa3389 Ermal
	if (is_array($config['aliases']['alias'])) {
1899
		foreach ($config['aliases']['alias'] as $alias) {
1900 751533a2 Phil Davis
			if ($name == $alias['name']) {
1901 5ffa3389 Ermal
				return $alias['type'];
1902 751533a2 Phil Davis
			}
1903 5ffa3389 Ermal
		}
1904
	}
1905
1906 86a5e1a8 Renato Botelho
	return "";
1907 5ffa3389 Ermal
}
1908
1909 5b237745 Scott Ullrich
/* expand a host or network alias, if necessary */
1910
function alias_expand($name) {
1911 2ec7ab35 Chris Buechler
	global $config, $aliastable;
1912
	$urltable_prefix = "/var/db/aliastables/";
1913
	$urltable_filename = $urltable_prefix . $name . ".txt";
1914 98bbf05a Scott Ullrich
1915 751533a2 Phil Davis
	if (isset($aliastable[$name])) {
1916 a97a77a2 Phil Davis
		// alias names cannot be strictly numeric. redmine #4289
1917
		if (is_numericint($name)) {
1918
			return null;
1919
		}
1920 3673b6d0 Renato Botelho do Couto
		/*
1921
		 * make sure if it's a ports alias, it actually exists.
1922
		 * redmine #5845
1923
		 */
1924 2ec7ab35 Chris Buechler
		foreach ($config['aliases']['alias'] as $alias) {
1925
			if ($alias['name'] == $name) {
1926
				if ($alias['type'] == "urltable_ports") {
1927 3673b6d0 Renato Botelho do Couto
					if (is_URL($alias['url']) &&
1928
					    file_exists($urltable_filename) &&
1929
					    filesize($urltable_filename)) {
1930 2ec7ab35 Chris Buechler
						return "\${$name}";
1931
					} else {
1932
						return null;
1933
					}
1934
				}
1935
			}
1936
		}
1937 4335dc87 Bill Marquette
		return "\${$name}";
1938 3673b6d0 Renato Botelho do Couto
	} else if (is_ipaddr($name) || is_subnet($name) ||
1939
	    is_port_or_range($name)) {
1940 57989da5 Scott Ullrich
		return "{$name}";
1941 751533a2 Phil Davis
	} else {
1942 5b237745 Scott Ullrich
		return null;
1943 751533a2 Phil Davis
	}
1944 5b237745 Scott Ullrich
}
1945
1946 c7de8be4 jim-p
function alias_expand_urltable($name) {
1947
	global $config;
1948
	$urltable_prefix = "/var/db/aliastables/";
1949
	$urltable_filename = $urltable_prefix . $name . ".txt";
1950
1951 3673b6d0 Renato Botelho do Couto
	if (!is_array($config['aliases']['alias'])) {
1952
		return null;
1953
	}
1954
1955
	foreach ($config['aliases']['alias'] as $alias) {
1956
		if (!preg_match("/urltable/i", $alias['type']) ||
1957
		    ($alias['name'] != $name)) {
1958
			continue;
1959
		}
1960
1961
		if (is_URL($alias["url"]) && file_exists($urltable_filename)) {
1962
			if (!filesize($urltable_filename)) {
1963
				// file exists, but is empty, try to sync
1964
				send_event("service sync alias {$name}");
1965 5ffa3389 Ermal
			}
1966 3673b6d0 Renato Botelho do Couto
			return $urltable_filename;
1967
		} else {
1968
			send_event("service sync alias {$name}");
1969
			break;
1970 c7de8be4 jim-p
		}
1971
	}
1972
	return null;
1973
}
1974
1975 dd83f869 lukehamburg
/* obtain MAC address given an IP address by looking at the ARP/NDP table */
1976 20cf8d8e lukehamburg
function arp_get_mac_by_ip($ip, $do_ping = true) {
1977 6c2f0930 lukehamburg
	unset($macaddr);
1978
	$retval = 1;
1979 dd83f869 lukehamburg
	switch (is_ipaddr($ip)) {
1980
		case 4:
1981 c516cb28 lukehamburg
			if ($do_ping === true) {
1982
				mwexec("/sbin/ping -c 1 -t 1 " . escapeshellarg($ip), true);
1983
			}
1984 6c2f0930 lukehamburg
			$macaddr = exec("/usr/sbin/arp -n " . escapeshellarg($ip) . " | /usr/bin/awk '{print $4}'", $output, $retval);
1985 dd83f869 lukehamburg
			break;
1986
		case 6:
1987 c516cb28 lukehamburg
			if ($do_ping === true) {
1988
				mwexec("/sbin/ping6 -c 1 -X 1 " . escapeshellarg($ip), true);
1989
			}
1990 6c2f0930 lukehamburg
			$macaddr = exec("/usr/sbin/ndp -n " . escapeshellarg($ip) . " | /usr/bin/awk '{print $2}'", $output, $retval);
1991 dd83f869 lukehamburg
			break;
1992
	}
1993 6c2f0930 lukehamburg
	if ($retval == 0 && is_macaddr($macaddr)) {
1994
		return $macaddr;
1995
	} else {
1996
		return false;
1997 5b237745 Scott Ullrich
	}
1998
}
1999
2000 98bbf05a Scott Ullrich
/* return a fieldname that is safe for xml usage */
2001
function xml_safe_fieldname($fieldname) {
2002 4f3fc80d Renato Botelho
	$replace = array(
2003
	    '/', '-', ' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
2004
	    '_', '+', '=', '{', '}', '[', ']', '|', '/', '<', '>', '?',
2005
	    ':', ',', '.', '\'', '\\'
2006
	);
2007 ddce8ef2 Colin Smith
	return strtolower(str_replace($replace, "", $fieldname));
2008 98bbf05a Scott Ullrich
}
2009
2010 805b9ab6 Ermal
function mac_format($clientmac) {
2011 86a5e1a8 Renato Botelho
	global $config, $cpzone;
2012 4129df39 Scott Ullrich
2013 86a5e1a8 Renato Botelho
	$mac = explode(":", $clientmac);
2014
	$mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
2015 4129df39 Scott Ullrich
2016 751533a2 Phil Davis
	switch ($mac_format) {
2017
		case 'singledash':
2018
			return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
2019 4129df39 Scott Ullrich
2020 751533a2 Phil Davis
		case 'ietf':
2021
			return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
2022 4129df39 Scott Ullrich
2023 751533a2 Phil Davis
		case 'cisco':
2024
			return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
2025 4129df39 Scott Ullrich
2026 751533a2 Phil Davis
		case 'unformatted':
2027
			return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
2028 4129df39 Scott Ullrich
2029 751533a2 Phil Davis
		default:
2030
			return $clientmac;
2031 86a5e1a8 Renato Botelho
	}
2032 4129df39 Scott Ullrich
}
2033
2034 d3ac1cea Viktor G
function resolve_retry($hostname, $protocol = 'inet', $retries = 5, $numrecords = 1) {
2035 979cd6db Scott Ullrich
2036 d3ac1cea Viktor G
	$recresult = array();
2037
	$returnres = array();
2038 86a5e1a8 Renato Botelho
	for ($i = 0; $i < $retries; $i++) {
2039 d3ac1cea Viktor G
		switch ($protocol) {
2040
			case 'any':
2041
				$checkproto = 'is_ipaddr';
2042
				$dnsproto = DNS_ANY;
2043
				$dnstype = array('A', 'AAAA');
2044
				break;
2045
			case 'inet6':
2046
				$checkproto = 'is_ipaddrv6';
2047
				$dnsproto = DNS_AAAA;
2048
				$dnstype = array('AAAA');
2049
				break;
2050
			case 'inet': 
2051
			default:
2052
				$checkproto = 'is_ipaddrv4';
2053
				$dnsproto = DNS_A;
2054
				$dnstype = array('A');
2055
				break;
2056
		}
2057
		if ($checkproto($hostname)) {
2058
			return $hostname;
2059
		}
2060 8f85087b Viktor Gurov
		$dnsresult = @dns_get_record($hostname, $dnsproto);
2061
		if (!empty($dnsresult)) {
2062
			foreach ($dnsresult as $dnsrec => $ip) {
2063
				if (is_array($ip)) {
2064
					if (in_array($ip['type'], $dnstype)) {
2065
					    if ($checkproto($ip['ip'])) { 
2066
						    $recresult[] = $ip['ip'];
2067
					    }
2068
					    if ($checkproto($ip['ipv6'])) { 
2069
						    $recresult[] = $ip['ipv6'];
2070
					    }
2071
					}
2072 d3ac1cea Viktor G
				}
2073
			}
2074 5bbd08e1 Warren Baker
		}
2075 979cd6db Scott Ullrich
2076 5bbd08e1 Warren Baker
		sleep(1);
2077
	}
2078 979cd6db Scott Ullrich
2079 d3ac1cea Viktor G
	if (!empty($recresult)) {
2080
		if ($numrecords == 1) {
2081
			return $recresult[0];
2082
		} else {
2083
			return array_slice($recresult, 0, $numrecords);
2084
		}
2085
	}
2086
2087 5bbd08e1 Warren Baker
	return false;
2088 979cd6db Scott Ullrich
}
2089
2090 44bfd1fa Scott Ullrich
function format_bytes($bytes) {
2091 4eac105f Phil Davis
	if ($bytes >= 1099511627776) {
2092 7b512ab3 Phil Davis
		return sprintf("%.2f TiB", $bytes/1099511627776);
2093 4eac105f Phil Davis
	} else if ($bytes >= 1073741824) {
2094 7b512ab3 Phil Davis
		return sprintf("%.2f GiB", $bytes/1073741824);
2095 44bfd1fa Scott Ullrich
	} else if ($bytes >= 1048576) {
2096 7b512ab3 Phil Davis
		return sprintf("%.2f MiB", $bytes/1048576);
2097 44bfd1fa Scott Ullrich
	} else if ($bytes >= 1024) {
2098 7b512ab3 Phil Davis
		return sprintf("%.0f KiB", $bytes/1024);
2099 44bfd1fa Scott Ullrich
	} else {
2100 10ae204f Stephen Beaver
		return sprintf("%d B", $bytes);
2101 44bfd1fa Scott Ullrich
	}
2102
}
2103
2104 cc2cff0b Luiz Otavio O Souza
function format_number($num, $precision = 3) {
2105
	$units = array('', 'K', 'M', 'G', 'T');
2106
2107
	$i = 0;
2108
	while ($num > 1000 && $i < count($units)) {
2109
		$num /= 1000;
2110
		$i++;
2111
	}
2112 92130da3 Luiz Otavio O Souza
	$num = round($num, $precision);
2113 cc2cff0b Luiz Otavio O Souza
2114
	return ("$num {$units[$i]}");
2115
}
2116
2117 15c2e494 joshuasign
2118
function unformat_number($formated_num) {
2119
	$num = strtoupper($formated_num);
2120
    
2121
	if ( strpos($num,"T") !== false ) {
2122
		$num = str_replace("T","",$num) * 1000 * 1000 * 1000 * 1000;
2123
	} else if ( strpos($num,"G") !== false ) {
2124
		$num = str_replace("G","",$num) * 1000 * 1000 * 1000;
2125
	} else if ( strpos($num,"M") !== false ) {
2126
		$num = str_replace("M","",$num) * 1000 * 1000;
2127
	} else if ( strpos($num,"K") !== false ) {
2128
		$num = str_replace("K","",$num) * 1000;
2129
	}
2130
    
2131
	return $num;
2132
}
2133
2134 4f7956ad Steve Beaver
function update_filter_reload_status($text, $new=false) {
2135 5bbd08e1 Warren Baker
	global $g;
2136 2b4d37de Ermal Lu?i
2137 4f7956ad Steve Beaver
	if ($new) {
2138
		file_put_contents("{$g['varrun_path']}/filter_reload_status", $text  . PHP_EOL);
2139
	} else {
2140
		file_put_contents("{$g['varrun_path']}/filter_reload_status", $text  . PHP_EOL, FILE_APPEND);
2141
	}
2142 2b4d37de Ermal Lu?i
}
2143
2144 a2219caf Renato Botelho
/****** util/return_dir_as_array
2145 2b4d37de Ermal Lu?i
 * NAME
2146
 *   return_dir_as_array - Return a directory's contents as an array.
2147
 * INPUTS
2148 a2219caf Renato Botelho
 *   $dir          - string containing the path to the desired directory.
2149
 *   $filter_regex - string containing a regular expression to filter file names. Default empty.
2150 2b4d37de Ermal Lu?i
 * RESULT
2151
 *   $dir_array - array containing the directory's contents. This array will be empty if the path specified is invalid.
2152
 ******/
2153 a2219caf Renato Botelho
function return_dir_as_array($dir, $filter_regex = '') {
2154 5bbd08e1 Warren Baker
	$dir_array = array();
2155
	if (is_dir($dir)) {
2156
		if ($dh = opendir($dir)) {
2157
			while (($file = readdir($dh)) !== false) {
2158 751533a2 Phil Davis
				if (($file == ".") || ($file == "..")) {
2159 a2219caf Renato Botelho
					continue;
2160 751533a2 Phil Davis
				}
2161 a2219caf Renato Botelho
2162 751533a2 Phil Davis
				if (empty($filter_regex) || preg_match($filter_regex, $file)) {
2163 5bbd08e1 Warren Baker
					array_push($dir_array, $file);
2164 751533a2 Phil Davis
				}
2165 5bbd08e1 Warren Baker
			}
2166
			closedir($dh);
2167
		}
2168
	}
2169
	return $dir_array;
2170 2b4d37de Ermal Lu?i
}
2171
2172
function run_plugins($directory) {
2173 5bbd08e1 Warren Baker
	global $config, $g;
2174
2175
	/* process packager manager custom rules */
2176
	$files = return_dir_as_array($directory);
2177
	if (is_array($files)) {
2178
		foreach ($files as $file) {
2179 751533a2 Phil Davis
			if (stristr($file, ".sh") == true) {
2180 5bbd08e1 Warren Baker
				mwexec($directory . $file . " start");
2181 086cf944 Phil Davis
			} else if (!is_dir($directory . "/" . $file) && stristr($file, ".inc")) {
2182 5bbd08e1 Warren Baker
				require_once($directory . "/" . $file);
2183 751533a2 Phil Davis
			}
2184 2990acf8 Scott Ullrich
		}
2185 5bbd08e1 Warren Baker
	}
2186 2b4d37de Ermal Lu?i
}
2187
2188
/*
2189
 *    safe_mkdir($path, $mode = 0755)
2190
 *    create directory if it doesn't already exist and isn't a file!
2191
 */
2192 6c07db48 Phil Davis
function safe_mkdir($path, $mode = 0755) {
2193 5bbd08e1 Warren Baker
	global $g;
2194 2b4d37de Ermal Lu?i
2195 5bbd08e1 Warren Baker
	if (!is_file($path) && !is_dir($path)) {
2196
		return @mkdir($path, $mode, true);
2197
	} else {
2198
		return false;
2199
	}
2200 2b4d37de Ermal Lu?i
}
2201
2202 aa4f498d Erik Fonnesbeck
/*
2203
 * get_sysctl($names)
2204
 * Get values of sysctl OID's listed in $names (accepts an array or a single
2205
 * name) and return an array of key/value pairs set for those that exist
2206
 */
2207
function get_sysctl($names) {
2208 751533a2 Phil Davis
	if (empty($names)) {
2209 aa4f498d Erik Fonnesbeck
		return array();
2210 751533a2 Phil Davis
	}
2211 aa4f498d Erik Fonnesbeck
2212
	if (is_array($names)) {
2213
		$name_list = array();
2214
		foreach ($names as $name) {
2215
			$name_list[] = escapeshellarg($name);
2216
		}
2217 751533a2 Phil Davis
	} else {
2218 aa4f498d Erik Fonnesbeck
		$name_list = array(escapeshellarg($names));
2219 751533a2 Phil Davis
	}
2220 aa4f498d Erik Fonnesbeck
2221 3c44c845 Luiz Souza
	exec("/sbin/sysctl -iq " . implode(" ", $name_list), $output);
2222 aa4f498d Erik Fonnesbeck
	$values = array();
2223
	foreach ($output as $line) {
2224
		$line = explode(": ", $line, 2);
2225 751533a2 Phil Davis
		if (count($line) == 2) {
2226 aa4f498d Erik Fonnesbeck
			$values[$line[0]] = $line[1];
2227 751533a2 Phil Davis
		}
2228 aa4f498d Erik Fonnesbeck
	}
2229
2230
	return $values;
2231
}
2232
2233 ff23363d Renato Botelho
/*
2234
 * get_single_sysctl($name)
2235
 * Wrapper for get_sysctl() to simplify read of a single sysctl value
2236
 * return the value for sysctl $name or empty string if it doesn't exist
2237
 */
2238
function get_single_sysctl($name) {
2239 751533a2 Phil Davis
	if (empty($name)) {
2240 ff23363d Renato Botelho
		return "";
2241 751533a2 Phil Davis
	}
2242 ff23363d Renato Botelho
2243
	$value = get_sysctl($name);
2244 751533a2 Phil Davis
	if (empty($value) || !isset($value[$name])) {
2245 ff23363d Renato Botelho
		return "";
2246 751533a2 Phil Davis
	}
2247 ff23363d Renato Botelho
2248
	return $value[$name];
2249
}
2250
2251 aa4f498d Erik Fonnesbeck
/*
2252
 * set_sysctl($value_list)
2253
 * Set sysctl OID's listed as key/value pairs and return
2254
 * an array with keys set for those that succeeded
2255
 */
2256
function set_sysctl($values) {
2257 751533a2 Phil Davis
	if (empty($values)) {
2258 aa4f498d Erik Fonnesbeck
		return array();
2259 751533a2 Phil Davis
	}
2260 aa4f498d Erik Fonnesbeck
2261
	$value_list = array();
2262
	foreach ($values as $key => $value) {
2263
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
2264
	}
2265
2266 3c44c845 Luiz Souza
	exec("/sbin/sysctl -iq " . implode(" ", $value_list), $output, $success);
2267 aa4f498d Erik Fonnesbeck
2268
	/* Retry individually if failed (one or more read-only) */
2269
	if ($success <> 0 && count($value_list) > 1) {
2270
		foreach ($value_list as $value) {
2271 3c44c845 Luiz Souza
			exec("/sbin/sysctl -iq " . $value, $output);
2272 aa4f498d Erik Fonnesbeck
		}
2273
	}
2274
2275
	$ret = array();
2276
	foreach ($output as $line) {
2277
		$line = explode(": ", $line, 2);
2278 751533a2 Phil Davis
		if (count($line) == 2) {
2279 aa4f498d Erik Fonnesbeck
			$ret[$line[0]] = true;
2280 751533a2 Phil Davis
		}
2281 aa4f498d Erik Fonnesbeck
	}
2282
2283
	return $ret;
2284
}
2285
2286 82f75815 Renato Botelho
/*
2287
 * set_single_sysctl($name, $value)
2288
 * Wrapper to set_sysctl() to make it simple to set only one sysctl
2289 751533a2 Phil Davis
 * returns boolean meaning if it succeeded
2290 82f75815 Renato Botelho
 */
2291
function set_single_sysctl($name, $value) {
2292 751533a2 Phil Davis
	if (empty($name)) {
2293 82f75815 Renato Botelho
		return false;
2294 751533a2 Phil Davis
	}
2295 82f75815 Renato Botelho
2296
	$result = set_sysctl(array($name => $value));
2297
2298 751533a2 Phil Davis
	if (!isset($result[$name]) || $result[$name] != $value) {
2299 82f75815 Renato Botelho
		return false;
2300 751533a2 Phil Davis
	}
2301 82f75815 Renato Botelho
2302
	return true;
2303
}
2304
2305 2b4d37de Ermal Lu?i
/*
2306
 *     get_memory()
2307
 *     returns an array listing the amount of
2308
 *     memory installed in the hardware
2309 517fb89e Phil Davis
 *     [0] net memory available for the OS (FreeBSD) after some is taken by BIOS, video or whatever - e.g. 235 MBytes
2310
 *     [1] real (actual) memory of the system, should be the size of the RAM card/s - e.g. 256 MBytes
2311 2b4d37de Ermal Lu?i
 */
2312
function get_memory() {
2313 971de1f9 Renato Botelho
	$physmem = get_single_sysctl("hw.physmem");
2314
	$realmem = get_single_sysctl("hw.realmem");
2315 5cd73772 Ermal
	/* convert from bytes to megabytes */
2316 086cf944 Phil Davis
	return array(($physmem/1048576), ($realmem/1048576));
2317 2b4d37de Ermal Lu?i
}
2318
2319
function mute_kernel_msgs() {
2320 6fa9f38c Renato Botelho
	global $g, $config;
2321 dc61252a Renato Botelho
2322 751533a2 Phil Davis
	if ($config['system']['enableserial']) {
2323 86a5e1a8 Renato Botelho
		return;
2324 751533a2 Phil Davis
	}
2325 5bbd08e1 Warren Baker
	exec("/sbin/conscontrol mute on");
2326 2b4d37de Ermal Lu?i
}
2327
2328
function unmute_kernel_msgs() {
2329 6fa9f38c Renato Botelho
	global $g;
2330 dc61252a Renato Botelho
2331 5bbd08e1 Warren Baker
	exec("/sbin/conscontrol mute off");
2332 2b4d37de Ermal Lu?i
}
2333
2334
function start_devd() {
2335 505e3e0e Renato Botelho
	global $g;
2336
2337 91677c09 Luiz Souza
	/* Generate hints for the kernel loader. */
2338
	$module_paths = explode(";", get_single_sysctl("kern.module_path"));
2339
	foreach ($module_paths as $id => $path) {
2340 d36cf2c9 jim-p
		if (!is_dir($path) || file_exists("{$path}/linker.hints")) {
2341 91677c09 Luiz Souza
			continue;
2342
		}
2343
		if (($files = scandir($path)) == false) {
2344
			continue;
2345
		}
2346
		$found = false;
2347
		foreach ($files as $id => $file) {
2348
			if (strlen($file) > 3 &&
2349
			    strcasecmp(substr($file, -3), ".ko") == 0) {
2350
				$found = true;
2351
				break;
2352
			}
2353
		}
2354
		if ($found == false) {
2355
			continue;
2356
		}
2357
		$_gb = exec("/usr/sbin/kldxref $path");
2358
		unset($_gb);
2359
	}
2360
2361 751533a2 Phil Davis
	/* Use the undocumented -q options of devd to quiet its log spamming */
2362 505e3e0e Renato Botelho
	$_gb = exec("/sbin/devd -q -f /etc/{$g['product_name']}-devd.conf");
2363 5bbd08e1 Warren Baker
	sleep(1);
2364 a7f79eda Ermal LUÇI
	unset($_gb);
2365 2b4d37de Ermal Lu?i
}
2366
2367 66bcba1b Ermal
function is_interface_vlan_mismatch() {
2368 5bbd08e1 Warren Baker
	global $config, $g;
2369 66bcba1b Ermal
2370 5bbd08e1 Warren Baker
	if (is_array($config['vlans']['vlan'])) {
2371
		foreach ($config['vlans']['vlan'] as $vlan) {
2372 2915acf8 Chris Buechler
			if (substr($vlan['if'], 0, 4) == "lagg") {
2373
				return false;
2374
			}
2375 751533a2 Phil Davis
			if (does_interface_exist($vlan['if']) == false) {
2376 66bcba1b Ermal
				return true;
2377 751533a2 Phil Davis
			}
2378 5bbd08e1 Warren Baker
		}
2379
	}
2380 66bcba1b Ermal
2381
	return false;
2382
}
2383
2384 2b4d37de Ermal Lu?i
function is_interface_mismatch() {
2385 857da904 Scott Ullrich
	global $config, $g;
2386 2b4d37de Ermal Lu?i
2387 857da904 Scott Ullrich
	$do_assign = false;
2388
	$i = 0;
2389 e0a45ce0 Erik Fonnesbeck
	$missing_interfaces = array();
2390 72993196 Ermal
	if (is_array($config['interfaces'])) {
2391 857da904 Scott Ullrich
		foreach ($config['interfaces'] as $ifname => $ifcfg) {
2392 12bcf7e9 Luiz Souza
			if (interface_is_vlan($ifcfg['if']) != NULL ||
2393 0793de1a Luiz Souza
			    interface_is_qinq($ifcfg['if']) != NULL ||
2394 281dede0 Renato Botelho do Couto
			    preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^ipsec|^gif|^gre|^lagg|^bridge|vlan|_wlan|_\d{0,4}_\d{0,4}$/i", $ifcfg['if'])) {
2395 857da904 Scott Ullrich
				// Do not check these interfaces.
2396
				$i++;
2397
				continue;
2398 751533a2 Phil Davis
			} else if (does_interface_exist($ifcfg['if']) == false) {
2399 e0a45ce0 Erik Fonnesbeck
				$missing_interfaces[] = $ifcfg['if'];
2400 72993196 Ermal
				$do_assign = true;
2401 751533a2 Phil Davis
			} else {
2402 857da904 Scott Ullrich
				$i++;
2403 751533a2 Phil Davis
			}
2404 857da904 Scott Ullrich
		}
2405 72993196 Ermal
	}
2406 2b4d37de Ermal Lu?i
2407 751533a2 Phil Davis
	if (file_exists("{$g['tmp_path']}/assign_complete")) {
2408 e0a45ce0 Erik Fonnesbeck
		$do_assign = false;
2409 751533a2 Phil Davis
	}
2410 e0a45ce0 Erik Fonnesbeck
2411 751533a2 Phil Davis
	if (!empty($missing_interfaces) && $do_assign) {
2412 e0a45ce0 Erik Fonnesbeck
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
2413 751533a2 Phil Davis
	} else {
2414 e0a45ce0 Erik Fonnesbeck
		@unlink("{$g['tmp_path']}/missing_interfaces");
2415 751533a2 Phil Davis
	}
2416 2b4d37de Ermal Lu?i
2417 857da904 Scott Ullrich
	return $do_assign;
2418 2b4d37de Ermal Lu?i
}
2419
2420 6e8f7b53 Ermal Lu?i
/* sync carp entries to other firewalls */
2421
function carp_sync_client() {
2422 e14d1c01 Ermal Lu?i
	global $g;
2423 0ae6daf8 Ermal
	send_event("filter sync");
2424 6e8f7b53 Ermal Lu?i
}
2425
2426 6dc88d53 Ermal Luci
/****f* util/isAjax
2427
 * NAME
2428
 *   isAjax - reports if the request is driven from prototype
2429
 * INPUTS
2430
 *   none
2431
 * RESULT
2432
 *   true/false
2433
 ******/
2434
function isAjax() {
2435 5bbd08e1 Warren Baker
	return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
2436 6dc88d53 Ermal Luci
}
2437
2438 dad2b40e Tim Allender
/****f* util/timeout
2439
 * NAME
2440
 *   timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space.
2441
 * INPUTS
2442
 *   optional, seconds to wait before timeout. Default 9 seconds.
2443
 * RESULT
2444
 *   returns 1 char of user input or null if no input.
2445
 ******/
2446
function timeout($timer = 9) {
2447 751533a2 Phil Davis
	while (!isset($key)) {
2448
		if ($timer >= 9) {
2449 6c07db48 Phil Davis
			echo chr(8) . chr(8) . ($timer == 9 ? chr(32) : null) . "{$timer}";
2450 751533a2 Phil Davis
		} else {
2451
			echo chr(8). "{$timer}";
2452
		}
2453 dad2b40e Tim Allender
		`/bin/stty -icanon min 0 time 25`;
2454
		$key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`);
2455
		`/bin/stty icanon`;
2456 751533a2 Phil Davis
		if ($key == '') {
2457 dad2b40e Tim Allender
			unset($key);
2458 751533a2 Phil Davis
		}
2459 dad2b40e Tim Allender
		$timer--;
2460 751533a2 Phil Davis
		if ($timer == 0) {
2461 dad2b40e Tim Allender
			break;
2462 751533a2 Phil Davis
		}
2463 dad2b40e Tim Allender
	}
2464 86a5e1a8 Renato Botelho
	return $key;
2465 dad2b40e Tim Allender
}
2466 6dc88d53 Ermal Luci
2467 fdf3af3f Scott Ullrich
/****f* util/msort
2468
 * NAME
2469
 *   msort - sort array
2470
 * INPUTS
2471
 *   $array to be sorted, field to sort by, direction of sort
2472
 * RESULT
2473
 *   returns newly sorted array
2474
 ******/
2475 6c07db48 Phil Davis
function msort($array, $id = "id", $sort_ascending = true) {
2476 4a8bc5a2 Scott Ullrich
	$temp_array = array();
2477 a47598aa Renato Botelho
	if (!is_array($array)) {
2478
		return $temp_array;
2479
	}
2480 751533a2 Phil Davis
	while (count($array)>0) {
2481 4a8bc5a2 Scott Ullrich
		$lowest_id = 0;
2482 6c07db48 Phil Davis
		$index = 0;
2483 4a8bc5a2 Scott Ullrich
		foreach ($array as $item) {
2484
			if (isset($item[$id])) {
2485
				if ($array[$lowest_id][$id]) {
2486
					if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
2487
						$lowest_id = $index;
2488
					}
2489
				}
2490
			}
2491
			$index++;
2492
		}
2493
		$temp_array[] = $array[$lowest_id];
2494 086cf944 Phil Davis
		$array = array_merge(array_slice($array, 0, $lowest_id), array_slice($array, $lowest_id + 1));
2495 4a8bc5a2 Scott Ullrich
	}
2496
	if ($sort_ascending) {
2497
		return $temp_array;
2498
	} else {
2499 86a5e1a8 Renato Botelho
		return array_reverse($temp_array);
2500 4a8bc5a2 Scott Ullrich
	}
2501
}
2502
2503 5e9dd72a sullrich
/****f* util/is_URL
2504
 * NAME
2505
 *   is_URL
2506
 * INPUTS
2507
 *   string to check
2508
 * RESULT
2509
 *   Returns true if item is a URL
2510
 ******/
2511
function is_URL($url) {
2512
	$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
2513 751533a2 Phil Davis
	if ($match) {
2514 86a5e1a8 Renato Botelho
		return true;
2515 751533a2 Phil Davis
	}
2516 5e9dd72a sullrich
	return false;
2517
}
2518
2519 ab94ba00 Ermal Lu?i
function is_file_included($file = "") {
2520
	$files = get_included_files();
2521 751533a2 Phil Davis
	if (in_array($file, $files)) {
2522 ab94ba00 Ermal Lu?i
		return true;
2523 751533a2 Phil Davis
	}
2524 86a5e1a8 Renato Botelho
2525 ab94ba00 Ermal Lu?i
	return false;
2526
}
2527
2528 f2cc3344 Renato Botelho
/*
2529
 * Replace a value on a deep associative array using regex
2530
 */
2531
function array_replace_values_recursive($data, $match, $replace) {
2532 751533a2 Phil Davis
	if (empty($data)) {
2533 f2cc3344 Renato Botelho
		return $data;
2534 751533a2 Phil Davis
	}
2535 f2cc3344 Renato Botelho
2536 751533a2 Phil Davis
	if (is_string($data)) {
2537 f2cc3344 Renato Botelho
		$data = preg_replace("/{$match}/", $replace, $data);
2538 751533a2 Phil Davis
	} else if (is_array($data)) {
2539
		foreach ($data as $k => $v) {
2540 f2cc3344 Renato Botelho
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
2541 751533a2 Phil Davis
		}
2542
	}
2543 f2cc3344 Renato Botelho
2544
	return $data;
2545
}
2546
2547 0d90fcaf jim-p
/*
2548
	This function was borrowed from a comment on PHP.net at the following URL:
2549
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
2550
 */
2551 5bbd08e1 Warren Baker
function array_merge_recursive_unique($array0, $array1) {
2552
2553
	$arrays = func_get_args();
2554
	$remains = $arrays;
2555 0d90fcaf jim-p
2556 5bbd08e1 Warren Baker
	// We walk through each arrays and put value in the results (without
2557
	// considering previous value).
2558
	$result = array();
2559 0d90fcaf jim-p
2560 5bbd08e1 Warren Baker
	// loop available array
2561 751533a2 Phil Davis
	foreach ($arrays as $array) {
2562 0d90fcaf jim-p
2563 5bbd08e1 Warren Baker
		// The first remaining array is $array. We are processing it. So
2564 751533a2 Phil Davis
		// we remove it from remaining arrays.
2565 86a5e1a8 Renato Botelho
		array_shift($remains);
2566 0d90fcaf jim-p
2567 5bbd08e1 Warren Baker
		// We don't care non array param, like array_merge since PHP 5.0.
2568 751533a2 Phil Davis
		if (is_array($array)) {
2569 5bbd08e1 Warren Baker
			// Loop values
2570 751533a2 Phil Davis
			foreach ($array as $key => $value) {
2571
				if (is_array($value)) {
2572 5bbd08e1 Warren Baker
					// we gather all remaining arrays that have such key available
2573
					$args = array();
2574 751533a2 Phil Davis
					foreach ($remains as $remain) {
2575
						if (array_key_exists($key, $remain)) {
2576 5bbd08e1 Warren Baker
							array_push($args, $remain[$key]);
2577
						}
2578
					}
2579
2580 751533a2 Phil Davis
					if (count($args) > 2) {
2581 5bbd08e1 Warren Baker
						// put the recursion
2582
						$result[$key] = call_user_func_array(__FUNCTION__, $args);
2583
					} else {
2584 751533a2 Phil Davis
						foreach ($value as $vkey => $vval) {
2585 b2c97ede jim-p
							if (!is_array($result[$key])) {
2586
								$result[$key] = array();
2587
							}
2588 5bbd08e1 Warren Baker
							$result[$key][$vkey] = $vval;
2589
						}
2590
					}
2591
				} else {
2592
					// simply put the value
2593
					$result[$key] = $value;
2594
				}
2595
			}
2596
		}
2597
	}
2598
	return $result;
2599 0d90fcaf jim-p
}
2600
2601 f898c1a9 jim-p
2602 9a456170 Darren Embry
/*
2603
 * converts a string like "a,b,c,d"
2604
 * into an array like array("a" => "b", "c" => "d")
2605
 */
2606
function explode_assoc($delimiter, $string) {
2607
	$array = explode($delimiter, $string);
2608
	$result = array();
2609
	$numkeys = floor(count($array) / 2);
2610
	for ($i = 0; $i < $numkeys; $i += 1) {
2611
		$result[$array[$i * 2]] = $array[$i * 2 + 1];
2612
	}
2613
	return $result;
2614
}
2615
2616 bea18841 Phil Davis
/*
2617
 * Given a string of text with some delimiter, look for occurrences
2618
 * of some string and replace all of those.
2619
 * $text - the text string (e.g. "abc,defg,x123,ipv4,xyz")
2620
 * $delimiter - the delimiter (e.g. ",")
2621
 * $element - the element to match (e.g. "defg")
2622
 * $replacement - the string to replace it with (e.g. "42")
2623
 * Returns the resulting delimited string (e.g. "abc,42,x123,ipv4,xyz")
2624
 */
2625
function replace_element_in_list($text, $delimiter, $element, $replacement) {
2626
	$textArray = explode($delimiter, $text);
2627
	while (($entry = array_search($element, $textArray)) !== false) {
2628
		$textArray[$entry] = $replacement;
2629
	}
2630
	return implode(',', $textArray);
2631
}
2632
2633 c428cdf4 Renato Botelho do Couto
/* Return system's route table */
2634
function route_table() {
2635
	$_gb = exec("/usr/bin/netstat --libxo json -nWr", $rawdata, $rc);
2636
2637
	if ($rc != 0) {
2638
		return array();
2639
	}
2640
2641
	$netstatarr = json_decode(implode(" ", $rawdata), JSON_OBJECT_AS_ARRAY);
2642
	$netstatarr = $netstatarr['statistics']['route-information']
2643
	    ['route-table']['rt-family'];
2644
2645
	$result = array();
2646
	$result['inet'] = array();
2647
	$result['inet6'] = array();
2648
	foreach ($netstatarr as $item) {
2649
		if ($item['address-family'] == 'Internet') {
2650
			$result['inet'] = $item['rt-entry'];
2651
		} else if ($item['address-family'] == 'Internet6') {
2652
			$result['inet6'] = $item['rt-entry'];
2653
		}
2654
	}
2655
	unset($netstatarr);
2656
2657
	return $result;
2658
}
2659
2660
/* Get static route for specific destination */
2661 7990de53 Viktor G
function route_get($target, $ipprotocol = '', $useroute = false) {
2662
	global $config;
2663
2664 c428cdf4 Renato Botelho do Couto
	if (!empty($ipprotocol)) {
2665
		$family = $ipprotocol;
2666
	} else if (is_v4($target)) {
2667
		$family = 'inet';
2668
	} else if (is_v6($target)) {
2669
		$family = 'inet6';
2670
	}
2671
2672
	if (empty($family)) {
2673
		return array();
2674
	}
2675
2676 7990de53 Viktor G
	if ($useroute) {
2677
		if ($family == 'inet') {
2678
			$inet = '4';
2679
		} else {
2680
			$inet = '6';
2681
		}
2682 46ff02ac Viktor G
		$interface = exec("/sbin/route -n{$inet} get {$target} 2>/dev/null | /usr/bin/awk '/interface:/{print $2}'");
2683 7990de53 Viktor G
		if (empty($interface)) {
2684
			return array();
2685
		} elseif ($interface == 'lo0') {
2686
			// interface assigned IP address
2687
			foreach ($config['interfaces'] as $intf => $infconf) {
2688
				if ((($inet == '4') && (get_interface_ip($intf) == $target)) ||
2689
				    (($inet == '6') && (get_interface_ipv6($intf) == $target))) {
2690
					$interface = convert_friendly_interface_to_real_interface_name($intf);
2691
					$gateway = $interface;
2692
					break;
2693
				}
2694
			}
2695
		} else {
2696 46ff02ac Viktor G
			$gateway = exec("/sbin/route -n{$inet} get {$target} 2>/dev/null | /usr/bin/awk '/gateway:/{print $2}'");
2697 07b780c8 Viktor G
			if (!$gateway) {
2698
				// non-local gateway
2699
				$gateway = get_interface_mac($interface);
2700
			}
2701 7990de53 Viktor G
		}
2702
		$result[] = array('gateway' => $gateway, 'interface-name' => $interface);
2703
	} else {
2704
		$rtable = route_table();
2705
		if (empty($rtable)) {
2706
			return array();
2707
		}
2708 ffe95182 Renato Botelho do Couto
2709 7990de53 Viktor G
		$result = array();
2710
		foreach ($rtable[$family] as $item) {
2711
			if ($item['destination'] == $target ||
2712
			    ip_in_subnet($target, $item['destination'])) {
2713
				$result[] = $item;
2714
			}
2715 c428cdf4 Renato Botelho do Couto
		}
2716
	}
2717
2718
	return $result;
2719
}
2720
2721
/* Get default route */
2722
function route_get_default($ipprotocol) {
2723
	if (empty($ipprotocol) || ($ipprotocol != 'inet' &&
2724
	    $ipprotocol != 'inet6')) {
2725
		return '';
2726
	}
2727
2728 7990de53 Viktor G
	$route = route_get('default', $ipprotocol, true);
2729 c428cdf4 Renato Botelho do Couto
2730
	if (empty($route)) {
2731
		return '';
2732
	}
2733
2734
	if (!isset($route[0]['gateway'])) {
2735
		return '';
2736
	}
2737
2738
	return $route[0]['gateway'];
2739
}
2740
2741
/* Delete a static route */
2742
function route_del($target, $ipprotocol = '') {
2743 94bd7fb3 Renato Botelho
	global $config;
2744
2745 c428cdf4 Renato Botelho do Couto
	if (empty($target)) {
2746
		return;
2747
	}
2748
2749
	if (!empty($ipprotocol) && $ipprotocol != 'inet' &&
2750
	    $ipprotocol != 'inet6') {
2751 94bd7fb3 Renato Botelho
		return false;
2752
	}
2753
2754 07b780c8 Viktor G
	$route = route_get($target, $ipprotocol, true);
2755 c428cdf4 Renato Botelho do Couto
2756
	if (empty($route)) {
2757
		return;
2758 94bd7fb3 Renato Botelho
	}
2759
2760 c428cdf4 Renato Botelho do Couto
	$target_prefix = '';
2761
	if (is_subnet($target)) {
2762
		$target_prefix = '-net';
2763
	} else if (is_ipaddr($target)) {
2764
		$target_prefix = '-host';
2765
	}
2766
2767
	if (!empty($ipprotocol)) {
2768
		$target_prefix .= " -{$ipprotocol}";
2769
	} else if (is_v6($target)) {
2770
		$target_prefix .= ' -inet6';
2771
	} else if (is_v4($target)) {
2772
		$target_prefix .= ' -inet';
2773
	}
2774
2775
	foreach ($route as $item) {
2776
		if (substr($item['gateway'], 0, 5) == 'link#') {
2777
			continue;
2778
		}
2779
2780
		if (is_macaddr($item['gateway'])) {
2781
			$gw = '-iface ' . $item['interface-name'];
2782
		} else {
2783
			$gw = $item['gateway'];
2784
		}
2785
2786
		$_gb = exec(escapeshellcmd("/sbin/route del {$target_prefix} " .
2787
		    "{$target} {$gw}"), $output, $rc);
2788 94bd7fb3 Renato Botelho
2789
		if (isset($config['system']['route-debug'])) {
2790 c428cdf4 Renato Botelho do Couto
			log_error("ROUTING debug: " . microtime() .
2791
			    " - DEL RC={$rc} - {$target} - gw: " . $gw);
2792
			file_put_contents("/dev/console", "\n[" . getmypid() .
2793
			    "] ROUTE DEL: {$target_prefix} {$target} {$gw} " .
2794
			    "result: {$rc}");
2795 94bd7fb3 Renato Botelho
		}
2796
	}
2797 c428cdf4 Renato Botelho do Couto
}
2798
2799
/*
2800
 * Add static route.  If it already exists, remove it and re-add
2801
 *
2802
 * $target - IP, subnet or 'default'
2803
 * $gw     - gateway address
2804
 * $iface  - Network interface
2805
 * $args   - Extra arguments for /sbin/route
2806
 * $ipprotocol - 'inet' or 'inet6'.  Mandatory when $target == 'default'
2807
 *
2808
 */
2809
function route_add_or_change($target, $gw, $iface = '', $args = '',
2810
    $ipprotocol = '') {
2811
	global $config;
2812
2813
	if (empty($target) || (empty($gw) && empty($iface))) {
2814
		return false;
2815
	}
2816
2817
	if ($target == 'default' && empty($ipprotocol)) {
2818
		return false;
2819
	}
2820
2821
	if (!empty($ipprotocol) && $ipprotocol != 'inet' &&
2822
	    $ipprotocol != 'inet6') {
2823
		return false;
2824
	}
2825
2826 d9818e01 Viktor G
	/* use '-host' for IPv6 /128 routes, see https://redmine.pfsense.org/issues/11594 */
2827
	if (is_subnetv4($target) || (is_subnetv6($target) && (subnet_size($target) > 1))) {
2828 c428cdf4 Renato Botelho do Couto
		$target_prefix = '-net';
2829
	} else if (is_ipaddr($target)) {
2830
		$target_prefix = '-host';
2831
	}
2832
2833
	if (!empty($ipprotocol)) {
2834
		$target_prefix .= " -{$ipprotocol}";
2835
	} else if (is_v6($target)) {
2836
		$target_prefix .= ' -inet6';
2837
	} else if (is_v4($target)) {
2838
		$target_prefix .= ' -inet';
2839
	}
2840
2841
	/* If there is another route to the same target, remove it */
2842
	route_del($target, $ipprotocol);
2843
2844
	$params = '';
2845
	if (!empty($iface) && does_interface_exist($iface)) {
2846 087d28fa Viktor G
		$params .= " -iface {$iface}";
2847 c428cdf4 Renato Botelho do Couto
	}
2848
	if (is_ipaddr($gw)) {
2849 cca31114 Viktor G
		/* set correct linklocal gateway address,
2850
		 * see https://redmine.pfsense.org/issues/11713 */
2851
		if (is_linklocal($gw) && empty(get_ll_scope($gw))) {
2852
			$routeget = route_get($gw, 'inet6', true);
2853
			$gw .= "%" . $routeget[0]['interface-name'];
2854
		}
2855 c428cdf4 Renato Botelho do Couto
		$params .= " " . $gw;
2856
	}
2857
2858
	if (empty($params)) {
2859
		log_error("route_add_or_change: Invalid gateway {$gw} and/or " .
2860
		    "network interface {$iface}");
2861
		return false;
2862
	}
2863
2864
	$_gb = exec(escapeshellcmd("/sbin/route add {$target_prefix} " .
2865
	    "{$target} {$args} {$params}"), $output, $rc);
2866
2867 43a9b03d PiBa-NL
	if (isset($config['system']['route-debug'])) {
2868 c428cdf4 Renato Botelho do Couto
		log_error("ROUTING debug: " . microtime() .
2869
		    " - ADD RC={$rc} - {$target} {$args}");
2870 33f28cc5 Renato Botelho do Couto
		file_put_contents("/dev/console", "\n[" . getmypid() .
2871 c428cdf4 Renato Botelho do Couto
		    "] ROUTE ADD: {$target_prefix} {$target} {$args} " .
2872
		    "{$params} result: {$rc}");
2873 43a9b03d PiBa-NL
	}
2874 94bd7fb3 Renato Botelho
2875
	return ($rc == 0);
2876
}
2877
2878 c297d257 Viktor Gurov
function set_ipv6routes_mtu($interface, $mtu) {
2879 0249b6f8 Renato Botelho do Couto
	global $config, $g;
2880
2881
	$ipv6mturoutes = array();
2882
	$if = convert_real_interface_to_friendly_interface_name($interface);
2883
	if (!$config['interfaces'][$if]['ipaddrv6']) {
2884
		return;
2885
	}
2886
	$a_gateways = return_gateways_array();
2887
	$a_staticroutes = get_staticroutes(false, false, true);
2888
	foreach ($a_gateways as $gate) {
2889
		foreach ($a_staticroutes as $sroute) {
2890
			if ($gate['interface'] == $interface &&
2891
			    $sroute['gateway'] == $gate['name']) {
2892
				$tgt = $sroute['network'];
2893
				$gateway = $gate['gateway'];
2894
				$ipv6mturoutes[$tgt] = $gateway;
2895
			}
2896
		}
2897
		if ($gate['interface'] == $interface &&
2898
		    $gate['isdefaultgw']) {
2899
			$tgt = "default";
2900
			$gateway = $gate['gateway'];
2901
			$ipv6mturoutes[$tgt] = $gateway;
2902
		}
2903
	}
2904
	foreach ($ipv6mturoutes as $tgt => $gateway) {
2905
		mwexec("/sbin/route change -6 -mtu " . escapeshellarg($mtu) .
2906
		    " " . escapeshellarg($tgt) . " " .
2907
		    escapeshellarg($gateway));
2908
	}
2909 c297d257 Viktor Gurov
}
2910
2911 71f0623e PiBa-NL
function alias_to_subnets_recursive($name, $returnhostnames = false) {
2912
	global $aliastable;
2913
	$result = array();
2914
	if (!isset($aliastable[$name])) {
2915
		return $result;
2916
	}
2917
	$subnets = preg_split('/\s+/', $aliastable[$name]);
2918
	foreach ($subnets as $net) {
2919
		if (is_alias($net)) {
2920
			$sub = alias_to_subnets_recursive($net, $returnhostnames);
2921
			$result = array_merge($result, $sub);
2922
			continue;
2923
		} elseif (!is_subnet($net)) {
2924
			if (is_ipaddrv4($net)) {
2925
				$net .= "/32";
2926
			} else if (is_ipaddrv6($net)) {
2927
				$net .= "/128";
2928
			} else if ($returnhostnames === false || !is_fqdn($net)) {
2929
				continue;
2930
			}
2931
		}
2932
		$result[] = $net;
2933
	}
2934
	return $result;
2935
}
2936
2937 cf08b49e Phil Davis
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false, $returnenabledroutesonly = false) {
2938 1901463c Renato Botelho
	global $config, $aliastable;
2939 f898c1a9 jim-p
2940
	/* Bail if there are no routes, but return an array always so callers don't have to check. */
2941 dbc1b8ee jim-p
	init_config_arr(array('staticroutes', 'route'));
2942
	if (empty($config['staticroutes']['route'])) {
2943 f898c1a9 jim-p
		return array();
2944 751533a2 Phil Davis
	}
2945 f898c1a9 jim-p
2946 bcab1b07 Ermal
	$allstaticroutes = array();
2947
	$allsubnets = array();
2948 f898c1a9 jim-p
	/* Loop through routes and expand aliases as we find them. */
2949
	foreach ($config['staticroutes']['route'] as $route) {
2950 cf08b49e Phil Davis
		if ($returnenabledroutesonly && isset($route['disabled'])) {
2951
			continue;
2952
		}
2953
2954 f898c1a9 jim-p
		if (is_alias($route['network'])) {
2955 71f0623e PiBa-NL
			foreach (alias_to_subnets_recursive($route['network'], $returnhostnames) as $net) {
2956 f898c1a9 jim-p
				$temproute = $route;
2957
				$temproute['network'] = $net;
2958
				$allstaticroutes[] = $temproute;
2959
				$allsubnets[] = $net;
2960
			}
2961
		} elseif (is_subnet($route['network'])) {
2962
			$allstaticroutes[] = $route;
2963
			$allsubnets[] = $route['network'];
2964
		}
2965
	}
2966 751533a2 Phil Davis
	if ($returnsubnetsonly) {
2967 f898c1a9 jim-p
		return $allsubnets;
2968 751533a2 Phil Davis
	} else {
2969 f898c1a9 jim-p
		return $allstaticroutes;
2970 751533a2 Phil Davis
	}
2971 f898c1a9 jim-p
}
2972 a0539faa Darren Embry
2973
/****f* util/get_alias_list
2974
 * NAME
2975
 *   get_alias_list - Provide a list of aliases.
2976
 * INPUTS
2977
 *   $type          - Optional, can be a string or array specifying what type(s) of aliases you need.
2978
 * RESULT
2979
 *   Array containing list of aliases.
2980
 *   If $type is unspecified, all aliases are returned.
2981
 *   If $type is a string, all aliases of the type specified in $type are returned.
2982
 *   If $type is an array, all aliases of any type specified in any element of $type are returned.
2983
 */
2984
function get_alias_list($type = null) {
2985
	global $config;
2986
	$result = array();
2987
	if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) {
2988
		foreach ($config['aliases']['alias'] as $alias) {
2989
			if ($type === null) {
2990
				$result[] = $alias['name'];
2991 751533a2 Phil Davis
			} else if (is_array($type)) {
2992 a0539faa Darren Embry
				if (in_array($alias['type'], $type)) {
2993
					$result[] = $alias['name'];
2994
				}
2995 751533a2 Phil Davis
			} else if ($type === $alias['type']) {
2996 a0539faa Darren Embry
				$result[] = $alias['name'];
2997
			}
2998
		}
2999 86a5e1a8 Renato Botelho
	}
3000 a0539faa Darren Embry
	return $result;
3001
}
3002
3003 4dfd930e Darren Embry
/* returns an array consisting of every element of $haystack that is not equal to $needle. */
3004
function array_exclude($needle, $haystack) {
3005
	$result = array();
3006
	if (is_array($haystack)) {
3007
		foreach ($haystack as $thing) {
3008
			if ($needle !== $thing) {
3009
				$result[] = $thing;
3010
			}
3011
		}
3012
	}
3013
	return $result;
3014
}
3015
3016 77a341a4 Renato Botelho
/* Define what is preferred, IPv4 or IPv6 */
3017
function prefer_ipv4_or_ipv6() {
3018
	global $config;
3019
3020 751533a2 Phil Davis
	if (isset($config['system']['prefer_ipv4'])) {
3021 77a341a4 Renato Botelho
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv4");
3022 751533a2 Phil Davis
	} else {
3023 77a341a4 Renato Botelho
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv6");
3024 751533a2 Phil Davis
	}
3025 77a341a4 Renato Botelho
}
3026
3027 111bea0d Renato Botelho
/* Redirect to page passing parameters via POST */
3028
function post_redirect($page, $params) {
3029 751533a2 Phil Davis
	if (!is_array($params)) {
3030 111bea0d Renato Botelho
		return;
3031 751533a2 Phil Davis
	}
3032 111bea0d Renato Botelho
3033
	print "<html><body><form action=\"{$page}\" name=\"formredir\" method=\"post\">\n";
3034
	foreach ($params as $key => $value) {
3035
		print "<input type=\"hidden\" name=\"{$key}\" value=\"{$value}\" />\n";
3036
	}
3037 8fd9052f Colin Fleming
	print "</form>\n";
3038
	print "<script type=\"text/javascript\">\n";
3039
	print "//<![CDATA[\n";
3040
	print "document.formredir.submit();\n";
3041
	print "//]]>\n";
3042
	print "</script>\n";
3043 111bea0d Renato Botelho
	print "</body></html>\n";
3044
}
3045
3046 ea20169a jim-p
/* Locate disks that can be queried for S.M.A.R.T. data. */
3047
function get_smart_drive_list() {
3048 e738a4c9 jim-p
	/* SMART supports some disks directly, and some controllers directly,
3049
	 * See https://redmine.pfsense.org/issues/9042 */
3050
	$supported_disk_types = array("ad", "da", "ada");
3051
	$supported_controller_types = array("nvme");
3052 ea20169a jim-p
	$disk_list = explode(" ", get_single_sysctl("kern.disks"));
3053
	foreach ($disk_list as $id => $disk) {
3054
		// We only want certain kinds of disks for S.M.A.R.T.
3055 a68c6785 Phil Davis
		// 1 is a match, 0 is no match, False is any problem processing the regex
3056 e738a4c9 jim-p
		if (preg_match("/^(" . implode("|", $supported_disk_types) . ").*[0-9]{1,2}$/", $disk) !== 1) {
3057 ea20169a jim-p
			unset($disk_list[$id]);
3058 e738a4c9 jim-p
			continue;
3059
		}
3060
	}
3061
	foreach ($supported_controller_types as $controller) {
3062
		$devices = glob("/dev/{$controller}*");
3063
		if (!is_array($devices)) {
3064
			continue;
3065
		}
3066
		foreach ($devices as $device) {
3067
			$disk_list[] = basename($device);
3068 ea20169a jim-p
		}
3069
	}
3070
	sort($disk_list);
3071
	return $disk_list;
3072
}
3073
3074 77a8a7d6 Steve Beaver
// Validate a network address
3075
//	$addr: the address to validate
3076
//	$type: IPV4|IPV6|IPV4V6
3077 c393f1d1 Steve Beaver
//	$label: the label used by the GUI to display this value. Required to compose an error message
3078 77a8a7d6 Steve Beaver
//	$err_msg: pointer to the callers error message array so that error messages can be added to it here
3079
//	$alias: are aliases permitted for this address?
3080 24eb39e2 Phil Davis
// Returns:
3081 74999ad8 Phil Davis
//	IPV4 - if $addr is a valid IPv4 address
3082
//	IPV6 - if $addr is a valid IPv6 address
3083
//	ALIAS - if $alias=true and $addr is an alias
3084 24eb39e2 Phil Davis
//	false - otherwise
3085
3086 77a8a7d6 Steve Beaver
function validateipaddr(&$addr, $type, $label, &$err_msg, $alias=false) {
3087
	switch ($type) {
3088
		case IPV4:
3089
			if (is_ipaddrv4($addr)) {
3090 dc938839 Phil Davis
				return IPV4;
3091 77a8a7d6 Steve Beaver
			} else if ($alias) {
3092
				if (is_alias($addr)) {
3093 dc938839 Phil Davis
					return ALIAS;
3094 77a8a7d6 Steve Beaver
				} else {
3095 bb9747b2 Phil Davis
					$err_msg[] = sprintf(gettext("%s must be a valid IPv4 address or alias."), $label);
3096 77a8a7d6 Steve Beaver
					return false;
3097
				}
3098
			} else {
3099 bb9747b2 Phil Davis
				$err_msg[] = sprintf(gettext("%s must be a valid IPv4 address."), $label);
3100 77a8a7d6 Steve Beaver
				return false;
3101
			}
3102
		break;
3103
		case IPV6:
3104
			if (is_ipaddrv6($addr)) {
3105
				$addr = strtolower($addr);
3106 dc938839 Phil Davis
				return IPV6;
3107 aa2b8133 Phil Davis
			} else if ($alias) {
3108 77a8a7d6 Steve Beaver
				if (is_alias($addr)) {
3109 dc938839 Phil Davis
					return ALIAS;
3110 77a8a7d6 Steve Beaver
				} else {
3111 bb9747b2 Phil Davis
					$err_msg[] = sprintf(gettext("%s must be a valid IPv6 address or alias."), $label);
3112 77a8a7d6 Steve Beaver
					return false;
3113
				}
3114
			} else {
3115 bb9747b2 Phil Davis
				$err_msg[] = sprintf(gettext("%s must be a valid IPv6 address."), $label);
3116 77a8a7d6 Steve Beaver
				return false;
3117
			}
3118
		break;
3119
		case IPV4V6:
3120
			if (is_ipaddrv6($addr)) {
3121
				$addr = strtolower($addr);
3122 dc938839 Phil Davis
				return IPV6;
3123 77a8a7d6 Steve Beaver
			} else if (is_ipaddrv4($addr)) {
3124 dc938839 Phil Davis
				return IPV4;
3125 aa2b8133 Phil Davis
			} else if ($alias) {
3126 77a8a7d6 Steve Beaver
				if (is_alias($addr)) {
3127 dc938839 Phil Davis
					return ALIAS;
3128 77a8a7d6 Steve Beaver
				} else {
3129 bb9747b2 Phil Davis
					$err_msg[] = sprintf(gettext("%s must be a valid IPv4 or IPv6 address or alias."), $label);
3130 77a8a7d6 Steve Beaver
					return false;
3131
				}
3132
			} else {
3133 bb9747b2 Phil Davis
				$err_msg[] = sprintf(gettext("%s must be a valid IPv4 or IPv6 address."), $label);
3134 77a8a7d6 Steve Beaver
				return false;
3135
			}
3136
		break;
3137
	}
3138
3139
	return false;
3140
}
3141 7be23d53 marjohn56
3142 64b9d133 kangtastic
/* From DUID configuration inputs, format a string that looks (more) like the expected raw DUID format:
3143 fffb9eed kangtastic
 * 1) For DUIDs entered as a known DUID type, convert to a hexstring and prepend the DUID number, after having done the following:
3144
 *     a) For DUID-LLT and DUID-EN, convert the time/enterprise ID input to hex and append the link-layer address/identifier input.
3145
 *     b) For DUID-LLT and DUID-LL, prepend a hardware type of 1.
3146
 *     c) For DUID-UUID, remove any "-".
3147
 * 2) Replace any remaining "-" with ":".
3148 f4bbec8b Phil Davis
 * 3) If any components are input with just a single char (hex digit hopefully), put a "0" in front.
3149 fffb9eed kangtastic
 * 4) The first two components should be a 16-bit integer (little- or big-endian, depending on the current machine type) that
3150 7955bcce kangtastic
 *    is equal to the number of other components. If not, prepend this as "nn:00" (all pfSense builds are little-endian).
3151 fffb9eed kangtastic
 *    This is convenience, because the DUID reported by dhcp6c in logs does not include this count, which corresponds to the
3152
 *    option-len field of DHCPv6's OPTION_CLIENTID option.
3153 f4bbec8b Phil Davis
 *
3154
 * The final result should be closer to:
3155
 *
3156 7955bcce kangtastic
 * "nn:00:00:0n:nn:nn:nn:..."
3157 f4bbec8b Phil Davis
 *
3158
 * This function does not validate the input. is_duid() will do validation.
3159 64b9d133 kangtastic
 */
3160
function format_duid($duidtype, $duidpt1, $duidpt2=null) {
3161 fffb9eed kangtastic
	if ($duidpt2)
3162
		$duidpt1 = implode(':', str_split(str_pad(dechex($duidpt1), 8, '0', STR_PAD_LEFT), 2)) . ':' . $duidpt2;
3163 64b9d133 kangtastic
3164 fffb9eed kangtastic
	/* Make hexstrings */
3165
	if ($duidtype) {
3166
		switch ($duidtype) {
3167
		/* Add a hardware type to DUID-LLT and DUID-LL; assume Ethernet */
3168
		case 1:
3169
		case 3:
3170
			$duidpt1 = '00:01:' . $duidpt1;
3171
			break;
3172
		/* Remove '-' from given UUID and insert ':' every 2 characters */
3173
		case 4:
3174
			$duidpt1 = implode(':', str_split(str_replace('-', '', $duidpt1), 2));
3175
			break;
3176
		default:
3177
		}
3178
		$duidpt1 = '00:0' . $duidtype . ':' . $duidpt1;
3179 f4bbec8b Phil Davis
	}
3180 fd2e503a Phil Davis
3181 fffb9eed kangtastic
	$values = explode(':', strtolower(str_replace('-', ':', $duidpt1)));
3182
3183 7955bcce kangtastic
	if (hexdec($values[0]) != count($values) - 2)
3184 64b9d133 kangtastic
		array_unshift($values, dechex(count($values)), '00');
3185 fd2e503a Phil Davis
3186
	array_walk($values, function(&$value) {
3187 4f3fc80d Renato Botelho
		$value = str_pad($value, 2, '0', STR_PAD_LEFT);
3188 fd2e503a Phil Davis
	});
3189
3190 febfd592 Phil Davis
	return implode(":", $values);
3191 f4bbec8b Phil Davis
}
3192
3193 64b9d133 kangtastic
/* Returns true if $dhcp6duid is a valid DUID entry.
3194 7955bcce kangtastic
 * Parse the entry to check for valid length according to known DUID types.
3195 64b9d133 kangtastic
 */
3196 7be23d53 marjohn56
function is_duid($dhcp6duid) {
3197
	$values = explode(":", $dhcp6duid);
3198 7955bcce kangtastic
	if (hexdec($values[0]) == count($values) - 2) {
3199 64b9d133 kangtastic
		switch (hexdec($values[2] . $values[3])) {
3200 fffb9eed kangtastic
		case 0:
3201
			return false;
3202
			break;
3203 64b9d133 kangtastic
		case 1:
3204 fffb9eed kangtastic
			if (count($values) != 16 || strlen($dhcp6duid) != 47)
3205 64b9d133 kangtastic
				return false;
3206
			break;
3207
		case 3:
3208 fffb9eed kangtastic
			if (count($values) != 12 || strlen($dhcp6duid) != 35)
3209 64b9d133 kangtastic
				return false;
3210
			break;
3211
		case 4:
3212 fffb9eed kangtastic
			if (count($values) != 20 || strlen($dhcp6duid) != 59)
3213 64b9d133 kangtastic
				return false;
3214
			break;
3215 fffb9eed kangtastic
		/* DUID is up to 128 octets; allow 2 octets for type code, 2 more for option-len */
3216 64b9d133 kangtastic
		default:
3217 fffb9eed kangtastic
			if (count($values) > 132 || strlen($dhcp6duid) != count($values) * 3 - 1)
3218 64b9d133 kangtastic
				return false;
3219
		}
3220 fffb9eed kangtastic
	} else
3221 7be23d53 marjohn56
		return false;
3222 fffb9eed kangtastic
3223 64b9d133 kangtastic
	for ($i = 0; $i < count($values); $i++) {
3224 7be23d53 marjohn56
		if (ctype_xdigit($values[$i]) == false)
3225
			return false;
3226
		if (hexdec($values[$i]) < 0 || hexdec($values[$i]) > 255)
3227
			return false;
3228
	}
3229 fffb9eed kangtastic
3230 7be23d53 marjohn56
	return true;
3231
}
3232
3233
/* Write the DHCP6 DUID file */
3234
function write_dhcp6_duid($duidstring) {
3235
	// Create the hex array from the dhcp6duid config entry and write to file
3236
	global $g;
3237 4f3fc80d Renato Botelho
3238
	if(!is_duid($duidstring)) {
3239 7be23d53 marjohn56
		log_error(gettext("Error: attempting to write DUID file - Invalid DUID detected"));
3240
		return false;
3241
	}
3242
	$temp = str_replace(":","",$duidstring);
3243
	$duid_binstring = pack("H*",$temp);
3244
	if ($fd = fopen("{$g['vardb_path']}/dhcp6c_duid", "wb")) {
3245
		fwrite($fd, $duid_binstring);
3246
		fclose($fd);
3247
		return true;
3248
	}
3249
	log_error(gettext("Error: attempting to write DUID file - File write error"));
3250
	return false;
3251
}
3252 2acedbbf marjohn56
3253 9e08a2bd marjohn56
/* returns duid string from 'vardb_path']}/dhcp6c_duid' */
3254 4f3fc80d Renato Botelho
function get_duid_from_file() {
3255 9e08a2bd marjohn56
	global $g;
3256 4f3fc80d Renato Botelho
3257 2acedbbf marjohn56
	$duid_ASCII = "";
3258 9e08a2bd marjohn56
	$count = 0;
3259 a271ed3d Renato Botelho
3260
	if (file_exists("{$g['vardb_path']}/dhcp6c_duid") &&
3261
	    ($fd = fopen("{$g['vardb_path']}/dhcp6c_duid", "r"))) {
3262 64b9d133 kangtastic
		$fsize = filesize("{$g['vardb_path']}/dhcp6c_duid");
3263
		if ($fsize <= 132) {
3264
			$buffer = fread($fd, $fsize);
3265
			while($count < $fsize) {
3266 2acedbbf marjohn56
				$duid_ASCII .= bin2hex($buffer[$count]);
3267 9e08a2bd marjohn56
				$count++;
3268 64b9d133 kangtastic
				if($count < $fsize) {
3269 9e08a2bd marjohn56
					$duid_ASCII .= ":";
3270
				}
3271
			}
3272
		}
3273
		fclose($fd);
3274
	}
3275
	//if no file or error with read then the string returns blanked DUID string
3276 2acedbbf marjohn56
	if(!is_duid($duid_ASCII)) {
3277 9e08a2bd marjohn56
		return "--:--:--:--:--:--:--:--:--:--:--:--:--:--:--:--";
3278
	}
3279 4f3fc80d Renato Botelho
	return($duid_ASCII);
3280 9e08a2bd marjohn56
}
3281 117776e0 doktornotor
3282
/* Replaces the Mac OS 9 and earlier (\r) and DOS/Windows (\r\n) newlines with the Unix equivalent (\n). */
3283
function unixnewlines($text) {
3284
	return preg_replace('/\r\n?/', "\n", $text);
3285
}
3286
3287 923eb50e Renato Botelho
function array_remove_duplicate($array, $field) {
3288
	foreach ($array as $sub) {
3289
		if (isset($sub[$field])) {
3290
			$cmp[] = $sub[$field];
3291
		}
3292
	}
3293
	$unique = array_unique(array_reverse($cmp, true));
3294
	foreach ($unique as $k => $rien) {
3295
		$new[] = $array[$k];
3296
	}
3297
	return $new;
3298
}
3299
3300 626c7734 Renato Botelho
function dhcpd_date_adjust_gmt($dt) {
3301
	global $config;
3302
3303
	init_config_arr(array('dhcpd'));
3304
3305
	foreach ($config['dhcpd'] as $dhcpditem) {
3306
		if ($dhcpditem['dhcpleaseinlocaltime'] == "yes") {
3307
			$ts = strtotime($dt . " GMT");
3308
			if ($ts !== false) {
3309
				return strftime("%Y/%m/%d %H:%M:%S", $ts);
3310
			}
3311
		}
3312
	}
3313
3314
	/*
3315
	 * If we did not need to convert to local time or the conversion
3316
	 * failed, just return the input.
3317
	 */
3318
	return $dt;
3319
}
3320
3321 09d59743 jim-p
global $supported_image_types;
3322
$supported_image_types = array(
3323
	IMAGETYPE_JPEG,
3324
	IMAGETYPE_PNG,
3325
	IMAGETYPE_GIF,
3326
	IMAGETYPE_WEBP
3327
);
3328
3329
function is_supported_image($image_filename) {
3330
	global $supported_image_types;
3331
	$img_info = getimagesize($image_filename);
3332
3333
	/* If it's not an image, or it isn't in the supported list, return false */
3334
	if (($img_info === false) ||
3335
	    !in_array($img_info[2], array_keys($supported_image_types))) {
3336
		return false;
3337
	} else {
3338
		return $img_info[2];
3339
	}
3340
}
3341
3342 49e36202 Viktor Gurov
function get_lagg_ports ($laggport) {
3343
	$laggp = array();
3344
	foreach ($laggport as $lgp) {
3345
		list($lpname, $lpinfo) = explode(" ", $lgp);
3346
		preg_match('~<(.+)>~', $lpinfo, $lgportmode);
3347
		if ($lgportmode[1]) {
3348
			$laggp[] = $lpname . " (" . $lgportmode[1] . ")";
3349
		} else {
3350
			$laggp[] = $lpname;
3351
		}
3352
	}
3353
	if ($laggp) {
3354
		return implode(", ", $laggp);
3355
	} else {
3356
		return false;
3357
	}
3358
}
3359
3360 4537e922 Viktor G
function cisco_to_cidr($addr) {
3361
	if (!is_ipaddr($addr)) {
3362
		throw new Exception('Invalid IP Addr');
3363
	}
3364
3365
	$mask = decbin(~ip2long($addr));
3366
	$mask = substr($mask, -32);
3367
	$k = 0;
3368
	for ($i = 0; $i <= 32; $i++) {
3369
		$k += intval($mask[$i]);
3370
	}
3371
	return $k;
3372
}
3373
3374
function cisco_extract_index($prule) {
3375
	$index = explode("#", $prule);
3376
	if (is_numeric($index[1])) {
3377
		return intval($index[1]);
3378
	} else {
3379
		syslog(LOG_WARNING, "Error parsing rule {$prule}: Could not extract index");
3380
	}
3381
	return -1;;
3382
}
3383
3384
function parse_cisco_acl_rule($rule, $devname, $dir, $proto) {
3385
	$rule_orig = $rule;
3386
	$rule = explode(" ", $rule);
3387
	$tmprule = "";
3388
	$index = 0;
3389
3390
	if ($rule[$index] == "permit") {
3391
		$startrule = "pass {$dir} quick on {$devname} ";
3392
	} else if ($rule[$index] == "deny") {
3393
		$startrule = "block {$dir} quick on {$devname} ";
3394
	} else {
3395
		return;
3396
	}
3397
3398
	$index++;
3399
3400
	switch ($rule[$index]) {
3401
		case "ip":
3402
			break;
3403
		case "icmp":
3404
			$icmp = ($proto == "inet") ? "icmp" : "ipv6-icmp";
3405
			$tmprule .= "proto {$icmp} ";
3406
			break;
3407
		case "tcp":
3408
		case "udp":
3409
			$tmprule .= "proto {$rule[$index]} ";
3410
			break;
3411
		default:
3412
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid protocol.");
3413
			return;
3414
	}
3415
	$index++;
3416
3417
	/* Source */
3418
	if (trim($rule[$index]) == "host") {
3419
		$index++;
3420 6bb8cdd4 Viktor G
		if ((($proto == 'inet') && (is_ipaddrv4(trim($rule[$index])) || (trim($rule[$index]) == "{clientip}"))) ||
3421
		    (($proto == 'inet6') && (is_ipaddrv6(trim($rule[$index])) || (trim($rule[$index]) == "{clientipv6}")))) {
3422 4537e922 Viktor G
			$tmprule .= "from {$rule[$index]} ";
3423
			$index++;
3424
		} else {
3425
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid source host '{$rule[$index]}'.");
3426
			return;
3427
		}
3428
	} elseif (is_subnetv6(trim($rule[$index])) && ($proto == "inet6")) {
3429
		$tmprule .= "from {$rule[$index]} ";
3430
		$index++;
3431
	} elseif (trim($rule[$index]) == "any") {
3432
		$tmprule .= "from any ";
3433
		$index++;
3434
	} else {
3435
		$network = $rule[$index];
3436
		$netmask = $rule[++$index];
3437
3438
		if (is_ipaddrv4($network) && ($proto == "inet")) {
3439
			try {
3440
				$netmask = cisco_to_cidr($netmask);
3441
			} catch(Exception $e) {
3442
				syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid source netmask '$netmask'.");
3443
				return;
3444
			}
3445 321fbbdb bashkarev
			$tmprule .= "from {$network}/{$netmask} ";
3446 4537e922 Viktor G
		} else {
3447
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid source network '$network'.");
3448
			return;
3449
		}
3450
3451
		$index++;
3452
	}
3453
3454
	/* Source Operator */
3455
	if (in_array(trim($rule[$index]), array("lt", "gt", "eq", "neq"))) {
3456
		switch(trim($rule[$index])) {
3457
			case "lt":
3458
				$operator = "<";
3459
				break;
3460
			case "gt":
3461
				$operator = ">";
3462
				break;
3463
			case "eq":
3464
				$operator = "=";
3465
				break;
3466
			case "neq":
3467
				$operator = "!=";
3468
				break;
3469
		}
3470
3471
		$port = $rule[++$index];
3472
		if (is_port($port)) {
3473
			$tmprule .= "port {$operator} {$port} ";
3474
		} else {
3475
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid source port: '$port' not a numeric value between 0 and 65535.");
3476
			return;
3477
		}
3478
		$index++;
3479
	} else if (trim($rule[$index]) == "range") {
3480
		$port = array($rule[++$index], $rule[++$index]);
3481
		if (is_port($port[0]) && is_port($port[1])) {
3482
			$port[0]--;
3483
			$port[1]++;
3484
			$tmprule .= "port {$port[0]} >< {$port[1]} ";
3485
		} else {
3486
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid source ports: '$port[0]' & '$port[1]' one or both are not a numeric value between 0 and 65535.");
3487
			return;
3488
		}
3489
		$index++;
3490
	}
3491
3492
	/* Destination */
3493
	if (trim($rule[$index]) == "host") {
3494
		$index++;
3495 6bb8cdd4 Viktor G
		if ((($proto == 'inet') && (is_ipaddrv4(trim($rule[$index])) || (trim($rule[$index]) == "{clientip}"))) ||
3496
		    (($proto == 'inet6') && (is_ipaddrv6(trim($rule[$index])) || (trim($rule[$index]) == "{clientipv6}")))) {
3497 4537e922 Viktor G
			$tmprule .= "to {$rule[$index]} ";
3498
			$index++;
3499
		} else {
3500
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid destination host '{$rule[$index]}'.");
3501
			return;
3502
		}
3503
	} elseif (is_subnetv6(trim($rule[$index])) && ($proto == "inet6")) {
3504
		$tmprule .= "to {$rule[$index]} ";
3505
		$index++;
3506
	} elseif (trim($rule[$index]) == "any") {
3507
		$tmprule .= "to any ";
3508
		$index++;
3509
	} else {
3510
		$network = $rule[$index];
3511
		$netmask = $rule[++$index];
3512
3513
		if (is_ipaddrv4($network) && ($proto == "inet")) {
3514
			try {
3515
				$netmask = cisco_to_cidr($netmask);
3516
			} catch(Exception $e) {
3517
				syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid destination network '$network'.");
3518
				return;
3519
			}
3520 321fbbdb bashkarev
			$tmprule .= "to {$network}/{$netmask} ";
3521 4537e922 Viktor G
		} else {
3522
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid destination network '$network'.");
3523
			return;
3524
		}
3525
3526
		$index++;
3527
	}
3528
3529
	/* Destination Operator */
3530
	if (in_array(trim($rule[$index]), array("lt", "gt", "eq", "neq"))) {
3531
		switch(trim($rule[$index])) {
3532
			case "lt":
3533
				$operator = "<";
3534
				break;
3535
			case "gt":
3536
				$operator = ">";
3537
				break;
3538
			case "eq":
3539
				$operator = "=";
3540
				break;
3541
			case "neq":
3542
				$operator = "!=";
3543
				break;
3544
		}
3545
3546
		$port = $rule[++$index];
3547
		if (is_port($port)) {
3548
			$tmprule .= "port {$operator} {$port} ";
3549
		} else {
3550
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid destination port: '$port' not a numeric value between 0 and 65535.");
3551
			return;
3552
		}
3553
		$index++;
3554
	} else if (trim($rule[$index]) == "range") {
3555
		$port = array($rule[++$index], $rule[++$index]);
3556
		if (is_port($port[0]) && is_port($port[1])) {
3557
			$port[0]--;
3558
			$port[1]++;
3559
			$tmprule .= "port {$port[0]} >< {$port[1]} ";
3560
		} else {
3561
			syslog(LOG_WARNING, "Error parsing rule {$rule_orig}: Invalid destination ports: '$port[0]' '$port[1]' one or both are not a numeric value between 0 and 65535.");
3562
			return;
3563
		}
3564
		$index++;
3565
	}
3566
3567
	$tmprule = $startrule . $proto . " " . $tmprule;
3568
	return $tmprule;
3569
}
3570
3571
function parse_cisco_acl($attribs, $dev) {
3572
	global $attributes;
3573
3574
	if (!is_array($attribs)) {
3575
		return "";
3576
	}
3577
	$finalrules = "";
3578
	if (is_array($attribs['ciscoavpair'])) {
3579
		$inrules = array('inet' => array(), 'inet6' => array());
3580
		$outrules = array('inet' => array(), 'inet6' => array());
3581
		foreach ($attribs['ciscoavpair'] as $avrules) {
3582
			$rule = explode("=", $avrules);
3583
			$dir = "";
3584
			if (strstr($rule[0], "inacl")) {
3585
				$dir = "in";
3586
			} else if (strstr($rule[0], "outacl")) {
3587
				$dir = "out";
3588
			} else if (strstr($rule[0], "dns-servers")) {
3589
				$attributes['dns-servers'] = explode(" ", $rule[1]);
3590
				continue;
3591
			} else if (strstr($rule[0], "route")) {
3592
				if (!is_array($attributes['routes'])) {
3593
					$attributes['routes'] = array();
3594
				}
3595
				$attributes['routes'][] = $rule[1];
3596
				continue;
3597
			}
3598
			$rindex = cisco_extract_index($rule[0]);
3599
			if ($rindex < 0) {
3600
				continue;
3601
			}
3602
3603
			if (strstr($rule[0], "ipv6")) {
3604
				$proto = "inet6";
3605
			} else {
3606
				$proto = "inet";
3607
			}
3608
3609
			$tmprule = parse_cisco_acl_rule($rule[1], $dev, $dir, $proto);
3610
3611
			if ($dir == "in") {
3612
				$inrules[$proto][$rindex] = $tmprule;
3613
			} else if ($dir == "out") {
3614
				$outrules[$proto][$rindex] = $tmprule;
3615
			}
3616
		}
3617
3618
3619
		$state = "";
3620
		foreach (array('inet', 'inet6') as $ip) {
3621
			if (!empty($outrules[$ip])) {
3622
				$state = "no state";
3623
			}
3624
			ksort($inrules[$ip], SORT_NUMERIC);
3625
			foreach ($inrules[$ip] as $inrule) {
3626
				$finalrules .= "{$inrule} {$state}\n";
3627
			}
3628
			if (!empty($outrules[$ip])) {
3629
				ksort($outrules[$ip], SORT_NUMERIC);
3630
				foreach ($outrules[$ip] as $outrule) {
3631
					$finalrules .= "{$outrule} {$state}\n";
3632
				}
3633
			}
3634
		}
3635
	}
3636
	return $finalrules;
3637
}
3638
3639 c91be02b Viktor G
function alias_idn_to_utf8($alias) {
3640
	if (is_alias($alias)) {
3641
		return $alias;
3642
	} else {
3643
		return idn_to_utf8($alias);
3644
	}
3645
}
3646
3647
function alias_idn_to_ascii($alias) {
3648
	if (is_alias($alias)) {
3649
		return $alias;
3650
	} else {
3651
		return idn_to_ascii($alias);
3652
	}
3653
}
3654
3655 2428d0a9 Steve Beaver
// These funtions were in guiconfig.inc but have been moved here so non GUI processes can use them
3656
function address_to_pconfig($adr, &$padr, &$pmask, &$pnot, &$pbeginport, &$pendport) {
3657
	if (isset($adr['any'])) {
3658
		$padr = "any";
3659
	} else if ($adr['network']) {
3660
		$padr = $adr['network'];
3661
	} else if ($adr['address']) {
3662
		list($padr, $pmask) = explode("/", $adr['address']);
3663
		if (!$pmask) {
3664
			if (is_ipaddrv6($padr)) {
3665
				$pmask = 128;
3666
			} else {
3667
				$pmask = 32;
3668
			}
3669
		}
3670
	}
3671
3672
	if (isset($adr['not'])) {
3673
		$pnot = 1;
3674
	} else {
3675
		$pnot = 0;
3676
	}
3677
3678
	if ($adr['port']) {
3679
		list($pbeginport, $pendport) = explode("-", $adr['port']);
3680
		if (!$pendport) {
3681
			$pendport = $pbeginport;
3682
		}
3683
	} else if (!is_alias($pbeginport) && !is_alias($pendport)) {
3684
		$pbeginport = "any";
3685
		$pendport = "any";
3686
	}
3687
}
3688
3689
function pconfig_to_address(&$adr, $padr, $pmask, $pnot = false, $pbeginport = 0, $pendport = 0) {
3690
	$adr = array();
3691
3692
	if ($padr == "any") {
3693
		$adr['any'] = true;
3694
	} else if (is_specialnet($padr)) {
3695
		$adr['network'] = $padr;
3696
	} else {
3697
		$adr['address'] = $padr;
3698
		if (is_ipaddrv6($padr)) {
3699
			if ($pmask != 128) {
3700
				$adr['address'] .= "/" . $pmask;
3701
			}
3702
		} else {
3703
			if ($pmask != 32) {
3704
				$adr['address'] .= "/" . $pmask;
3705
			}
3706
		}
3707
	}
3708
3709
	if ($pnot) {
3710
		$adr['not'] = true;
3711
	} else {
3712
		unset($adr['not']);
3713
	}
3714
3715
	if (($pbeginport != 0) && ($pbeginport != "any")) {
3716
		if ($pbeginport != $pendport) {
3717
			$adr['port'] = $pbeginport . "-" . $pendport;
3718
		} else {
3719
			$adr['port'] = $pbeginport;
3720
		}
3721
	}
3722
3723
	/*
3724
	 * If the port is still unset, then it must not be numeric, but could
3725
	 * be an alias or a well-known/registered service.
3726
	 * See https://redmine.pfsense.org/issues/8410
3727
	 */
3728
	if (!isset($adr['port']) && is_port_or_alias($pbeginport)) {
3729
		$adr['port'] = $pbeginport;
3730
	}
3731
}
3732
3733
function is_specialnet($net) {
3734
	global $specialsrcdst;
3735
3736
	if (!$net) {
3737
		return false;
3738
	}
3739
	if (in_array($net, $specialsrcdst)) {
3740
		return true;
3741
	} else {
3742
		return false;
3743
	}
3744
}
3745 eb295a1b Ermal
?>