Project

General

Profile

Download (65.8 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * services_dhcp.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2023 Rubicon Communications, LLC (Netgate)
9
 * All rights reserved.
10
 *
11
 * originally based on m0n0wall (http://m0n0.ch/wall)
12
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
13
 * All rights reserved.
14
 *
15
 * 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
 *
19
 * http://www.apache.org/licenses/LICENSE-2.0
20
 *
21
 * 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
 */
27

    
28
##|+PRIV
29
##|*IDENT=page-services-dhcpserver
30
##|*NAME=Services: DHCP Server
31
##|*DESCR=Allow access to the 'Services: DHCP Server' page.
32
##|*MATCH=services_dhcp.php*
33
##|-PRIV
34

    
35
require_once('guiconfig.inc');
36
require_once('filter.inc');
37
require_once('rrd.inc');
38
require_once('shaper.inc');
39
require_once('util.inc');
40

    
41
global $ddnsdomainkeyalgorithms;
42

    
43
function is_dhcrelay_enabled(string $if): bool
44
{
45
	if (config_path_enabled('dhcrelay')) {
46
		foreach (explode(',', config_get_path('dhcrelay/interface')) as $dhcrelayif) {
47
			if ($dhcrelayif === $if) {
48
				return (true);
49
			}
50
		}
51
	}
52

    
53
	return (false);
54
}
55

    
56
if (!g_get('services_dhcp_server_enable')) {
57
	header("Location: /");
58
	exit;
59
}
60

    
61
$if = $_REQUEST['if'];
62
$iflist = get_configured_interface_with_descr();
63

    
64
/* set the starting interface */
65
if (!$if || !isset($iflist[$if])) {
66
	$found_starting_if = false;
67
	// First look for an interface with DHCP already enabled.
68
	foreach (array_keys($iflist) as $ifent) {
69
		if (config_path_enabled("dhcpd/{$ifent}") &&
70
		    is_ipaddrv4(config_get_path("interfaces/{$ifent}/ipaddr")) &&
71
		    ((int) config_get_path("interfaces/{$ifent}/subnet", 0) < 31)) {
72
			$if = $ifent;
73
			$found_starting_if = true;
74
			break;
75
		}
76
	}
77

    
78
	/*
79
	 * If there is no DHCP-enabled interface and LAN is a candidate,
80
	 * then choose LAN.
81
	 */
82
	if (!$found_starting_if &&
83
	    !empty(array_get_path($iflist, 'lan')) &&
84
	    is_ipaddrv4(config_get_path("interfaces/lan/ipaddr")) &&
85
	    ((int) config_get_path("interfaces/lan/subnet", 0) < 31)) {
86
		$if = 'lan';
87
		$found_starting_if = true;
88
	}
89

    
90
	// At the last select whatever can be found.
91
	$fallback = "";
92
	if (!$found_starting_if) {
93
		foreach (array_keys($iflist) as $ifent) {
94
			/* Not static IPv4 or subnet >= 31 */
95
			if (!is_ipaddrv4(config_get_path("interfaces/{$ifent}/ipaddr")) ||
96
			    empty(config_get_path("interfaces/{$ifent}/subnet")) ||
97
			    ((int) config_get_path("interfaces/{$ifent}/subnet", 0) >= 31)) {
98
				continue;
99
			} elseif (empty($fallback)) {
100
				/* First potential fallback in case no interfaces
101
				 * have DHCP enabled. */
102
				$fallback = $ifent;
103
			}
104

    
105
			/* If this interface has does not have DHCP enabled,
106
			 * skip it for now. */
107
			if (!config_path_enabled("dhcpd/{$ifent}")) {
108
				continue;
109
			}
110

    
111
			$if = $ifent;
112
			break;
113
		}
114
		if (empty($if) || !empty($fallback)) {
115
			$if = $fallback;
116
		}
117
	}
118
}
119

    
120
$act = $_REQUEST['act'];
121

    
122
$a_pools = array();
123

    
124
if (!empty(config_get_path("dhcpd/{$if}"))) {
125
	$pool = $_REQUEST['pool'];
126
	if (is_numeric($_POST['pool'])) {
127
		$pool = $_POST['pool'];
128
	}
129

    
130
	// If we have a pool but no interface name, that's not valid. Redirect away.
131
	if (is_numeric($pool) && empty($if)) {
132
		header("Location: services_dhcp.php");
133
		exit;
134
	}
135

    
136
	init_config_arr(array('dhcpd', $if, 'pool'));
137
	$a_pools = &$config['dhcpd'][$if]['pool'];
138

    
139
	if (is_numeric($pool) && $a_pools[$pool]) {
140
		$dhcpdconf = &$a_pools[$pool];
141
	} elseif ($act == "newpool") {
142
		$dhcpdconf = array();
143
	} else {
144
		$dhcpdconf = &$config['dhcpd'][$if];
145
	}
146

    
147
	init_config_arr(array('dhcpd', $if, 'staticmap'));
148
	$a_maps = &$config['dhcpd'][$if]['staticmap'];
149
}
150

    
151
if (is_array($dhcpdconf)) {
152
	// Global Options
153
	if (!is_numeric($pool) && !($act == "newpool")) {
154
		$pconfig['enable'] = isset($dhcpdconf['enable']);
155
		$pconfig['staticarp'] = isset($dhcpdconf['staticarp']);
156
		// No reason to specify this per-pool, per the dhcpd.conf man page it needs to be in every
157
		//	 pool and should be specified in every pool both nodes share, so we'll treat it as global
158
		$pconfig['failover_peerip'] = $dhcpdconf['failover_peerip'];
159

    
160
		// dhcpleaseinlocaltime is global to all interfaces. So if it is selected on any interface,
161
		// then show it true/checked.
162
		foreach (config_get_path('dhcpd', []) as $dhcpdifitem) {
163
			if (empty($dhcpdifitem)) {
164
				continue;
165
			}
166
			$dhcpleaseinlocaltime = $dhcpdifitem['dhcpleaseinlocaltime'];
167
			if ($dhcpleaseinlocaltime) {
168
				break;
169
			}
170
		}
171

    
172
		$pconfig['dhcpleaseinlocaltime'] = $dhcpleaseinlocaltime;
173
	} else {
174
		// Options that exist only in pools
175
		$pconfig['descr'] = $dhcpdconf['descr'];
176
	}
177

    
178
	// Options that can be global or per-pool.
179
	if (is_array($dhcpdconf['range'])) {
180
		$pconfig['range_from'] = $dhcpdconf['range']['from'];
181
		$pconfig['range_to'] = $dhcpdconf['range']['to'];
182
	}
183

    
184
	$pconfig['deftime'] = $dhcpdconf['defaultleasetime'];
185
	$pconfig['maxtime'] = $dhcpdconf['maxleasetime'];
186
	$pconfig['gateway'] = $dhcpdconf['gateway'];
187
	$pconfig['domain'] = $dhcpdconf['domain'];
188
	$pconfig['domainsearchlist'] = $dhcpdconf['domainsearchlist'];
189
	list($pconfig['wins1'], $pconfig['wins2']) = $dhcpdconf['winsserver'];
190
	list($pconfig['dns1'], $pconfig['dns2'], $pconfig['dns3'], $pconfig['dns4']) = $dhcpdconf['dnsserver'];
191
	$pconfig['ignorebootp'] = isset($dhcpdconf['ignorebootp']);
192

    
193
	if (isset($dhcpdconf['denyunknown'])) {
194
		$pconfig['denyunknown'] = empty($dhcpdconf['denyunknown']) ? "enabled" : $dhcpdconf['denyunknown'];
195
	} else {
196
		$pconfig['denyunknown'] = "disabled";
197
	}
198

    
199
	$pconfig['ignoreclientuids'] = isset($dhcpdconf['ignoreclientuids']);
200
	$pconfig['nonak'] = isset($dhcpdconf['nonak']);
201
	$pconfig['ddnsdomain'] = $dhcpdconf['ddnsdomain'];
202
	$pconfig['ddnsdomainprimary'] = $dhcpdconf['ddnsdomainprimary'];
203
	$pconfig['ddnsdomainsecondary'] = $dhcpdconf['ddnsdomainsecondary'];
204
	$pconfig['ddnsdomainkeyname'] = $dhcpdconf['ddnsdomainkeyname'];
205
	$pconfig['ddnsdomainkeyalgorithm'] = $dhcpdconf['ddnsdomainkeyalgorithm'];
206
	$pconfig['ddnsdomainkey'] = $dhcpdconf['ddnsdomainkey'];
207
	$pconfig['ddnsupdate'] = isset($dhcpdconf['ddnsupdate']);
208
	$pconfig['ddnsforcehostname'] = isset($dhcpdconf['ddnsforcehostname']);
209
	$pconfig['mac_allow'] = $dhcpdconf['mac_allow'];
210
	$pconfig['mac_deny'] = $dhcpdconf['mac_deny'];
211
	list($pconfig['ntp1'], $pconfig['ntp2'], $pconfig['ntp3'] ) = $dhcpdconf['ntpserver'];
212
	$pconfig['tftp'] = $dhcpdconf['tftp'];
213
	$pconfig['ldap'] = $dhcpdconf['ldap'];
214
	$pconfig['netboot'] = isset($dhcpdconf['netboot']);
215
	$pconfig['nextserver'] = $dhcpdconf['nextserver'];
216
	$pconfig['filename'] = $dhcpdconf['filename'];
217
	$pconfig['filename32'] = $dhcpdconf['filename32'];
218
	$pconfig['filename64'] = $dhcpdconf['filename64'];
219
	$pconfig['filename32arm'] = $dhcpdconf['filename32arm'];
220
	$pconfig['filename64arm'] = $dhcpdconf['filename64arm'];
221
	$pconfig['uefihttpboot'] = $dhcpdconf['uefihttpboot'];
222
	$pconfig['rootpath'] = $dhcpdconf['rootpath'];
223
	$pconfig['netmask'] = $dhcpdconf['netmask'];
224
	$pconfig['numberoptions'] = $dhcpdconf['numberoptions'];
225
	$pconfig['statsgraph'] = $dhcpdconf['statsgraph'];
226
	$pconfig['disablepingcheck'] = $dhcpdconf['disablepingcheck'];
227
	$pconfig['ddnsclientupdates'] = $dhcpdconf['ddnsclientupdates'];
228

    
229
	// OMAPI Settings
230
	if(isset($dhcpdconf['omapi_port'])) {
231
		$pconfig['omapi_port'] = $dhcpdconf['omapi_port'];
232
		$pconfig['omapi_key'] = $dhcpdconf['omapi_key'];
233
		$pconfig['omapi_key_algorithm'] = $dhcpdconf['omapi_key_algorithm'];
234
	}
235
}
236

    
237
$ifcfgip = config_get_path("interfaces/{$if}/ipaddr");
238
$ifcfgsn = config_get_path("interfaces/{$if}/subnet");
239

    
240
$subnet_start = gen_subnetv4($ifcfgip, $ifcfgsn);
241
$subnet_end = gen_subnetv4_max($ifcfgip, $ifcfgsn);
242

    
243
function validate_partial_mac_list($maclist) {
244
	$macs = explode(',', $maclist);
245

    
246
	// Loop through and look for invalid MACs.
247
	foreach ($macs as $mac) {
248
		if (!is_macaddr($mac, true)) {
249
			return false;
250
		}
251
	}
252

    
253
	return true;
254
}
255

    
256
if (isset($_POST['save'])) {
257

    
258
	unset($input_errors);
259

    
260
	$pconfig = $_POST;
261

    
262
	$numberoptions = array();
263
	for ($x = 0; $x < 99; $x++) {
264
		if (isset($_POST["number{$x}"]) && ctype_digit($_POST["number{$x}"])) {
265
			if ($_POST["number{$x}"] < 1 || $_POST["number{$x}"] > 254) {
266
				$input_errors[] = gettext("The DHCP option must be a number between 1 and 254.");
267
				continue;
268
			}
269
			$numbervalue = array();
270
			$numbervalue['number'] = htmlspecialchars($_POST["number{$x}"]);
271
			$numbervalue['type'] = htmlspecialchars($_POST["itemtype{$x}"]);
272
			$numbervalue['value'] = base64_encode($_POST["value{$x}"]);
273
			$numberoptions['item'][] = $numbervalue;
274
		}
275
	}
276

    
277
	// Reload the new pconfig variable that the form uses.
278
	$pconfig['numberoptions'] = $numberoptions;
279

    
280
	/* input validation */
281

    
282
	/*
283
	 * Check the OMAPI settings
284
	 * - Make sure that if the port is defined, that it is valid and isn't in use
285
	 * - Make sure the key is defined and the length is appropriate for the selected algorithm
286
	 * - Generate a new key if selected
287
	 */
288
	if (!empty($_POST['omapi_port'])) {
289
		// Check the port entry
290
		switch(true){
291
			case !is_port($_POST['omapi_port']) || $_POST['omapi_port'] <= 1024:
292
				$input_errors[] = gettext("The specified OMAPI port number is invalid. Port number must be between 1024 and 65635.");
293
				break;
294
			case is_port_in_use($_POST['omapi_port']) && $_POST['omapi_port'] != $dhcpdconf['omapi_port']:
295
				$input_errors[] = gettext("Specified port number for OMAPI is in use. Please choose another port or consider using the default.");
296
				break;
297
		}
298

    
299
		// Define the minimum base64 character length for each algorithm
300
		$key_char_len_by_alg = array(
301
			'hmac-md5' => 24,
302
			'hmac-sha1' => 28,
303
			'hmac-sha224' => 40,
304
			'hmac-sha256' => 44,
305
			'hmac-sha384' => 64,
306
			'hmac-sha512' => 88
307
		);
308

    
309
		// Generate a key if checked
310
		if ($_POST['omapi_gen_key'] == "yes") {
311
			// Figure out the key bits from the selected algorithm
312
			switch ($_POST['omapi_key_algorithm']) {
313
				case "hmac-md5":
314
					$key_bit_len = 128;
315
					break;
316
				case "hmac-sha1":
317
					$key_bit_len = 160;
318
					break;
319
				default:
320
					$key_bit_len = str_replace("hmac-sha","",$_POST['omapi_key_algorithm']);
321
					break;
322
			}
323

    
324
			// Convert the bits to bytes
325
			$key_bytes_len = $key_bit_len / 8; // 8 bits = 1 Byte
326

    
327
			// Generate random bytes based on key length
328
			$ran_bytes = openssl_random_pseudo_bytes($key_bytes_len);
329

    
330
			// Encode the bytes to get the key string
331
			$key_str = base64_encode($ran_bytes);
332

    
333
			// Set the key
334
			$_POST['omapi_key'] = $key_str;
335
			$pconfig['omapi_key'] = $key_str;
336

    
337
			// Uncheck the generate box
338
			unset($_POST['omapi_gen_key']);
339
			unset($pconfig['omapi_gen_key']);
340
		} elseif (!empty($_POST['omapi_key'])) { // Check the key if it's not being generated
341
			if (strlen($_POST['omapi_key']) < $key_char_len_by_alg[$_POST['omapi_key_algorithm']]) {
342
				$input_errors[] = gettext("Please specify a valid OMAPI key. Key does not meet the minimum length requirement of {$key_char_len_by_alg[$_POST['omapi_key_algorithm']]} for the selected algorithm {$_POST['omapi_key_algorithm']}.");
343
			}
344
		} else {
345
			$input_errors[] = gettext("A key is required when OMAPI is enabled (port specified).");
346
		}
347
	}
348

    
349
	// Note: if DHCP Server is not enabled, then it is OK to adjust other parameters without specifying range from-to.
350
	if ($_POST['enable'] || is_numeric($pool) || $act == "newpool") {
351
		$reqdfields = explode(" ", "range_from range_to");
352
		$reqdfieldsn = array(gettext("Range begin"), gettext("Range end"));
353

    
354
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
355
	}
356

    
357
	if (($_POST['nonak']) && !empty($_POST['failover_peerip'])) {
358
		$input_errors[] = gettext("Ignore Denied Clients may not be used when a Failover Peer IP is defined.");
359
	}
360

    
361
	if ($_POST['range_from'] && !is_ipaddrv4($_POST['range_from'])) {
362
		$input_errors[] = gettext("A valid IPv4 address must be specified for range from.");
363
	}
364
	if ($_POST['range_to'] && !is_ipaddrv4($_POST['range_to'])) {
365
		$input_errors[] = gettext("A valid IPv4 address must be specified for range to.");
366
	}
367
	if (($_POST['range_from'] && !$_POST['range_to']) || ($_POST['range_to'] && !$_POST['range_from'])) {
368
		$input_errors[] = gettext("Range From and Range To must both be entered.");
369
	}
370
	if (($_POST['gateway'] && $_POST['gateway'] != "none" && !is_ipaddrv4($_POST['gateway']))) {
371
		$input_errors[] = gettext("A valid IP address must be specified for the gateway.");
372
	}
373
	if (($_POST['wins1'] && !is_ipaddrv4($_POST['wins1'])) || ($_POST['wins2'] && !is_ipaddrv4($_POST['wins2']))) {
374
		$input_errors[] = gettext("A valid IP address must be specified for the primary/secondary WINS servers.");
375
	}
376
	$parent_ip = get_interface_ip($_POST['if']);
377
	if (is_ipaddrv4($parent_ip) && $_POST['gateway'] && $_POST['gateway'] != "none") {
378
		$parent_sn = get_interface_subnet($_POST['if']);
379
		if (!ip_in_subnet($_POST['gateway'], gen_subnet($parent_ip, $parent_sn) . "/" . $parent_sn) && !ip_in_interface_alias_subnet($_POST['if'], $_POST['gateway'])) {
380
			$input_errors[] = sprintf(gettext("The gateway address %s does not lie within the chosen interface's subnet."), $_POST['gateway']);
381
		}
382
	}
383

    
384
	if (($_POST['dns1'] && !is_ipaddrv4($_POST['dns1'])) || ($_POST['dns2'] && !is_ipaddrv4($_POST['dns2'])) || ($_POST['dns3'] && !is_ipaddrv4($_POST['dns3'])) || ($_POST['dns4'] && !is_ipaddrv4($_POST['dns4']))) {
385
		$input_errors[] = gettext("A valid IP address must be specified for each of the DNS servers.");
386
	}
387

    
388
	if ($_POST['deftime'] && (!is_numeric($_POST['deftime']) || ($_POST['deftime'] < 60))) {
389
		$input_errors[] = gettext("The default lease time must be at least 60 seconds.");
390
	}
391

    
392
	// Default value if it's empty
393
	$deftime = (is_numeric($_POST['deftime'])) ? $_POST['deftime'] : 7200;
394
	foreach (config_get_path('captiveportal', []) as $cpZone => $cpdata) {
395
		if (!isset($cpdata['enable'])) {
396
			continue;
397
		}
398
		if (!isset($cpdata['timeout']) || !is_numeric($cpdata['timeout'])) {
399
			continue;
400
		}
401
		$cp_ifs = explode(',', $cpdata['interface']);
402
		if (!in_array($if, $cp_ifs)) {
403
			continue;
404
		}
405
		if ($cpdata['timeout'] > $deftime) {
406
			$input_errors[] = sprintf(gettext(
407
				'The Captive Portal zone (%1$s) has Hard Timeout parameter set to a value bigger than Default lease time (%2$s).'), $cpZone, $deftime);
408
		}
409
	}
410

    
411
	if ($_POST['maxtime'] && (!is_numeric($_POST['maxtime']) || ($_POST['maxtime'] < 60) || ($_POST['maxtime'] < $_POST['deftime']))) {
412
		$input_errors[] = gettext("The maximum lease time must be at least 60 seconds, and the same value or greater than the default lease time.");
413
	}
414
	if ($_POST['ddnsupdate']) {
415
		if (!is_domain($_POST['ddnsdomain'])) {
416
			$input_errors[] = gettext("A valid domain name must be specified for the dynamic DNS registration.");
417
		}
418
		if (!is_ipaddr($_POST['ddnsdomainprimary'])) {
419
			$input_errors[] = gettext("A valid primary domain name server IP address must be specified for the dynamic domain name.");
420
		}
421
		if (!empty($_POST['ddnsdomainsecondary']) && !is_ipaddr($_POST['ddnsdomainsecondary'])) {
422
			$input_errors[] = gettext("A valid secondary domain name server IP address must be specified for the dynamic domain name.");
423
		}
424
		if (!$_POST['ddnsdomainkeyname'] || !$_POST['ddnsdomainkeyalgorithm'] || !$_POST['ddnsdomainkey']) {
425
			$input_errors[] = gettext("A valid domain key name, algorithm and secret must be specified.");
426
		}
427
		if (preg_match('/[^A-Za-z0-9\.\-\_]/', $_POST['ddnsdomainkeyname'])) {
428
			$input_errors[] = gettext("The domain key name may only contain the characters a-z, A-Z, 0-9, '-', '_' and '.'");
429
		}
430
		if ($_POST['ddnsdomainkey'] && !base64_decode($_POST['ddnsdomainkey'], true)) {
431
			$input_errors[] = gettext("The domain key secret must be a Base64 encoded value.");
432
		}
433
	}
434
	if ($_POST['domainsearchlist']) {
435
		$domain_array = preg_split("/[ ;]+/", $_POST['domainsearchlist']);
436
		foreach ($domain_array as $curdomain) {
437
			if (!is_domain($curdomain)) {
438
				$input_errors[] = gettext("A valid domain search list must be specified.");
439
				break;
440
			}
441
		}
442
	}
443

    
444
	// Validate MACs
445
	if (!empty($_POST['mac_allow']) && !validate_partial_mac_list($_POST['mac_allow'])) {
446
		$input_errors[] = gettext("If a mac allow list is specified, it must contain only valid partial MAC addresses.");
447
	}
448
	if (!empty($_POST['mac_deny']) && !validate_partial_mac_list($_POST['mac_deny'])) {
449
		$input_errors[] = gettext("If a mac deny list is specified, it must contain only valid partial MAC addresses.");
450
	}
451

    
452
	if (($_POST['ntp1'] && (!is_ipaddrv4($_POST['ntp1']) && !is_hostname($_POST['ntp1']))) ||
453
	    ($_POST['ntp2'] && (!is_ipaddrv4($_POST['ntp2']) && !is_hostname($_POST['ntp2']))) ||
454
	    ($_POST['ntp3'] && (!is_ipaddrv4($_POST['ntp3']) && !is_hostname($_POST['ntp3'])))) {
455
		$input_errors[] = gettext("A valid IP address or hostname must be specified for the primary/secondary NTP servers.");
456
	}
457
	if ($_POST['domain'] && (!is_domain($_POST['domain'], false, false))) {
458
		$input_errors[] = gettext("A valid domain name must be specified for the DNS domain.");
459
	}
460
	if ($_POST['tftp'] && !is_ipaddrv4($_POST['tftp']) && !is_domain($_POST['tftp']) && !filter_var($_POST['tftp'], FILTER_VALIDATE_URL)) {
461
		$input_errors[] = gettext("A valid IP address, hostname or URL must be specified for the TFTP server.");
462
	}
463
	if (($_POST['nextserver'] && !is_ipaddrv4($_POST['nextserver']))) {
464
		$input_errors[] = gettext("A valid IP address must be specified for the network boot server.");
465
	}
466

    
467
	if (gen_subnet($ifcfgip, $ifcfgsn) == $_POST['range_from']) {
468
		$input_errors[] = gettext("The network address cannot be used in the starting subnet range.");
469
	}
470
	if (gen_subnet_max($ifcfgip, $ifcfgsn) == $_POST['range_to']) {
471
		$input_errors[] = gettext("The broadcast address cannot be used in the ending subnet range.");
472
	}
473

    
474
	// Disallow a range that includes the virtualip
475
	foreach (config_get_path('virtualip/vip', []) as $vip) {
476
		if ($vip['interface'] == $if) {
477
			if ($vip['subnet'] && is_inrange_v4($vip['subnet'], $_POST['range_from'], $_POST['range_to'])) {
478
				$input_errors[] = sprintf(gettext("The subnet range cannot overlap with virtual IP address %s."), $vip['subnet']);
479
			}
480
		}
481
	}
482

    
483
	$noip = false;
484
	if (is_array($a_maps)) {
485
		foreach ($a_maps as $map) {
486
			if (empty($map['ipaddr'])) {
487
				$noip = true;
488
			}
489
		}
490
	}
491

    
492
	if ($_POST['staticarp'] && $noip) {
493
		$input_errors[] = gettext("Cannot enable static ARP when there are static map entries without IP addresses. Ensure all static maps have IP addresses and try again.");
494
	}
495

    
496
	if (is_array($pconfig['numberoptions']['item'])) {
497
		foreach ($pconfig['numberoptions']['item'] as $numberoption) {
498
			$numberoption_value = base64_decode($numberoption['value']);
499
			if ($numberoption['type'] == 'text' && strstr($numberoption_value, '"')) {
500
				$input_errors[] = gettext("Text type cannot include quotation marks.");
501
			} else if ($numberoption['type'] == 'string' && !preg_match('/^"[^"]*"$/', $numberoption_value) && !preg_match('/^[0-9a-f]{2}(?:\:[0-9a-f]{2})*$/i', $numberoption_value)) {
502
				$input_errors[] = gettext("String type must be enclosed in quotes like \"this\" or must be a series of octets specified in hexadecimal, separated by colons, like 01:23:45:67:89:ab:cd:ef");
503
			} else if ($numberoption['type'] == 'boolean' && $numberoption_value != 'true' && $numberoption_value != 'false' && $numberoption_value != 'on' && $numberoption_value != 'off') {
504
				$input_errors[] = gettext("Boolean type must be true, false, on, or off.");
505
			} else if ($numberoption['type'] == 'unsigned integer 8' && (!is_numeric($numberoption_value) || $numberoption_value < 0 || $numberoption_value > 255)) {
506
				$input_errors[] = gettext("Unsigned 8-bit integer type must be a number in the range 0 to 255.");
507
			} else if ($numberoption['type'] == 'unsigned integer 16' && (!is_numeric($numberoption_value) || $numberoption_value < 0 || $numberoption_value > 65535)) {
508
				$input_errors[] = gettext("Unsigned 16-bit integer type must be a number in the range 0 to 65535.");
509
			} else if ($numberoption['type'] == 'unsigned integer 32' && (!is_numeric($numberoption_value) || $numberoption_value < 0 || $numberoption_value > 4294967295)) {
510
				$input_errors[] = gettext("Unsigned 32-bit integer type must be a number in the range 0 to 4294967295.");
511
			} else if ($numberoption['type'] == 'signed integer 8' && (!is_numeric($numberoption_value) || $numberoption_value < -128 || $numberoption_value > 127)) {
512
				$input_errors[] = gettext("Signed 8-bit integer type must be a number in the range -128 to 127.");
513
			} else if ($numberoption['type'] == 'signed integer 16' && (!is_numeric($numberoption_value) || $numberoption_value < -32768 || $numberoption_value > 32767)) {
514
				$input_errors[] = gettext("Signed 16-bit integer type must be a number in the range -32768 to 32767.");
515
			} else if ($numberoption['type'] == 'signed integer 32' && (!is_numeric($numberoption_value) || $numberoption_value < -2147483648 || $numberoption_value > 2147483647)) {
516
				$input_errors[] = gettext("Signed 32-bit integer type must be a number in the range -2147483648 to 2147483647.");
517
			} else if ($numberoption['type'] == 'ip-address' && !is_ipaddrv4($numberoption_value) && !is_hostname($numberoption_value)) {
518
				$input_errors[] = gettext("IP address or host type must be an IP address or host name.");
519
			}
520
		}
521
	}
522

    
523
	if ((!isset($pool) || !is_numeric($pool)) && $act != "newpool") {
524
		/* If enabling DHCP Server, make sure that the DHCP Relay isn't enabled on this interface */
525
		if ($_POST['enable'] && config_path_enabled('dhcrelay') &&
526
		    (stristr(config_get_path('dhcrelay/interface', ''), $if) !== false)) {
527
			$input_errors[] = sprintf(gettext(
528
			    "The DHCP relay on the %s interface must be disabled before enabling the DHCP server."),
529
			    $iflist[$if]);
530
		}
531

    
532
		/* If disabling DHCP Server, make sure that DHCP registration isn't enabled for DNS forwarder/resolver */
533
		if (!$_POST['enable']) {
534
			/* Find out how many other interfaces have DHCP enabled. */
535
			$dhcp_enabled_count = 0;
536
			foreach (config_get_path('dhcpd', []) as $dhif => $dhcps) {
537
				if ($dhif == $if) {
538
					/* Skip this interface, we only want to know how many others are enabled. */
539
					continue;
540
				}
541
				if (config_path_enabled("dhcpd/{$dhif}")) {
542
					$dhcp_enabled_count++;
543
				}
544
			}
545

    
546
			if (config_path_enabled('dnsmasq') &&
547
			    ($dhcp_enabled_count == 0) &&
548
			    (config_path_enabled('dnsmasq', 'regdhcp') ||
549
			    config_path_enabled('dnsmasq', 'regdhcpstatic') ||
550
			    config_path_enabled('dnsmasq', 'dhcpfirst'))) {
551
				$input_errors[] = gettext(
552
				    "DHCP Registration features in the DNS Forwarder are active and require at least one enabled DHCP Server.");
553
			}
554
			if (config_path_enabled('unbound') &&
555
			    ($dhcp_enabled_count == 0) &&
556
			    (config_path_enabled('unbound', 'regdhcp') ||
557
			    config_path_enabled('unbound', 'regdhcpstatic'))) {
558
				$input_errors[] = gettext(
559
				    "DHCP Registration features in the DNS Resolver are active and require at least one enabled DHCP Server.");
560
			}
561
		}
562
	}
563

    
564
	// If nothing is wrong so far, and we have range from and to, then check conditions related to the values of range from and to.
565
	if (!$input_errors && $_POST['range_from'] && $_POST['range_to']) {
566
		/* make sure the range lies within the current subnet */
567
		if (ip_greater_than($_POST['range_from'], $_POST['range_to'])) {
568
			$input_errors[] = gettext("The range is invalid (first element higher than second element).");
569
		}
570

    
571
		if (!is_inrange_v4($_POST['range_from'], $subnet_start, $subnet_end) ||
572
			!is_inrange_v4($_POST['range_to'], $subnet_start, $subnet_end)) {
573
			$input_errors[] = gettext("The specified range lies outside of the current subnet.");
574
		}
575

    
576
		if (is_numeric($pool) || ($act == "newpool")) {
577
			if (is_inrange_v4($_POST['range_from'],
578
				config_get_path("dhcpd/{$if}/range/from"),
579
				config_get_path("dhcpd/{$if}/range/to") ||
580
				is_inrange_v4($_POST['range_to'],
581
				config_get_path("dhcpd/{$if}/range/from"),
582
				config_get_path("dhcpd/{$if}/range/to")))) {
583
				$input_errors[] = gettext("The specified range must not be within the DHCP range for this interface.");
584
			}
585
		}
586

    
587
		foreach ($a_pools as $id => $p) {
588
			if (is_numeric($pool) && ($id == $pool)) {
589
				continue;
590
			}
591

    
592
			if (is_inrange_v4($_POST['range_from'],
593
				$p['range']['from'], $p['range']['to']) ||
594
				is_inrange_v4($_POST['range_to'],
595
				$p['range']['from'], $p['range']['to'])) {
596
				$input_errors[] = gettext("The specified range must not be within the range configured on a DHCP pool for this interface.");
597
				break;
598
			}
599
		}
600

    
601
		if (is_array($a_maps)) {
602
			foreach ($a_maps as $map) {
603
				if (empty($map['ipaddr'])) {
604
					continue;
605
				}
606
				if (is_inrange_v4($map['ipaddr'], $_POST['range_from'], $_POST['range_to'])) {
607
					$input_errors[] = sprintf(gettext("The DHCP range cannot overlap any static DHCP mappings."));
608
					break;
609
				}
610
			}
611
		}
612
	}
613

    
614
	if (!$input_errors) {
615
		if (!is_numeric($pool)) {
616
			if ($act == "newpool") {
617
				$dhcpdconf = array();
618
			} else {
619
				config_init_path("dhcpd/{$if}");
620
				$dhcpdconf = config_get_path("dhcpd/{$if}");
621
			}
622
		} else {
623
			if (is_array($a_pools[$pool])) {
624
				$dhcpdconf = $a_pools[$pool];
625
			} else {
626
				// Someone specified a pool but it doesn't exist. Punt.
627
				header("Location: services_dhcp.php");
628
				exit;
629
			}
630
		}
631
		if (!is_array($dhcpdconf)) {
632
			$dhcpdconf = array();
633
		}
634
		if (!is_array($dhcpdconf['range'])) {
635
			$dhcpdconf['range'] = array();
636
		}
637

    
638
		$dhcpd_enable_changed = false;
639

    
640
		// Global Options
641
		if (!is_numeric($pool) && !($act == "newpool")) {
642
			$old_dhcpd_enable = isset($dhcpdconf['enable']);
643
			$new_dhcpd_enable = ($_POST['enable']) ? true : false;
644
			if ($old_dhcpd_enable != $new_dhcpd_enable) {
645
				/* DHCP has been enabled or disabled. The pf ruleset will need to be rebuilt to allow or disallow DHCP. */
646
				$dhcpd_enable_changed = true;
647
			}
648

    
649
			$dhcpdconf['enable'] = $new_dhcpd_enable;
650
			$dhcpdconf['staticarp'] = ($_POST['staticarp']) ? true : false;
651
			$previous = $dhcpdconf['failover_peerip'];
652
			if ($previous != $_POST['failover_peerip']) {
653
				mwexec("/bin/rm -rf /var/dhcpd/var/db/*");
654
			}
655

    
656
			$dhcpdconf['failover_peerip'] = $_POST['failover_peerip'];
657
			// dhcpleaseinlocaltime is global to all interfaces. So update the setting on all interfaces.
658
			foreach (config_get_path('dhcpd', []) as $dhcpdifkey => $keyvalue) {
659
				if (empty($keyvalue)) {
660
					continue;
661
				}
662
				if (isset($_POST['dhcpleaseinlocaltime'])) {
663
					config_set_path("dhcpd/{$dhcpdifkey}/dhcpleaseinlocaltime", $_POST['dhcpleaseinlocaltime']);
664
				} else {
665
					config_del_path("dhcpd/{$dhcpdifkey}/dhcpleaseinlocaltime");
666
				}
667
			}
668
		} else {
669
			// Options that exist only in pools
670
			$dhcpdconf['descr'] = $_POST['descr'];
671
		}
672

    
673
		// Options that can be global or per-pool.
674
		$dhcpdconf['range']['from'] = $_POST['range_from'];
675
		$dhcpdconf['range']['to'] = $_POST['range_to'];
676
		$dhcpdconf['defaultleasetime'] = $_POST['deftime'];
677
		$dhcpdconf['maxleasetime'] = $_POST['maxtime'];
678
		$dhcpdconf['netmask'] = $_POST['netmask'];
679

    
680
		unset($dhcpdconf['winsserver']);
681
		if ($_POST['wins1']) {
682
			$dhcpdconf['winsserver'][] = $_POST['wins1'];
683
		}
684
		if ($_POST['wins2']) {
685
			$dhcpdconf['winsserver'][] = $_POST['wins2'];
686
		}
687

    
688
		unset($dhcpdconf['dnsserver']);
689
		if ($_POST['dns1']) {
690
			$dhcpdconf['dnsserver'][] = $_POST['dns1'];
691
		}
692
		if ($_POST['dns2']) {
693
			$dhcpdconf['dnsserver'][] = $_POST['dns2'];
694
		}
695
		if ($_POST['dns3']) {
696
			$dhcpdconf['dnsserver'][] = $_POST['dns3'];
697
		}
698
		if ($_POST['dns4']) {
699
			$dhcpdconf['dnsserver'][] = $_POST['dns4'];
700
		}
701

    
702
		$dhcpdconf['gateway'] = $_POST['gateway'];
703
		$dhcpdconf['domain'] = $_POST['domain'];
704
		$dhcpdconf['domainsearchlist'] = $_POST['domainsearchlist'];
705
		$dhcpdconf['ignorebootp'] = ($_POST['ignorebootp']) ? true : false;
706

    
707
		if (in_array($_POST['denyunknown'], array("enabled", "class"))) {
708
			$dhcpdconf['denyunknown'] = $_POST['denyunknown'];
709
		} else {
710
			unset($dhcpdconf['denyunknown']);
711
		}
712

    
713
		$dhcpdconf['ignoreclientuids'] = ($_POST['ignoreclientuids']) ? true : false;
714
		$dhcpdconf['nonak'] = ($_POST['nonak']) ? true : false;
715
		$dhcpdconf['ddnsdomain'] = $_POST['ddnsdomain'];
716
		$dhcpdconf['ddnsdomainprimary'] = $_POST['ddnsdomainprimary'];
717
		$dhcpdconf['ddnsdomainsecondary'] = (!empty($_POST['ddnsdomainsecondary'])) ? $_POST['ddnsdomainsecondary'] : '';
718
		$dhcpdconf['ddnsdomainkeyname'] = $_POST['ddnsdomainkeyname'];
719
		$dhcpdconf['ddnsdomainkeyalgorithm'] = $_POST['ddnsdomainkeyalgorithm'];
720
		$dhcpdconf['ddnsdomainkey'] = $_POST['ddnsdomainkey'];
721
		$dhcpdconf['ddnsupdate'] = ($_POST['ddnsupdate']) ? true : false;
722
		$dhcpdconf['ddnsforcehostname'] = ($_POST['ddnsforcehostname']) ? true : false;
723
		$dhcpdconf['mac_allow'] = $_POST['mac_allow'];
724
		$dhcpdconf['mac_deny'] = $_POST['mac_deny'];
725
		if ($_POST['disablepingcheck']) {
726
			$dhcpdconf['disablepingcheck'] = $_POST['disablepingcheck'];
727
		} else {
728
			unset($dhcpdconf['disablepingcheck']);
729
		}
730
		$dhcpdconf['ddnsclientupdates'] = $_POST['ddnsclientupdates'];
731

    
732
		unset($dhcpdconf['ntpserver']);
733
		if ($_POST['ntp1']) {
734
			$dhcpdconf['ntpserver'][] = $_POST['ntp1'];
735
		}
736
		if ($_POST['ntp2']) {
737
			$dhcpdconf['ntpserver'][] = $_POST['ntp2'];
738
		}
739
		if ($_POST['ntp3']) {
740
			$dhcpdconf['ntpserver'][] = $_POST['ntp3'];
741
		}
742

    
743
		$dhcpdconf['tftp'] = $_POST['tftp'];
744
		$dhcpdconf['ldap'] = $_POST['ldap'];
745
		$dhcpdconf['netboot'] = ($_POST['netboot']) ? true : false;
746
		$dhcpdconf['nextserver'] = $_POST['nextserver'];
747
		$dhcpdconf['filename'] = $_POST['filename'];
748
		$dhcpdconf['filename32'] = $_POST['filename32'];
749
		$dhcpdconf['filename64'] = $_POST['filename64'];
750
		$dhcpdconf['filename32arm'] = $_POST['filename32arm'];
751
		$dhcpdconf['filename64arm'] = $_POST['filename64arm'];
752
		$dhcpdconf['uefihttpboot'] = $_POST['uefihttpboot'];
753
		$dhcpdconf['rootpath'] = $_POST['rootpath'];
754

    
755
		if (empty($_POST['statsgraph']) == isset($dhcpdconf['statsgraph'])) {
756
			$enable_rrd_graphing = true;
757
		}
758
		if (!empty($_POST['statsgraph'])) {
759
			$dhcpdconf['statsgraph'] = $_POST['statsgraph'];
760
		} elseif (isset($dhcpdconf['statsgraph'])) {
761
			unset($dhcpdconf['statsgraph']);
762
		}
763
		if ($enable_rrd_graphing) {
764
			enable_rrd_graphing();
765
		}
766

    
767
		// Handle the custom options rowhelper
768
		if (isset($dhcpdconf['numberoptions']['item'])) {
769
			unset($dhcpdconf['numberoptions']['item']);
770
		}
771

    
772
		$dhcpdconf['numberoptions'] = $numberoptions;
773

    
774
		if (is_numeric($pool) && is_array($a_pools[$pool])) {
775
			$a_pools[$pool] = $dhcpdconf;
776
		} elseif ($act == "newpool") {
777
			$a_pools[] = $dhcpdconf;
778
		} else {
779
			config_set_path("dhcpd/{$if}", $dhcpdconf);
780
		}
781

    
782
		// OMAPI Settings
783
		if ($_POST['omapi_port'] == ""){
784
			unset($dhcpdconf['omapi_port']);
785
			unset($dhcpdconf['omapi_key']);
786
			unset($dhcpdconf['omapi_key_algorithm']);
787

    
788
			unset($pconfig['omapi_port']);
789
			unset($pconfig['omapi_key']);
790
			unset($pconfig['omapi_key_algorithm']);
791
		} else {
792
			$dhcpdconf['omapi_port'] = $_POST['omapi_port'];
793
			$dhcpdconf['omapi_key'] = $_POST['omapi_key'];
794
			$dhcpdconf['omapi_key_algorithm'] = $_POST['omapi_key_algorithm'];
795
		}
796

    
797
		write_config(gettext("DHCP Server - Settings changed for interface " . strtoupper($if)));
798
	}
799
}
800

    
801
if ((isset($_POST['save']) || isset($_POST['apply'])) && (!$input_errors)) {
802
	$changes_applied = true;
803
	$retval = 0;
804
	$retvaldhcp = 0;
805
	$retvaldns = 0;
806
	/* dnsmasq_configure calls dhcpd_configure */
807
	/* no need to restart dhcpd twice */
808
	if (config_path_enabled('dnsmasq') &&
809
	    config_path_enabled('dnsmasq', 'regdhcpstatic')) {
810
		$retvaldns |= services_dnsmasq_configure();
811
		if ($retvaldns == 0) {
812
			clear_subsystem_dirty('hosts');
813
			clear_subsystem_dirty('staticmaps');
814
		}
815
	} elseif (config_path_enabled('unbound') &&
816
		  config_path_enabled('unbound', 'regdhcpstatic')) {
817
		$retvaldns |= services_unbound_configure();
818
		if ($retvaldns == 0) {
819
			clear_subsystem_dirty('unbound');
820
			clear_subsystem_dirty('hosts');
821
			clear_subsystem_dirty('staticmaps');
822
		}
823
	} else {
824
		$retvaldhcp |= services_dhcpd_configure();
825
		if ($retvaldhcp == 0) {
826
			clear_subsystem_dirty('staticmaps');
827
		}
828
	}
829
	/* BIND package - Bug #3710 */
830
	if (!function_exists('is_package_installed')) {
831
		require_once('pkg-utils.inc');
832
	}
833
	if (is_package_installed('pfSense-pkg-bind') &&
834
	    config_path_enabled('installedpackages/bind/config/0', 'enable_bind')) {
835
		$reloadbind = false;
836
		$bindzone = config_get_path('installedpackages/bindzone/config', []);
837

    
838
		for ($x = 0; $x < sizeof($bindzone); $x++) {
839
			$zone = $bindzone[$x];
840
			if ($zone['regdhcpstatic'] == 'on') {
841
				$reloadbind = true;
842
				break;
843
			}
844
		}
845
		if ($reloadbind === true) {
846
			if (file_exists("/usr/local/pkg/bind.inc")) {
847
				require_once("/usr/local/pkg/bind.inc");
848
				bind_sync();
849
			}
850
		}
851
	}
852
	if ($dhcpd_enable_changed) {
853
		$retvalfc |= filter_configure();
854
	}
855

    
856
	if ($retvaldhcp == 1 || $retvaldns == 1 || $retvalfc == 1) {
857
		$retval = 1;
858
	}
859
}
860

    
861
if ($act == "delpool") {
862
	if ($a_pools[$_POST['id']]) {
863
		unset($a_pools[$_POST['id']]);
864
		write_config("DHCP Server pool deleted");
865
		header("Location: services_dhcp.php?if={$if}");
866
		exit;
867
	}
868
}
869

    
870
if ($act == "del") {
871
	if (isset($a_maps[$_POST['id']])) {
872
		/* Remove static ARP entry, if necessary */
873
		if (isset($a_maps[$_POST['id']]['arp_table_static_entry'])) {
874
			mwexec("/usr/sbin/arp -d " . escapeshellarg($a_maps[$_POST['id']]['ipaddr']));
875
		}
876
		unset($a_maps[$_POST['id']]);
877
		write_config("DHCP Server static map deleted");
878
		if (config_path_enabled("dhcpd/{$if}")) {
879
			mark_subsystem_dirty('staticmaps');
880
			if (config_path_enabled('dnsmasq') && config_get_path('dnsmasq/regdhcpstatic', false)) {
881
				mark_subsystem_dirty('hosts');
882
			}
883
		}
884

    
885
		header("Location: services_dhcp.php?if={$if}");
886
		exit;
887
	}
888
}
889

    
890
// Build an HTML table that can be inserted into a Form_StaticText element
891
function build_pooltable() {
892
	global $a_pools, $if;
893

    
894
	$pooltbl =	'<div class="table-responsive">';
895
	$pooltbl .=		'<table class="table table-striped table-hover table-condensed">';
896
	$pooltbl .=			'<thead>';
897
	$pooltbl .=				'<tr>';
898
	$pooltbl .=					'<th>' . gettext("Pool Start") . '</th>';
899
	$pooltbl .=					'<th>' . gettext("Pool End") . '</th>';
900
	$pooltbl .=					'<th>' . gettext("Description") . '</th>';
901
	$pooltbl .=					'<th>' . gettext("Actions") . '</th>';
902
	$pooltbl .=				'</tr>';
903
	$pooltbl .=			'</thead>';
904
	$pooltbl .=			'<tbody>';
905

    
906
	if (is_array($a_pools)) {
907
		$i = 0;
908
		foreach ($a_pools as $poolent) {
909
			if (!empty($poolent['range']['from']) && !empty($poolent['range']['to'])) {
910
				$pooltbl .= '<tr>';
911
				$pooltbl .= '<td ondblclick="document.location=\'services_dhcp.php?if=' . htmlspecialchars($if) . '&pool=' . $i . '\';">' .
912
							htmlspecialchars($poolent['range']['from']) . '</td>';
913

    
914
				$pooltbl .= '<td ondblclick="document.location=\'services_dhcp.php?if=' . htmlspecialchars($if) . '&pool=' . $i . '\';">' .
915
							htmlspecialchars($poolent['range']['to']) . '</td>';
916

    
917
				$pooltbl .= '<td ondblclick="document.location=\'services_dhcp.php?if=' . htmlspecialchars($if) . '&pool=' . $i . '\';">' .
918
							htmlspecialchars($poolent['descr']) . '</td>';
919

    
920
				$pooltbl .= '<td><a class="fa fa-pencil" title="'. gettext("Edit pool") . '" href="services_dhcp.php?if=' . htmlspecialchars($if) . '&pool=' . $i . '"></a>';
921

    
922
				$pooltbl .= ' <a class="fa fa-trash" title="'. gettext("Delete pool") . '" href="services_dhcp.php?if=' . htmlspecialchars($if) . '&act=delpool&id=' . $i . '" usepost></a></td>';
923
				$pooltbl .= '</tr>';
924
			}
925
		$i++;
926
		}
927
	}
928

    
929
	$pooltbl .=			'</tbody>';
930
	$pooltbl .=		'</table>';
931
	$pooltbl .= '</div>';
932

    
933
	return($pooltbl);
934
}
935

    
936
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
937
$pglinks = array("", "services_dhcp.php");
938

    
939
if (!empty($if) && isset($iflist[$if])) {
940
	$pgtitle[] = $iflist[$if];
941
	$pglinks[] = "@self";
942
}
943
$shortcut_section = "dhcp";
944

    
945
include("head.inc");
946

    
947
if ($input_errors) {
948
	print_input_errors($input_errors);
949
}
950

    
951
if ($changes_applied) {
952
	print_apply_result_box($retval);
953
}
954

    
955
if (is_subsystem_dirty('staticmaps')) {
956
	print_apply_box(gettext("The static mapping configuration has been changed.") . "<br />" . gettext("The changes must be applied for them to take effect."));
957
}
958

    
959
/* active tabs */
960
$tab_array = array();
961
$tabscounter = 0;
962
$i = 0;
963
$have_small_subnet = false;
964

    
965
foreach ($iflist as $ifent => $ifname) {
966
	if (is_dhcrelay_enabled($ifent)) {
967
		continue;
968
	}
969
	$oc = config_get_path("interfaces/{$ifent}");
970

    
971
	/* Not static IPv4 or subnet >= 31 */
972
	if ($oc['subnet'] >= 31) {
973
		$have_small_subnet = true;
974
		$example_name = $ifname;
975
		$example_cidr = $oc['subnet'];
976
		continue;
977
	}
978
	if (!is_ipaddrv4($oc['ipaddr']) || empty($oc['subnet'])) {
979
		continue;
980
	}
981

    
982
	$tab_array[] = array($ifname, ($ifent === $if), 'services_dhcp.php?if='.$ifent);
983
	$tabscounter++;
984
}
985

    
986
if ($tabscounter == 0) {
987
	if ($have_small_subnet) {
988
		$sentence2 = sprintf(gettext('%1$s has a CIDR mask of %2$s, which does not contain enough addresses.'), htmlspecialchars($example_name), htmlspecialchars($example_cidr));
989
	} else {
990
		$sentence2 = gettext("This system has no interfaces configured with a static IPv4 address.");
991
	}
992
	print_info_box(gettext("The DHCP Server requires a static IPv4 subnet large enough to serve addresses to clients.") . " " . $sentence2);
993
	include("foot.inc");
994
	exit;
995
}
996

    
997
display_top_tabs($tab_array);
998

    
999
$form = new Form();
1000

    
1001
$section = new Form_Section(gettext('General Options'));
1002

    
1003
if (!is_numeric($pool) && !($act === 'newpool')) {
1004
	$input = new Form_Checkbox(
1005
		'enable',
1006
		gettext('Enable'),
1007
		sprintf(gettext('Enable DHCP Server on %s interface'), htmlspecialchars($iflist[$if])),
1008
		$pconfig['enable']
1009
	);
1010
	$section->addInput($input);
1011
} else {
1012
	print_info_box(gettext('Editing pool-specific options. To return to the Interface, click its tab above.'), 'info', false);
1013
}
1014

    
1015
$section->addInput(new Form_Checkbox(
1016
	'ignorebootp',
1017
	'BOOTP',
1018
	gettext('Ignore BOOTP queries'),
1019
	$pconfig['ignorebootp']
1020
));
1021

    
1022
$section->addInput(new Form_Select(
1023
	'denyunknown',
1024
	gettext('Deny unknown clients'),
1025
	$pconfig['denyunknown'],
1026
	array(
1027
		"disabled" => gettext('Allow all clients'),
1028
		"enabled" => gettext('Allow known clients from any interface'),
1029
		"class" => gettext('Allow known clients from only this interface')
1030
	)
1031
))->setHelp(gettext('When set to %3$sAllow all clients%4$s, any DHCP client will get an IP address within this scope/range on this interface. '.
1032
	'If set to %3$sAllow known clients from any interface%4$s, any DHCP client with a MAC address listed in a static mapping on %1$s%3$sany%4$s%2$s scope(s)/interface(s) will get an IP address. ' .
1033
	'If set to %3$sAllow known clients from only this interface%4$s, only MAC addresses listed in static mappings on this interface will get an IP address within this scope/range.'),
1034
	'<i>', '</i>', '<b>', '</b>');
1035

    
1036
$section->addInput(new Form_Checkbox(
1037
	'nonak',
1038
	gettext('Ignore denied clients'),
1039
	gettext('Ignore denied clients rather than reject'),
1040
	$pconfig['nonak']
1041
))->setHelp(gettext('This option is not compatible with failover and cannot be enabled when a Failover Peer IP address is configured.'));
1042

    
1043
$section->addInput(new Form_Checkbox(
1044
	'ignoreclientuids',
1045
	gettext('Ignore client identifiers'),
1046
	gettext('Do not record a unique identifier (UID) in client lease data if present in the client DHCP request'),
1047
	$pconfig['ignoreclientuids']
1048
))->setHelp(gettext('This option may be useful when a client can dual boot using different client identifiers but the same hardware (MAC) address.  Note that the resulting server behavior violates the official DHCP specification.'));
1049

    
1050

    
1051
if (is_numeric($pool) || ($act == "newpool")) {
1052
	$section->addInput(new Form_Input(
1053
		'descr',
1054
		'Pool Description',
1055
		'text',
1056
		$pconfig['descr']
1057
	));
1058
}
1059

    
1060
$section->addInput(new Form_StaticText(
1061
	'Subnet',
1062
	gen_subnet($ifcfgip, $ifcfgsn)
1063
));
1064

    
1065
$section->addInput(new Form_StaticText(
1066
	'Subnet mask',
1067
	gen_subnet_mask($ifcfgsn)
1068
));
1069

    
1070
// Compose a string to display the required address ranges
1071
$rangestr = ip_after($subnet_start) . ' - ' . ip_before($subnet_end);
1072

    
1073
if (is_numeric($pool) || ($act == "newpool")) {
1074
	$rangestr .= '<br />' . gettext('In-use DHCP Pool Ranges:');
1075
	$rangearr = config_get_path("dhcpd/{$if}/range", []);
1076
	if (!empty($rangearr)) {
1077
		$rangestr .= '<br />' . array_get_path($rangearr, 'from') . ' - ' . array_get_path($rangearr, 'to');
1078
	}
1079

    
1080
	foreach ($a_pools as $p) {
1081
		$pa = array_get_path($p, 'range', []);
1082
		if (!empty($pa)) {
1083
			$rangestr .= '<br />' . array_get_path($pa, 'from') . ' - ' . array_get_path($pa, 'to');
1084
		}
1085
	}
1086
}
1087

    
1088
$section->addInput(new Form_StaticText(
1089
	'Available range',
1090
	$rangestr
1091
));
1092

    
1093
$group = new Form_Group('*Range');
1094

    
1095
$group->add(new Form_IpAddress(
1096
	'range_from',
1097
	null,
1098
	$pconfig['range_from'],
1099
	'V4'
1100
))->setHelp('From');
1101

    
1102
$group->add(new Form_IpAddress(
1103
	'range_to',
1104
	null,
1105
	$pconfig['range_to'],
1106
	'V4'
1107
))->setHelp('To');
1108

    
1109
$section->add($group);
1110

    
1111
$form->add($section);
1112

    
1113
if (!is_numeric($pool) && !($act == "newpool")) {
1114
	$section = new Form_Section('Additional Pools');
1115

    
1116
	$btnaddpool = new Form_Button(
1117
		'btnaddpool',
1118
		'Add pool',
1119
		'services_dhcp.php?if=' . htmlspecialchars($if) . '&act=newpool',
1120
		'fa-plus'
1121
	);
1122
	$btnaddpool->addClass('btn-success');
1123

    
1124
	$section->addInput(new Form_StaticText(
1125
		'Add',
1126
		$btnaddpool
1127
	))->setHelp('If additional pools of addresses are needed inside of this subnet outside the above Range, they may be specified here.');
1128

    
1129
	if (is_array($a_pools)) {
1130
		$section->addInput(new Form_StaticText(
1131
			null,
1132
			build_pooltable()
1133
		));
1134
	}
1135

    
1136
	$form->add($section);
1137
}
1138

    
1139
$section = new Form_Section('Servers');
1140

    
1141
$section->addInput(new Form_IpAddress(
1142
	'wins1',
1143
	'WINS servers',
1144
	$pconfig['wins1'],
1145
	'V4'
1146
))->setAttribute('placeholder', 'WINS Server 1');
1147

    
1148
$section->addInput(new Form_IpAddress(
1149
	'wins2',
1150
	null,
1151
	$pconfig['wins2'],
1152
	'V4'
1153
))->setAttribute('placeholder', 'WINS Server 2');
1154

    
1155
for ($idx=1; $idx<=4; $idx++) {
1156
	$section->addInput(new Form_IpAddress(
1157
		'dns' . $idx,
1158
		($idx == 1) ? 'DNS servers':null,
1159
		$pconfig['dns' . $idx],
1160
		'V4'
1161
	))->setAttribute('placeholder', 'DNS Server ' . $idx)->setHelp(($idx == 4) ? 'Leave blank to use the system default DNS servers: The IP address of this firewall interface if DNS Resolver or Forwarder is enabled, otherwise the servers configured in General settings or those obtained dynamically.':'');
1162
}
1163

    
1164
$form->add($section);
1165

    
1166
//OMAPI
1167
$section = new Form_Section('OMAPI');
1168

    
1169
$section->addInput(new Form_Input(
1170
	'omapi_port',
1171
	'OMAPI Port',
1172
	'text',
1173
	$pconfig['omapi_port']
1174
))->setAttribute('placeholder', 'OMAPI Port')
1175
  ->setHelp('Set the port that OMAPI will listen on. The default port is 7911, leave blank to disable.' .
1176
	    'Only the first OMAPI configuration is used.');
1177

    
1178
$group = new Form_Group('OMAPI Key');
1179

    
1180
$group->add(new Form_Input(
1181
	'omapi_key',
1182
	'OMAPI Key',
1183
	'text',
1184
	$pconfig['omapi_key']
1185
))->setAttribute('placeholder', 'OMAPI Key')
1186
  ->setHelp('Enter a key matching the selected algorithm<br />to secure connections to the OMAPI endpoint.');
1187

    
1188
$group->add(new Form_Checkbox(
1189
	'omapi_gen_key',
1190
	'',
1191
	'Generate New Key',
1192
	$pconfig['omapi_gen_key']
1193
))->setHelp('Generate a new key based<br />on the selected algorithm.');
1194

    
1195
$section->add($group);
1196

    
1197
$section->addInput(new Form_Select(
1198
	'omapi_key_algorithm',
1199
	'Key Algorithm',
1200
	empty($pconfig['omapi_key_algorithm']) ? 'hmac-sha256' : $pconfig['omapi_key_algorithm'], // Set the default algorithm if not previous defined
1201
	array(
1202
		'hmac-md5' => 'HMAC-MD5 (legacy default)',
1203
		'hmac-sha1' => 'HMAC-SHA1',
1204
		'hmac-sha224' => 'HMAC-SHA224',
1205
		'hmac-sha256' => 'HMAC-SHA256 (current bind9 default)',
1206
		'hmac-sha384' => 'HMAC-SHA384',
1207
		'hmac-sha512' => 'HMAC-SHA512 (most secure)',
1208
	)
1209
))->setHelp('Set the algorithm that OMAPI key will use.');
1210

    
1211
$form->add($section);
1212

    
1213
$section = new Form_Section('Other Options');
1214

    
1215
$section->addInput(new Form_IpAddress(
1216
	'gateway',
1217
	'Gateway',
1218
	$pconfig['gateway'],
1219
	'V4'
1220
))->setPattern('[.a-zA-Z0-9_]+')
1221
  ->setHelp('The default is to use the IP address of this firewall interface as the gateway. Specify an alternate gateway here if this is not the correct gateway for the network. Enter "none" for no gateway assignment.');
1222

    
1223
$section->addInput(new Form_Input(
1224
	'domain',
1225
	'Domain name',
1226
	'text',
1227
	$pconfig['domain']
1228
))->setHelp('The default is to use the domain name of this firewall as the default domain name provided by DHCP. An alternate domain name may be specified here.');
1229

    
1230
$section->addInput(new Form_Input(
1231
	'domainsearchlist',
1232
	'Domain search list',
1233
	'text',
1234
	$pconfig['domainsearchlist']
1235
))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator.');
1236

    
1237
$section->addInput(new Form_Input(
1238
	'deftime',
1239
	'Default lease time',
1240
	'number',
1241
	$pconfig['deftime']
1242
))->setHelp('This is used for clients that do not ask for a specific expiration time. The default is 7200 seconds.');
1243

    
1244
$section->addInput(new Form_Input(
1245
	'maxtime',
1246
	'Maximum lease time',
1247
	'number',
1248
	$pconfig['maxtime']
1249
))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds.');
1250

    
1251
if (!is_numeric($pool) && !($act == "newpool")) {
1252
	$section->addInput(new Form_IpAddress(
1253
		'failover_peerip',
1254
		'Failover peer IP',
1255
		$pconfig['failover_peerip'],
1256
		'V4'
1257
	))->setHelp('Leave blank to disable. Enter the interface IP address of the other firewall (failover peer) in this subnet. Firewalls must be using CARP. ' .
1258
			'Advertising skew of the CARP VIP on this interface determines whether the DHCP daemon is Primary or Secondary. ' .
1259
			'Ensure the advertising skew for the VIP on one firewall is &lt; 20 and the other is &gt; 20.');
1260

    
1261
	$section->addInput(new Form_Checkbox(
1262
		'staticarp',
1263
		'Static ARP',
1264
		'Enable Static ARP entries',
1265
		$pconfig['staticarp']
1266
	))->setHelp('Restricts communication with the firewall to only hosts listed in static mappings containing both IP addresses and MAC addresses. ' .
1267
			'No other hosts will be able to communicate with the firewall on this interface. ' .
1268
			'This behavior is enforced even when DHCP server is disabled.');
1269

    
1270
	$section->addInput(new Form_Checkbox(
1271
		'dhcpleaseinlocaltime',
1272
		'Time format change',
1273
		'Change DHCP display lease time from UTC to local time',
1274
		$pconfig['dhcpleaseinlocaltime']
1275
	))->setHelp('By default DHCP leases are displayed in UTC time.	By checking this box DHCP lease time will be displayed in local time and set to the time zone selected.' .
1276
				' This will be used for all DHCP interfaces lease time.');
1277

    
1278
	$section->addInput(new Form_Checkbox(
1279
		'statsgraph',
1280
		'Statistics graphs',
1281
		'Enable monitoring graphs for DHCP lease statistics',
1282
		$pconfig['statsgraph']
1283
	))->setHelp('Enable this to add DHCP leases statistics to the Monitoring graphs. Disabled by default.');
1284

    
1285
	$section->addInput(new Form_Checkbox(
1286
		'disablepingcheck',
1287
		'Ping check',
1288
		'Disable ping check',
1289
		$pconfig['disablepingcheck']
1290
	))->setHelp('When enabled dhcpd sends a ping to the address being assigned, and if no response has been heard, it assigns the address. Enabled by default.');
1291
}
1292

    
1293
// DDNS
1294
$btnadv = new Form_Button(
1295
	'btnadvdns',
1296
	'Display Advanced',
1297
	null,
1298
	'fa-cog'
1299
);
1300

    
1301
$btnadv->setAttribute('type','button')->addClass('btn-info btn-sm');
1302

    
1303
$section->addInput(new Form_StaticText(
1304
	'Dynamic DNS',
1305
	$btnadv
1306
));
1307

    
1308
$section->addInput(new Form_Checkbox(
1309
	'ddnsupdate',
1310
	null,
1311
	'Enable registration of DHCP client names in DNS',
1312
	$pconfig['ddnsupdate']
1313
));
1314

    
1315
$section->addInput(new Form_Input(
1316
	'ddnsdomain',
1317
	'DDNS Domain',
1318
	'text',
1319
	$pconfig['ddnsdomain']
1320
))->setHelp('Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
1321

    
1322
$section->addInput(new Form_Checkbox(
1323
	'ddnsforcehostname',
1324
	'DDNS Hostnames',
1325
	'Force dynamic DNS hostname to be the same as configured hostname for Static Mappings',
1326
	$pconfig['ddnsforcehostname']
1327
))->setHelp('Default registers host name option supplied by DHCP client.');
1328

    
1329
$section->addInput(new Form_IpAddress(
1330
	'ddnsdomainprimary',
1331
	'Primary DDNS address',
1332
	$pconfig['ddnsdomainprimary'],
1333
	'BOTH'
1334
))->setHelp('Primary domain name server IP address for the dynamic domain name.');
1335

    
1336
$section->addInput(new Form_IpAddress(
1337
	'ddnsdomainsecondary',
1338
	'Secondary DDNS address',
1339
	$pconfig['ddnsdomainsecondary'],
1340
	'BOTH'
1341
))->setHelp('Secondary domain name server IP address for the dynamic domain name.');
1342

    
1343
$section->addInput(new Form_Input(
1344
	'ddnsdomainkeyname',
1345
	'DNS Domain key',
1346
	'text',
1347
	$pconfig['ddnsdomainkeyname']
1348
))->setHelp('Dynamic DNS domain key name which will be used to register client names in the DNS server.');
1349

    
1350
$section->addInput(new Form_Select(
1351
	'ddnsdomainkeyalgorithm',
1352
	'Key algorithm',
1353
	$pconfig['ddnsdomainkeyalgorithm'],
1354
	$ddnsdomainkeyalgorithms
1355
));
1356

    
1357
$section->addInput(new Form_Input(
1358
	'ddnsdomainkey',
1359
	'DNS Domain key secret',
1360
	'text',
1361
	$pconfig['ddnsdomainkey']
1362
))->setAttribute('placeholder', 'Base64 encoded string')
1363
->setHelp('Dynamic DNS domain key secret which will be used to register client names in the DNS server.');
1364

    
1365
$section->addInput(new Form_Select(
1366
	'ddnsclientupdates',
1367
	'DDNS Client Updates',
1368
	$pconfig['ddnsclientupdates'],
1369
	array(
1370
	    'allow' => gettext('Allow'),
1371
	    'deny' => gettext('Deny'),
1372
	    'ignore' => gettext('Ignore'))
1373
))->setHelp('How Forward entries are handled when client indicates they wish to update DNS.  ' .
1374
	    'Allow prevents DHCP from updating Forward entries, Deny indicates that DHCP will ' .
1375
	    'do the updates and the client should not, Ignore specifies that DHCP will do the ' .
1376
	    'update and the client can also attempt the update usually using a different domain name.');
1377

    
1378
// Advanced MAC
1379
$btnadv = new Form_Button(
1380
	'btnadvmac',
1381
	'Display Advanced',
1382
	null,
1383
	'fa-cog'
1384
);
1385

    
1386
$btnadv->setAttribute('type','button')->addClass('btn-info btn-sm');
1387

    
1388
$section->addInput(new Form_StaticText(
1389
	'MAC address control',
1390
	$btnadv
1391
));
1392

    
1393
$section->addInput(new Form_Input(
1394
	'mac_allow',
1395
	'MAC Allow',
1396
	'text',
1397
	$pconfig['mac_allow']
1398
))->setHelp('List of full or partial MAC addresses to allow in this scope/pool. Implicitly denies any MACs not listed. Does not define known/unknown clients. Enter addresses as comma separated without spaces, e.g.: 00:00:00,01:E5:FF');
1399

    
1400
$section->addInput(new Form_Input(
1401
	'mac_deny',
1402
	'MAC Deny',
1403
	'text',
1404
	$pconfig['mac_deny']
1405
))->setHelp('List of full or partial MAC addresses to deny access in this scope/pool. Implicitly allows any MACs not listed. Does not define known/unknown clients. Enter addresses as comma separated without spaces, e.g.: 00:00:00,01:E5:FF');
1406

    
1407
// Advanced NTP
1408
$btnadv = new Form_Button(
1409
	'btnadvntp',
1410
	'Display Advanced',
1411
	null,
1412
	'fa-cog'
1413
);
1414

    
1415
$btnadv->setAttribute('type','button')->addClass('btn-info btn-sm');
1416

    
1417
$section->addInput(new Form_StaticText(
1418
	'NTP',
1419
	$btnadv
1420
));
1421

    
1422
$section->addInput(new Form_IpAddress(
1423
	'ntp1',
1424
	'NTP Server 1',
1425
	$pconfig['ntp1'],
1426
	'HOSTV4'
1427
));
1428

    
1429
$section->addInput(new Form_IpAddress(
1430
	'ntp2',
1431
	'NTP Server 2',
1432
	$pconfig['ntp2'],
1433
	'HOSTV4'
1434
));
1435

    
1436
$section->addInput(new Form_IpAddress(
1437
	'ntp3',
1438
	'NTP Server 3',
1439
	$pconfig['ntp3'],
1440
	'HOSTV4'
1441
));
1442

    
1443
// Advanced TFTP
1444
$btnadv = new Form_Button(
1445
	'btnadvtftp',
1446
	'Display Advanced',
1447
	null,
1448
	'fa-cog'
1449
);
1450

    
1451
$btnadv->setAttribute('type','button')->addClass('btn-info btn-sm');
1452

    
1453
$section->addInput(new Form_StaticText(
1454
	'TFTP',
1455
	$btnadv
1456
));
1457

    
1458
$section->addInput(new Form_Input(
1459
	'tftp',
1460
	'TFTP Server',
1461
	'text',
1462
	$pconfig['tftp']
1463
))->setHelp('Leave blank to disable. Enter a valid IP address, hostname or URL for the TFTP server.');
1464

    
1465
// Advanced LDAP
1466
$btnadv = new Form_Button(
1467
	'btnadvldap',
1468
	'Display Advanced',
1469
	null,
1470
	'fa-cog'
1471
);
1472

    
1473
$btnadv->setAttribute('type','button')->addClass('btn-info btn-sm');
1474

    
1475
$section->addInput(new Form_StaticText(
1476
	'LDAP',
1477
	$btnadv
1478
));
1479

    
1480
$section->addInput(new Form_Input(
1481
	'ldap',
1482
	'LDAP Server URI',
1483
	'text',
1484
	$pconfig['ldap']
1485
))->setHelp('Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com ');
1486

    
1487
// Advanced Network Booting options
1488
$btnadv = new Form_Button(
1489
	'btnadvnwkboot',
1490
	'Display Advanced',
1491
	null,
1492
	'fa-cog'
1493
);
1494

    
1495
$btnadv->setAttribute('type','button')->addClass('btn-info btn-sm');
1496

    
1497
$section->addInput(new Form_StaticText(
1498
	'Network Booting',
1499
	$btnadv
1500
));
1501

    
1502
$section->addInput(new Form_Checkbox(
1503
	'netboot',
1504
	'Enable',
1505
	'Enable network booting',
1506
	$pconfig['netboot']
1507
));
1508

    
1509
$section->addInput(new Form_IpAddress(
1510
	'nextserver',
1511
	'Next Server',
1512
	$pconfig['nextserver'],
1513
	'V4'
1514
))->setHelp('Enter the IP address of the next server');
1515

    
1516
$section->addInput(new Form_Input(
1517
	'filename',
1518
	'Default BIOS file name',
1519
	'text',
1520
	$pconfig['filename']
1521
));
1522

    
1523
$section->addInput(new Form_Input(
1524
	'filename32',
1525
	'UEFI 32 bit file name',
1526
	'text',
1527
	$pconfig['filename32']
1528
));
1529

    
1530
$section->addInput(new Form_Input(
1531
	'filename64',
1532
	'UEFI 64 bit file name',
1533
	'text',
1534
	$pconfig['filename64']
1535
));
1536

    
1537
$section->addInput(new Form_Input(
1538
	'filename32arm',
1539
	'ARM 32 bit file name',
1540
	'text',
1541
	$pconfig['filename32arm']
1542
));
1543

    
1544
$section->addInput(new Form_Input(
1545
	'filename64arm',
1546
	'ARM 64 bit file name',
1547
	'text',
1548
	$pconfig['filename64arm']
1549
))->setHelp('Both a filename and a boot server must be configured for this to work! ' .
1550
			'All five filenames and a configured boot server are necessary for UEFI & ARM to work! ');
1551

    
1552
$section->addInput(new Form_Input(
1553
	'uefihttpboot',
1554
	'UEFI HTTPBoot URL',
1555
	'text',
1556
	$pconfig['uefihttpboot']
1557
))->setHelp('string-format: http://(servername)/(firmwarepath)');
1558

    
1559
$section->addInput(new Form_Input(
1560
	'rootpath',
1561
	'Root path',
1562
	'text',
1563
	$pconfig['rootpath']
1564
))->setHelp('string-format: iscsi:(servername):(protocol):(port):(LUN):targetname ');
1565

    
1566
// Advanced Additional options
1567
$btnadv = new Form_Button(
1568
	'btnadvopts',
1569
	'Display Advanced',
1570
	null,
1571
	'fa-cog'
1572
);
1573

    
1574
$btnadv->setAttribute('type','button')->addClass('btn-info btn-sm');
1575

    
1576
$section->addInput(new Form_StaticText(
1577
	'Additional BOOTP/DHCP Options',
1578
	$btnadv
1579
));
1580

    
1581
$form->add($section);
1582

    
1583
$section = new Form_Section('Additional BOOTP/DHCP Options');
1584
$section->addClass('adnlopts');
1585

    
1586
$section->addInput(new Form_StaticText(
1587
	null,
1588
	'<div class="alert alert-info"> ' . gettext('Enter the DHCP option number and the value for each item to include in the DHCP lease information.') . ' ' .
1589
	sprintf(gettext('For a list of available options please visit this %1$s URL%2$s.%3$s'), '<a href="https://www.iana.org/assignments/bootp-dhcp-parameters/" target="_blank">', '</a>', '</div>')
1590
));
1591

    
1592
if (!$pconfig['numberoptions']) {
1593
	$pconfig['numberoptions'] = array();
1594
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1595
}
1596

    
1597
$customitemtypes = array(
1598
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1599
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1600
	'signed integer 8' => gettext('Signed 8-bit integer'), 'signed integer 16' => gettext('Signed 16-bit integer'), 'signed integer 32' => gettext('Signed 32-bit integer'), 'ip-address' => gettext('IP address or host')
1601
);
1602

    
1603
$numrows = count($item) -1;
1604
$counter = 0;
1605

    
1606
$numrows = count($pconfig['numberoptions']['item']) -1;
1607

    
1608
foreach ($pconfig['numberoptions']['item'] as $item) {
1609
	$number = $item['number'];
1610
	$itemtype = $item['type'];
1611
	$value = base64_decode($item['value']);
1612

    
1613
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1614
	$group->addClass('repeatable');
1615

    
1616
	$group->add(new Form_Input(
1617
		'number' . $counter,
1618
		null,
1619
		'number',
1620
		$number,
1621
		['min'=>'1', 'max'=>'254']
1622
	))->setHelp($numrows == $counter ? 'Number':null);
1623

    
1624

    
1625
	$group->add(new Form_Select(
1626
		'itemtype' . $counter,
1627
		null,
1628
		$itemtype,
1629
		$customitemtypes
1630
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1631

    
1632
	$group->add(new Form_Input(
1633
		'value' . $counter,
1634
		null,
1635
		'text',
1636
		$value
1637
	))->setHelp($numrows == $counter ? 'Value':null);
1638

    
1639
	$group->add(new Form_Button(
1640
		'deleterow' . $counter,
1641
		'Delete',
1642
		null,
1643
		'fa-trash'
1644
	))->addClass('btn-warning');
1645

    
1646
	$section->add($group);
1647

    
1648
	$counter++;
1649
}
1650

    
1651
$section->addInput(new Form_Button(
1652
	'addrow',
1653
	'Add',
1654
	null,
1655
	'fa-plus'
1656
))->addClass('btn-success');
1657

    
1658
$form->add($section);
1659

    
1660
if ($act == "newpool") {
1661
	$form->addGlobal(new Form_Input(
1662
		'act',
1663
		null,
1664
		'hidden',
1665
		'newpool'
1666
	));
1667
}
1668

    
1669
if (is_numeric($pool)) {
1670
	$form->addGlobal(new Form_Input(
1671
		'pool',
1672
		null,
1673
		'hidden',
1674
		$pool
1675
	));
1676
}
1677

    
1678
$form->addGlobal(new Form_Input(
1679
	'if',
1680
	null,
1681
	'hidden',
1682
	$if
1683
));
1684

    
1685
print($form);
1686

    
1687
// DHCP Static Mappings table
1688

    
1689
if (!is_numeric($pool) && !($act == "newpool")) {
1690

    
1691
	// Decide whether display of the Client Id column is needed.
1692
	$got_cid = false;
1693
	if (is_array($a_maps)) {
1694
		foreach ($a_maps as $map) {
1695
			if (!empty($map['cid'])) {
1696
				$got_cid = true;
1697
				break;
1698
			}
1699
		}
1700
	}
1701
?>
1702

    
1703
<div class="panel panel-default">
1704
<?php
1705
	if (is_array($a_maps) && (count($a_maps) > 0)) {
1706
		$title = sprintf(gettext('DHCP Static Mappings for this Interface (total: %d)'), count($a_maps));
1707
	} else {
1708
		$title = gettext("DHCP Static Mappings for this Interface");
1709
	}
1710
?>
1711
	<div class="panel-heading"><h2 class="panel-title"><?=$title?></h2></div>
1712
	<div class="table-responsive">
1713
			<table class="table table-striped table-hover table-condensed sortable-theme-bootstrap" data-sortable>
1714
				<thead>
1715
					<tr>
1716
						<th><?=gettext("Static ARP")?></th>
1717
						<th><?=gettext("MAC address")?></th>
1718
<?php
1719
	if ($got_cid):
1720
?>
1721
						<th><?=gettext("Client Id")?></th>
1722
<?php
1723
	endif;
1724
?>
1725
						<th><?=gettext("IP address")?></th>
1726
						<th><?=gettext("Hostname")?></th>
1727
						<th><?=gettext("Description")?></th>
1728
						<th></th>
1729
					</tr>
1730
				</thead>
1731
<?php
1732
	if (is_array($a_maps)) {
1733
		$i = 0;
1734
?>
1735
				<tbody>
1736
<?php
1737
		foreach ($a_maps as $mapent) {
1738
?>
1739
					<tr>
1740
						<td class="text-center" ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1741
							<?php if (isset($mapent['arp_table_static_entry'])): ?>
1742
								<i class="fa fa-check"></i>
1743
							<?php endif; ?>
1744
						</td>
1745
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1746
							<?=htmlspecialchars($mapent['mac'])?>
1747
						</td>
1748
<?php
1749
			if ($got_cid):
1750
?>
1751
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1752
							<?=htmlspecialchars($mapent['cid'])?>
1753
						</td>
1754
<?php
1755
			endif;
1756
?>
1757
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1758
							<?=htmlspecialchars($mapent['ipaddr'])?>
1759
						</td>
1760
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1761
							<?=htmlspecialchars($mapent['hostname'])?>
1762
						</td>
1763
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1764
							<?=htmlspecialchars($mapent['descr'])?>
1765
						</td>
1766
						<td>
1767
							<a class="fa fa-pencil"	title="<?=gettext('Edit static mapping')?>"	href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>"></a>
1768
							<a class="fa fa-trash"	title="<?=gettext('Delete static mapping')?>"	href="services_dhcp.php?if=<?=htmlspecialchars($if)?>&amp;act=del&amp;id=<?=$i?>" usepost></a>
1769
						</td>
1770
					</tr>
1771
<?php
1772
		$i++;
1773
		}
1774
?>
1775
				</tbody>
1776
<?php
1777
	}
1778
?>
1779
		</table>
1780
	</div>
1781
</div>
1782

    
1783
<nav class="action-buttons">
1784
	<a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>" class="btn btn-sm btn-success">
1785
		<i class="fa fa-plus icon-embed-btn"></i>
1786
		<?=gettext("Add")?>
1787
	</a>
1788
</nav>
1789
<?php
1790
}
1791
?>
1792

    
1793
<script type="text/javascript">
1794
//<![CDATA[
1795
events.push(function() {
1796

    
1797
	// Show advanced DNS options ======================================================================================
1798
	var showadvdns = false;
1799

    
1800
	function show_advdns(ispageload) {
1801
		var text;
1802
		// On page load decide the initial state based on the data.
1803
		if (ispageload) {
1804
<?php
1805
			if (!$pconfig['ddnsupdate'] &&
1806
				!$pconfig['ddnsforcehostname'] &&
1807
				empty($pconfig['ddnsdomain']) &&
1808
				empty($pconfig['ddnsdomainprimary']) &&
1809
				empty($pconfig['ddnsdomainsecondary']) &&
1810
			    empty($pconfig['ddnsdomainkeyname']) &&
1811
			    (empty($pconfig['ddnsdomainkeyalgorithm']) || ($pconfig['ddnsdomainkeyalgorithm'] == "hmac-md5")) &&
1812
			    (empty($pconfig['ddnsclientupdates']) || ($pconfig['ddnsclientupdates'] == "allow")) &&
1813
			    empty($pconfig['ddnsdomainkey'])) {
1814
				$showadv = false;
1815
			} else {
1816
				$showadv = true;
1817
			}
1818
?>
1819
			showadvdns = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1820
		} else {
1821
			// It was a click, swap the state.
1822
			showadvdns = !showadvdns;
1823
		}
1824

    
1825
		hideCheckbox('ddnsupdate', !showadvdns);
1826
		hideInput('ddnsdomain', !showadvdns);
1827
		hideCheckbox('ddnsforcehostname', !showadvdns);
1828
		hideInput('ddnsdomainprimary', !showadvdns);
1829
		hideInput('ddnsdomainsecondary', !showadvdns);
1830
		hideInput('ddnsdomainkeyname', !showadvdns);
1831
		hideInput('ddnsdomainkeyalgorithm', !showadvdns);
1832
		hideInput('ddnsdomainkey', !showadvdns);
1833
		hideInput('ddnsclientupdates', !showadvdns);
1834

    
1835
		if (showadvdns) {
1836
			text = "<?=gettext('Hide Advanced');?>";
1837
		} else {
1838
			text = "<?=gettext('Display Advanced');?>";
1839
		}
1840
		$('#btnadvdns').html('<i class="fa fa-cog"></i> ' + text);
1841
	}
1842

    
1843
	$('#btnadvdns').click(function(event) {
1844
		show_advdns();
1845
	});
1846

    
1847
	// Show advanced MAC options ======================================================================================
1848
	var showadvmac = false;
1849

    
1850
	function show_advmac(ispageload) {
1851
		var text;
1852
		// On page load decide the initial state based on the data.
1853
		if (ispageload) {
1854
<?php
1855
			if (empty($pconfig['mac_allow']) && empty($pconfig['mac_deny'])) {
1856
				$showadv = false;
1857
			} else {
1858
				$showadv = true;
1859
			}
1860
?>
1861
			showadvmac = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1862
		} else {
1863
			// It was a click, swap the state.
1864
			showadvmac = !showadvmac;
1865
		}
1866

    
1867
		hideInput('mac_allow', !showadvmac);
1868
		hideInput('mac_deny', !showadvmac);
1869

    
1870
		if (showadvmac) {
1871
			text = "<?=gettext('Hide Advanced');?>";
1872
		} else {
1873
			text = "<?=gettext('Display Advanced');?>";
1874
		}
1875
		$('#btnadvmac').html('<i class="fa fa-cog"></i> ' + text);
1876
	}
1877

    
1878
	$('#btnadvmac').click(function(event) {
1879
		show_advmac();
1880
	});
1881

    
1882
	// Show advanced NTP options ======================================================================================
1883
	var showadvntp = false;
1884

    
1885
	function show_advntp(ispageload) {
1886
		var text;
1887
		// On page load decide the initial state based on the data.
1888
		if (ispageload) {
1889
<?php
1890
			if (empty($pconfig['ntp1']) && empty($pconfig['ntp2']) && empty($pconfig['ntp3']) ) {
1891
				$showadv = false;
1892
			} else {
1893
				$showadv = true;
1894
			}
1895
?>
1896
			showadvntp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1897
		} else {
1898
			// It was a click, swap the state.
1899
			showadvntp = !showadvntp;
1900
		}
1901

    
1902
		hideInput('ntp1', !showadvntp);
1903
		hideInput('ntp2', !showadvntp);
1904
		hideInput('ntp3', !showadvntp);
1905

    
1906
		if (showadvntp) {
1907
			text = "<?=gettext('Hide Advanced');?>";
1908
		} else {
1909
			text = "<?=gettext('Display Advanced');?>";
1910
		}
1911
		$('#btnadvntp').html('<i class="fa fa-cog"></i> ' + text);
1912
	}
1913

    
1914
	$('#btnadvntp').click(function(event) {
1915
		show_advntp();
1916
	});
1917

    
1918
	// Show advanced TFTP options ======================================================================================
1919
	var showadvtftp = false;
1920

    
1921
	function show_advtftp(ispageload) {
1922
		var text;
1923
		// On page load decide the initial state based on the data.
1924
		if (ispageload) {
1925
<?php
1926
			if (empty($pconfig['tftp'])) {
1927
				$showadv = false;
1928
			} else {
1929
				$showadv = true;
1930
			}
1931
?>
1932
			showadvtftp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1933
		} else {
1934
			// It was a click, swap the state.
1935
			showadvtftp = !showadvtftp;
1936
		}
1937

    
1938
		hideInput('tftp', !showadvtftp);
1939

    
1940
		if (showadvtftp) {
1941
			text = "<?=gettext('Hide Advanced');?>";
1942
		} else {
1943
			text = "<?=gettext('Display Advanced');?>";
1944
		}
1945
		$('#btnadvtftp').html('<i class="fa fa-cog"></i> ' + text);
1946
	}
1947

    
1948
	$('#btnadvtftp').click(function(event) {
1949
		show_advtftp();
1950
	});
1951

    
1952
	// Show advanced LDAP options ======================================================================================
1953
	var showadvldap = false;
1954

    
1955
	function show_advldap(ispageload) {
1956
		var text;
1957
		// On page load decide the initial state based on the data.
1958
		if (ispageload) {
1959
<?php
1960
			if (empty($pconfig['ldap'])) {
1961
				$showadv = false;
1962
			} else {
1963
				$showadv = true;
1964
			}
1965
?>
1966
			showadvldap = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1967
		} else {
1968
			// It was a click, swap the state.
1969
			showadvldap = !showadvldap;
1970
		}
1971

    
1972
		hideInput('ldap', !showadvldap);
1973

    
1974
		if (showadvldap) {
1975
			text = "<?=gettext('Hide Advanced');?>";
1976
		} else {
1977
			text = "<?=gettext('Display Advanced');?>";
1978
		}
1979
		$('#btnadvldap').html('<i class="fa fa-cog"></i> ' + text);
1980
	}
1981

    
1982
	$('#btnadvldap').click(function(event) {
1983
		show_advldap();
1984
	});
1985

    
1986
	// Show advanced additional opts options ===========================================================================
1987
	var showadvopts = false;
1988

    
1989
	function show_advopts(ispageload) {
1990
		var text;
1991
		// On page load decide the initial state based on the data.
1992
		if (ispageload) {
1993
<?php
1994
			if (empty($pconfig['numberoptions']) ||
1995
			    (empty($pconfig['numberoptions']['item'][0]['number']) && (empty($pconfig['numberoptions']['item'][0]['value'])))) {
1996
				$showadv = false;
1997
			} else {
1998
				$showadv = true;
1999
			}
2000
?>
2001
			showadvopts = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
2002
		} else {
2003
			// It was a click, swap the state.
2004
			showadvopts = !showadvopts;
2005
		}
2006

    
2007
		hideClass('adnlopts', !showadvopts);
2008

    
2009
		if (showadvopts) {
2010
			text = "<?=gettext('Hide Advanced');?>";
2011
		} else {
2012
			text = "<?=gettext('Display Advanced');?>";
2013
		}
2014
		$('#btnadvopts').html('<i class="fa fa-cog"></i> ' + text);
2015
	}
2016

    
2017
	$('#btnadvopts').click(function(event) {
2018
		show_advopts();
2019
	});
2020

    
2021
	// Show advanced Network Booting options ===========================================================================
2022
	var showadvnwkboot = false;
2023

    
2024
	function show_advnwkboot(ispageload) {
2025
		var text;
2026
		// On page load decide the initial state based on the data.
2027
		if (ispageload) {
2028
<?php
2029
			if (empty($pconfig['netboot'])) {
2030
				$showadv = false;
2031
			} else {
2032
				$showadv = true;
2033
			}
2034
?>
2035
			showadvnwkboot = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
2036
		} else {
2037
			// It was a click, swap the state.
2038
			showadvnwkboot = !showadvnwkboot;
2039
		}
2040

    
2041
		hideCheckbox('netboot', !showadvnwkboot);
2042
		hideInput('nextserver', !showadvnwkboot);
2043
		hideInput('filename', !showadvnwkboot);
2044
		hideInput('filename32', !showadvnwkboot);
2045
		hideInput('filename64', !showadvnwkboot);
2046
		hideInput('filename32arm', !showadvnwkboot);
2047
		hideInput('filename64arm', !showadvnwkboot);
2048
		hideInput('uefihttpboot', !showadvnwkboot);
2049
		hideInput('rootpath', !showadvnwkboot);
2050

    
2051
		if (showadvnwkboot) {
2052
			text = "<?=gettext('Hide Advanced');?>";
2053
		} else {
2054
			text = "<?=gettext('Display Advanced');?>";
2055
		}
2056
		$('#btnadvnwkboot').html('<i class="fa fa-cog"></i> ' + text);
2057
	}
2058

    
2059
	$('#btnadvnwkboot').click(function(event) {
2060
		show_advnwkboot();
2061
	});
2062

    
2063
	// ---------- On initial page load ------------------------------------------------------------
2064

    
2065
	show_advdns(true);
2066
	show_advmac(true);
2067
	show_advntp(true);
2068
	show_advtftp(true);
2069
	show_advldap(true);
2070
	show_advopts(true);
2071
	show_advnwkboot(true);
2072

    
2073
	// Suppress "Delete row" button if there are fewer than two rows
2074
	checkLastRow();
2075
});
2076
//]]>
2077
</script>
2078

    
2079
<?php include("foot.inc");
(118-118/228)