Project

General

Profile

Download (65.4 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
if (!g_get('services_dhcp_server_enable')) {
44
	header("Location: /");
45
	exit;
46
}
47

    
48
$if = $_REQUEST['if'];
49
$iflist = get_configured_interface_with_descr();
50

    
51
/* set the starting interface */
52
if (!$if || !isset($iflist[$if])) {
53
	$found_starting_if = false;
54
	// First look for an interface with DHCP already enabled.
55
	foreach (array_keys($iflist) as $ifent) {
56
		if (config_path_enabled("dhcpd/{$ifent}") &&
57
		    is_ipaddrv4(config_get_path("interfaces/{$ifent}/ipaddr")) &&
58
		    ((int) config_get_path("interfaces/{$ifent}/subnet", 0) < 31)) {
59
			$if = $ifent;
60
			$found_starting_if = true;
61
			break;
62
		}
63
	}
64

    
65
	/*
66
	 * If there is no DHCP-enabled interface and LAN is a candidate,
67
	 * then choose LAN.
68
	 */
69
	if (!$found_starting_if &&
70
	    !empty(array_get_path($iflist, 'lan')) &&
71
	    is_ipaddrv4(config_get_path("interfaces/lan/ipaddr")) &&
72
	    ((int) config_get_path("interfaces/lan/subnet", 0) < 31)) {
73
		$if = 'lan';
74
		$found_starting_if = true;
75
	}
76

    
77
	// At the last select whatever can be found.
78
	if (!$found_starting_if) {
79
		foreach (array_keys($iflist) as $ifent) {
80
			/* Not static IPv4 or subnet >= 31 */
81
			if (!is_ipaddrv4(config_get_path("interfaces/{$ifent}/ipaddr")) ||
82
			    empty($oc['subnet']) ||
83
			    ((int) config_get_path("interfaces/{$ifent}/subnet", 0) < 31)) {
84
				continue;
85
			}
86

    
87
			if (!config_path_enabled("dhcpd/{$ifent}")) {
88
				continue;
89
			}
90

    
91
			$if = $ifent;
92
			break;
93
		}
94
	}
95
}
96

    
97
$act = $_REQUEST['act'];
98

    
99
$a_pools = array();
100

    
101
if (!empty(config_get_path("dhcpd/{$if}"))) {
102
	$pool = $_REQUEST['pool'];
103
	if (is_numeric($_POST['pool'])) {
104
		$pool = $_POST['pool'];
105
	}
106

    
107
	// If we have a pool but no interface name, that's not valid. Redirect away.
108
	if (is_numeric($pool) && empty($if)) {
109
		header("Location: services_dhcp.php");
110
		exit;
111
	}
112

    
113
	init_config_arr(array('dhcpd', $if, 'pool'));
114
	$a_pools = &$config['dhcpd'][$if]['pool'];
115

    
116
	if (is_numeric($pool) && $a_pools[$pool]) {
117
		$dhcpdconf = &$a_pools[$pool];
118
	} elseif ($act == "newpool") {
119
		$dhcpdconf = array();
120
	} else {
121
		$dhcpdconf = &$config['dhcpd'][$if];
122
	}
123

    
124
	init_config_arr(array('dhcpd', $if, 'staticmap'));
125
	$a_maps = &$config['dhcpd'][$if]['staticmap'];
126
}
127

    
128
if (is_array($dhcpdconf)) {
129
	// Global Options
130
	if (!is_numeric($pool) && !($act == "newpool")) {
131
		$pconfig['enable'] = isset($dhcpdconf['enable']);
132
		$pconfig['staticarp'] = isset($dhcpdconf['staticarp']);
133
		// No reason to specify this per-pool, per the dhcpd.conf man page it needs to be in every
134
		//	 pool and should be specified in every pool both nodes share, so we'll treat it as global
135
		$pconfig['failover_peerip'] = $dhcpdconf['failover_peerip'];
136

    
137
		// dhcpleaseinlocaltime is global to all interfaces. So if it is selected on any interface,
138
		// then show it true/checked.
139
		foreach (config_get_path('dhcpd', []) as $dhcpdifitem) {
140
			if (empty($dhcpdifitem)) {
141
				continue;
142
			}
143
			$dhcpleaseinlocaltime = $dhcpdifitem['dhcpleaseinlocaltime'];
144
			if ($dhcpleaseinlocaltime) {
145
				break;
146
			}
147
		}
148

    
149
		$pconfig['dhcpleaseinlocaltime'] = $dhcpleaseinlocaltime;
150
	} else {
151
		// Options that exist only in pools
152
		$pconfig['descr'] = $dhcpdconf['descr'];
153
	}
154

    
155
	// Options that can be global or per-pool.
156
	if (is_array($dhcpdconf['range'])) {
157
		$pconfig['range_from'] = $dhcpdconf['range']['from'];
158
		$pconfig['range_to'] = $dhcpdconf['range']['to'];
159
	}
160

    
161
	$pconfig['deftime'] = $dhcpdconf['defaultleasetime'];
162
	$pconfig['maxtime'] = $dhcpdconf['maxleasetime'];
163
	$pconfig['gateway'] = $dhcpdconf['gateway'];
164
	$pconfig['domain'] = $dhcpdconf['domain'];
165
	$pconfig['domainsearchlist'] = $dhcpdconf['domainsearchlist'];
166
	list($pconfig['wins1'], $pconfig['wins2']) = $dhcpdconf['winsserver'];
167
	list($pconfig['dns1'], $pconfig['dns2'], $pconfig['dns3'], $pconfig['dns4']) = $dhcpdconf['dnsserver'];
168
	$pconfig['ignorebootp'] = isset($dhcpdconf['ignorebootp']);
169

    
170
	if (isset($dhcpdconf['denyunknown'])) {
171
		$pconfig['denyunknown'] = empty($dhcpdconf['denyunknown']) ? "enabled" : $dhcpdconf['denyunknown'];
172
	} else {
173
		$pconfig['denyunknown'] = "disabled";
174
	}
175

    
176
	$pconfig['ignoreclientuids'] = isset($dhcpdconf['ignoreclientuids']);
177
	$pconfig['nonak'] = isset($dhcpdconf['nonak']);
178
	$pconfig['ddnsdomain'] = $dhcpdconf['ddnsdomain'];
179
	$pconfig['ddnsdomainprimary'] = $dhcpdconf['ddnsdomainprimary'];
180
	$pconfig['ddnsdomainsecondary'] = $dhcpdconf['ddnsdomainsecondary'];
181
	$pconfig['ddnsdomainkeyname'] = $dhcpdconf['ddnsdomainkeyname'];
182
	$pconfig['ddnsdomainkeyalgorithm'] = $dhcpdconf['ddnsdomainkeyalgorithm'];
183
	$pconfig['ddnsdomainkey'] = $dhcpdconf['ddnsdomainkey'];
184
	$pconfig['ddnsupdate'] = isset($dhcpdconf['ddnsupdate']);
185
	$pconfig['ddnsforcehostname'] = isset($dhcpdconf['ddnsforcehostname']);
186
	$pconfig['mac_allow'] = $dhcpdconf['mac_allow'];
187
	$pconfig['mac_deny'] = $dhcpdconf['mac_deny'];
188
	list($pconfig['ntp1'], $pconfig['ntp2'], $pconfig['ntp3'] ) = $dhcpdconf['ntpserver'];
189
	$pconfig['tftp'] = $dhcpdconf['tftp'];
190
	$pconfig['ldap'] = $dhcpdconf['ldap'];
191
	$pconfig['netboot'] = isset($dhcpdconf['netboot']);
192
	$pconfig['nextserver'] = $dhcpdconf['nextserver'];
193
	$pconfig['filename'] = $dhcpdconf['filename'];
194
	$pconfig['filename32'] = $dhcpdconf['filename32'];
195
	$pconfig['filename64'] = $dhcpdconf['filename64'];
196
	$pconfig['filename32arm'] = $dhcpdconf['filename32arm'];
197
	$pconfig['filename64arm'] = $dhcpdconf['filename64arm'];
198
	$pconfig['uefihttpboot'] = $dhcpdconf['uefihttpboot'];
199
	$pconfig['rootpath'] = $dhcpdconf['rootpath'];
200
	$pconfig['netmask'] = $dhcpdconf['netmask'];
201
	$pconfig['numberoptions'] = $dhcpdconf['numberoptions'];
202
	$pconfig['statsgraph'] = $dhcpdconf['statsgraph'];
203
	$pconfig['disablepingcheck'] = $dhcpdconf['disablepingcheck'];
204
	$pconfig['ddnsclientupdates'] = $dhcpdconf['ddnsclientupdates'];
205

    
206
	// OMAPI Settings
207
	if(isset($dhcpdconf['omapi_port'])) {
208
		$pconfig['omapi_port'] = $dhcpdconf['omapi_port'];
209
		$pconfig['omapi_key'] = $dhcpdconf['omapi_key'];
210
		$pconfig['omapi_key_algorithm'] = $dhcpdconf['omapi_key_algorithm'];
211
	}
212
}
213

    
214
$ifcfgip = config_get_path("interfaces/{$if}/ipaddr");
215
$ifcfgsn = config_get_path("interfaces/{$if}/subnet");
216

    
217
$subnet_start = gen_subnetv4($ifcfgip, $ifcfgsn);
218
$subnet_end = gen_subnetv4_max($ifcfgip, $ifcfgsn);
219

    
220
function validate_partial_mac_list($maclist) {
221
	$macs = explode(',', $maclist);
222

    
223
	// Loop through and look for invalid MACs.
224
	foreach ($macs as $mac) {
225
		if (!is_macaddr($mac, true)) {
226
			return false;
227
		}
228
	}
229

    
230
	return true;
231
}
232

    
233
if (isset($_POST['save'])) {
234

    
235
	unset($input_errors);
236

    
237
	$pconfig = $_POST;
238

    
239
	$numberoptions = array();
240
	for ($x = 0; $x < 99; $x++) {
241
		if (isset($_POST["number{$x}"]) && ctype_digit($_POST["number{$x}"])) {
242
			if ($_POST["number{$x}"] < 1 || $_POST["number{$x}"] > 254) {
243
				$input_errors[] = gettext("The DHCP option must be a number between 1 and 254.");
244
				continue;
245
			}
246
			$numbervalue = array();
247
			$numbervalue['number'] = htmlspecialchars($_POST["number{$x}"]);
248
			$numbervalue['type'] = htmlspecialchars($_POST["itemtype{$x}"]);
249
			$numbervalue['value'] = base64_encode($_POST["value{$x}"]);
250
			$numberoptions['item'][] = $numbervalue;
251
		}
252
	}
253

    
254
	// Reload the new pconfig variable that the form uses.
255
	$pconfig['numberoptions'] = $numberoptions;
256

    
257
	/* input validation */
258

    
259
	/*
260
	 * Check the OMAPI settings
261
	 * - Make sure that if the port is defined, that it is valid and isn't in use
262
	 * - Make sure the key is defined and the length is appropriate for the selected algorithm
263
	 * - Generate a new key if selected
264
	 */
265
	if (!empty($_POST['omapi_port'])) {
266
		// Check the port entry
267
		switch(true){
268
			case !is_port($_POST['omapi_port']) || $_POST['omapi_port'] <= 1024:
269
				$input_errors[] = gettext("The specified OMAPI port number is invalid. Port number must be between 1024 and 65635.");
270
				break;
271
			case is_port_in_use($_POST['omapi_port']) && $_POST['omapi_port'] != $dhcpdconf['omapi_port']:
272
				$input_errors[] = gettext("Specified port number for OMAPI is in use. Please choose another port or consider using the default.");
273
				break;
274
		}
275

    
276
		// Define the minimum base64 character length for each algorithm
277
		$key_char_len_by_alg = array(
278
			'hmac-md5' => 24,
279
			'hmac-sha1' => 28,
280
			'hmac-sha224' => 40,
281
			'hmac-sha256' => 44,
282
			'hmac-sha384' => 64,
283
			'hmac-sha512' => 88
284
		);
285

    
286
		// Generate a key if checked
287
		if ($_POST['omapi_gen_key'] == "yes") {
288
			// Figure out the key bits from the selected algorithm
289
			switch ($_POST['omapi_key_algorithm']) {
290
				case "hmac-md5":
291
					$key_bit_len = 128;
292
					break;
293
				case "hmac-sha1":
294
					$key_bit_len = 160;
295
					break;
296
				default:
297
					$key_bit_len = str_replace("hmac-sha","",$_POST['omapi_key_algorithm']);
298
					break;
299
			}
300

    
301
			// Convert the bits to bytes
302
			$key_bytes_len = $key_bit_len / 8; // 8 bits = 1 Byte
303

    
304
			// Generate random bytes based on key length
305
			$ran_bytes = openssl_random_pseudo_bytes($key_bytes_len);
306

    
307
			// Encode the bytes to get the key string
308
			$key_str = base64_encode($ran_bytes);
309

    
310
			// Set the key
311
			$_POST['omapi_key'] = $key_str;
312
			$pconfig['omapi_key'] = $key_str;
313

    
314
			// Uncheck the generate box
315
			unset($_POST['omapi_gen_key']);
316
			unset($pconfig['omapi_gen_key']);
317
		} elseif (!empty($_POST['omapi_key'])) { // Check the key if it's not being generated
318
			if (strlen($_POST['omapi_key']) < $key_char_len_by_alg[$_POST['omapi_key_algorithm']]) {
319
				$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']}.");
320
			}
321
		} else {
322
			$input_errors[] = gettext("A key is required when OMAPI is enabled (port specified).");
323
		}
324
	}
325

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

    
331
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
332
	}
333

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

    
338
	if ($_POST['range_from'] && !is_ipaddrv4($_POST['range_from'])) {
339
		$input_errors[] = gettext("A valid IPv4 address must be specified for range from.");
340
	}
341
	if ($_POST['range_to'] && !is_ipaddrv4($_POST['range_to'])) {
342
		$input_errors[] = gettext("A valid IPv4 address must be specified for range to.");
343
	}
344
	if (($_POST['range_from'] && !$_POST['range_to']) || ($_POST['range_to'] && !$_POST['range_from'])) {
345
		$input_errors[] = gettext("Range From and Range To must both be entered.");
346
	}
347
	if (($_POST['gateway'] && $_POST['gateway'] != "none" && !is_ipaddrv4($_POST['gateway']))) {
348
		$input_errors[] = gettext("A valid IP address must be specified for the gateway.");
349
	}
350
	if (($_POST['wins1'] && !is_ipaddrv4($_POST['wins1'])) || ($_POST['wins2'] && !is_ipaddrv4($_POST['wins2']))) {
351
		$input_errors[] = gettext("A valid IP address must be specified for the primary/secondary WINS servers.");
352
	}
353
	$parent_ip = get_interface_ip($_POST['if']);
354
	if (is_ipaddrv4($parent_ip) && $_POST['gateway'] && $_POST['gateway'] != "none") {
355
		$parent_sn = get_interface_subnet($_POST['if']);
356
		if (!ip_in_subnet($_POST['gateway'], gen_subnet($parent_ip, $parent_sn) . "/" . $parent_sn) && !ip_in_interface_alias_subnet($_POST['if'], $_POST['gateway'])) {
357
			$input_errors[] = sprintf(gettext("The gateway address %s does not lie within the chosen interface's subnet."), $_POST['gateway']);
358
		}
359
	}
360

    
361
	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']))) {
362
		$input_errors[] = gettext("A valid IP address must be specified for each of the DNS servers.");
363
	}
364

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

    
369
	// Default value if it's empty
370
	$deftime = (is_numeric($_POST['deftime'])) ? $_POST['deftime'] : 7200;
371
	foreach (config_get_path('captiveportal', []) as $cpZone => $cpdata) {
372
		if (!isset($cpdata['enable'])) {
373
			continue;
374
		}
375
		if (!isset($cpdata['timeout']) || !is_numeric($cpdata['timeout'])) {
376
			continue;
377
		}
378
		$cp_ifs = explode(',', $cpdata['interface']);
379
		if (!in_array($if, $cp_ifs)) {
380
			continue;
381
		}
382
		if ($cpdata['timeout'] > $deftime) {
383
			$input_errors[] = sprintf(gettext(
384
				'The Captive Portal zone (%1$s) has Hard Timeout parameter set to a value bigger than Default lease time (%2$s).'), $cpZone, $deftime);
385
		}
386
	}
387

    
388
	if ($_POST['maxtime'] && (!is_numeric($_POST['maxtime']) || ($_POST['maxtime'] < 60) || ($_POST['maxtime'] < $_POST['deftime']))) {
389
		$input_errors[] = gettext("The maximum lease time must be at least 60 seconds, and the same value or greater than the default lease time.");
390
	}
391
	if ($_POST['ddnsupdate']) {
392
		if (!is_domain($_POST['ddnsdomain'])) {
393
			$input_errors[] = gettext("A valid domain name must be specified for the dynamic DNS registration.");
394
		}
395
		if (!is_ipaddr($_POST['ddnsdomainprimary'])) {
396
			$input_errors[] = gettext("A valid primary domain name server IP address must be specified for the dynamic domain name.");
397
		}
398
		if (!empty($_POST['ddnsdomainsecondary']) && !is_ipaddr($_POST['ddnsdomainsecondary'])) {
399
			$input_errors[] = gettext("A valid secondary domain name server IP address must be specified for the dynamic domain name.");
400
		}
401
		if (!$_POST['ddnsdomainkeyname'] || !$_POST['ddnsdomainkeyalgorithm'] || !$_POST['ddnsdomainkey']) {
402
			$input_errors[] = gettext("A valid domain key name, algorithm and secret must be specified.");
403
		}
404
		if (preg_match('/[^A-Za-z0-9\.\-\_]/', $_POST['ddnsdomainkeyname'])) {
405
			$input_errors[] = gettext("The domain key name may only contain the characters a-z, A-Z, 0-9, '-', '_' and '.'");
406
		}
407
		if ($_POST['ddnsdomainkey'] && !base64_decode($_POST['ddnsdomainkey'], true)) {
408
			$input_errors[] = gettext("The domain key secret must be a Base64 encoded value.");
409
		}
410
	}
411
	if ($_POST['domainsearchlist']) {
412
		$domain_array = preg_split("/[ ;]+/", $_POST['domainsearchlist']);
413
		foreach ($domain_array as $curdomain) {
414
			if (!is_domain($curdomain)) {
415
				$input_errors[] = gettext("A valid domain search list must be specified.");
416
				break;
417
			}
418
		}
419
	}
420

    
421
	// Validate MACs
422
	if (!empty($_POST['mac_allow']) && !validate_partial_mac_list($_POST['mac_allow'])) {
423
		$input_errors[] = gettext("If a mac allow list is specified, it must contain only valid partial MAC addresses.");
424
	}
425
	if (!empty($_POST['mac_deny']) && !validate_partial_mac_list($_POST['mac_deny'])) {
426
		$input_errors[] = gettext("If a mac deny list is specified, it must contain only valid partial MAC addresses.");
427
	}
428

    
429
	if (($_POST['ntp1'] && (!is_ipaddrv4($_POST['ntp1']) && !is_hostname($_POST['ntp1']))) ||
430
	    ($_POST['ntp2'] && (!is_ipaddrv4($_POST['ntp2']) && !is_hostname($_POST['ntp2']))) ||
431
	    ($_POST['ntp3'] && (!is_ipaddrv4($_POST['ntp3']) && !is_hostname($_POST['ntp3'])))) {
432
		$input_errors[] = gettext("A valid IP address or hostname must be specified for the primary/secondary NTP servers.");
433
	}
434
	if ($_POST['domain'] && (!is_domain($_POST['domain'], false, false))) {
435
		$input_errors[] = gettext("A valid domain name must be specified for the DNS domain.");
436
	}
437
	if ($_POST['tftp'] && !is_ipaddrv4($_POST['tftp']) && !is_domain($_POST['tftp']) && !filter_var($_POST['tftp'], FILTER_VALIDATE_URL)) {
438
		$input_errors[] = gettext("A valid IP address, hostname or URL must be specified for the TFTP server.");
439
	}
440
	if (($_POST['nextserver'] && !is_ipaddrv4($_POST['nextserver']))) {
441
		$input_errors[] = gettext("A valid IP address must be specified for the network boot server.");
442
	}
443

    
444
	if (gen_subnet($ifcfgip, $ifcfgsn) == $_POST['range_from']) {
445
		$input_errors[] = gettext("The network address cannot be used in the starting subnet range.");
446
	}
447
	if (gen_subnet_max($ifcfgip, $ifcfgsn) == $_POST['range_to']) {
448
		$input_errors[] = gettext("The broadcast address cannot be used in the ending subnet range.");
449
	}
450

    
451
	// Disallow a range that includes the virtualip
452
	foreach (config_get_path('virtualip/vip', []) as $vip) {
453
		if ($vip['interface'] == $if) {
454
			if ($vip['subnet'] && is_inrange_v4($vip['subnet'], $_POST['range_from'], $_POST['range_to'])) {
455
				$input_errors[] = sprintf(gettext("The subnet range cannot overlap with virtual IP address %s."), $vip['subnet']);
456
			}
457
		}
458
	}
459

    
460
	$noip = false;
461
	if (is_array($a_maps)) {
462
		foreach ($a_maps as $map) {
463
			if (empty($map['ipaddr'])) {
464
				$noip = true;
465
			}
466
		}
467
	}
468

    
469
	if ($_POST['staticarp'] && $noip) {
470
		$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.");
471
	}
472

    
473
	if (is_array($pconfig['numberoptions']['item'])) {
474
		foreach ($pconfig['numberoptions']['item'] as $numberoption) {
475
			$numberoption_value = base64_decode($numberoption['value']);
476
			if ($numberoption['type'] == 'text' && strstr($numberoption_value, '"')) {
477
				$input_errors[] = gettext("Text type cannot include quotation marks.");
478
			} else if ($numberoption['type'] == 'string' && !preg_match('/^"[^"]*"$/', $numberoption_value) && !preg_match('/^[0-9a-f]{2}(?:\:[0-9a-f]{2})*$/i', $numberoption_value)) {
479
				$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");
480
			} else if ($numberoption['type'] == 'boolean' && $numberoption_value != 'true' && $numberoption_value != 'false' && $numberoption_value != 'on' && $numberoption_value != 'off') {
481
				$input_errors[] = gettext("Boolean type must be true, false, on, or off.");
482
			} else if ($numberoption['type'] == 'unsigned integer 8' && (!is_numeric($numberoption_value) || $numberoption_value < 0 || $numberoption_value > 255)) {
483
				$input_errors[] = gettext("Unsigned 8-bit integer type must be a number in the range 0 to 255.");
484
			} else if ($numberoption['type'] == 'unsigned integer 16' && (!is_numeric($numberoption_value) || $numberoption_value < 0 || $numberoption_value > 65535)) {
485
				$input_errors[] = gettext("Unsigned 16-bit integer type must be a number in the range 0 to 65535.");
486
			} else if ($numberoption['type'] == 'unsigned integer 32' && (!is_numeric($numberoption_value) || $numberoption_value < 0 || $numberoption_value > 4294967295)) {
487
				$input_errors[] = gettext("Unsigned 32-bit integer type must be a number in the range 0 to 4294967295.");
488
			} else if ($numberoption['type'] == 'signed integer 8' && (!is_numeric($numberoption_value) || $numberoption_value < -128 || $numberoption_value > 127)) {
489
				$input_errors[] = gettext("Signed 8-bit integer type must be a number in the range -128 to 127.");
490
			} else if ($numberoption['type'] == 'signed integer 16' && (!is_numeric($numberoption_value) || $numberoption_value < -32768 || $numberoption_value > 32767)) {
491
				$input_errors[] = gettext("Signed 16-bit integer type must be a number in the range -32768 to 32767.");
492
			} else if ($numberoption['type'] == 'signed integer 32' && (!is_numeric($numberoption_value) || $numberoption_value < -2147483648 || $numberoption_value > 2147483647)) {
493
				$input_errors[] = gettext("Signed 32-bit integer type must be a number in the range -2147483648 to 2147483647.");
494
			} else if ($numberoption['type'] == 'ip-address' && !is_ipaddrv4($numberoption_value) && !is_hostname($numberoption_value)) {
495
				$input_errors[] = gettext("IP address or host type must be an IP address or host name.");
496
			}
497
		}
498
	}
499

    
500
	if ((!isset($pool) || !is_numeric($pool)) && $act != "newpool") {
501
		/* If enabling DHCP Server, make sure that the DHCP Relay isn't enabled on this interface */
502
		if ($_POST['enable'] && config_path_enabled('dhcrelay') &&
503
		    (stristr(config_get_path('dhcrelay/interface', ''), $if) !== false)) {
504
			$input_errors[] = sprintf(gettext(
505
			    "The DHCP relay on the %s interface must be disabled before enabling the DHCP server."),
506
			    $iflist[$if]);
507
		}
508

    
509
		/* If disabling DHCP Server, make sure that DHCP registration isn't enabled for DNS forwarder/resolver */
510
		if (!$_POST['enable']) {
511
			/* Find out how many other interfaces have DHCP enabled. */
512
			$dhcp_enabled_count = 0;
513
			foreach (config_get_path('dhcpd', []) as $dhif => $dhcps) {
514
				if ($dhif == $if) {
515
					/* Skip this interface, we only want to know how many others are enabled. */
516
					continue;
517
				}
518
				if (config_path_enabled("dhcpd/{$dhif}")) {
519
					$dhcp_enabled_count++;
520
				}
521
			}
522

    
523
			if (config_path_enabled('dnsmasq') &&
524
			    ($dhcp_enabled_count == 0) &&
525
			    (config_path_enabled('dnsmasq', 'regdhcp') ||
526
			    config_path_enabled('dnsmasq', 'regdhcpstatic') ||
527
			    config_path_enabled('dnsmasq', 'dhcpfirst'))) {
528
				$input_errors[] = gettext(
529
				    "DHCP Registration features in the DNS Forwarder are active and require at least one enabled DHCP Server.");
530
			}
531
			if (config_path_enabled('unbound') &&
532
			    ($dhcp_enabled_count == 0) &&
533
			    (config_path_enabled('unbound', 'regdhcp') ||
534
			    config_path_enabled('unbound', 'regdhcpstatic'))) {
535
				$input_errors[] = gettext(
536
				    "DHCP Registration features in the DNS Resolver are active and require at least one enabled DHCP Server.");
537
			}
538
		}
539
	}
540

    
541
	// 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.
542
	if (!$input_errors && $_POST['range_from'] && $_POST['range_to']) {
543
		/* make sure the range lies within the current subnet */
544
		if (ip_greater_than($_POST['range_from'], $_POST['range_to'])) {
545
			$input_errors[] = gettext("The range is invalid (first element higher than second element).");
546
		}
547

    
548
		if (!is_inrange_v4($_POST['range_from'], $subnet_start, $subnet_end) ||
549
			!is_inrange_v4($_POST['range_to'], $subnet_start, $subnet_end)) {
550
			$input_errors[] = gettext("The specified range lies outside of the current subnet.");
551
		}
552

    
553
		if (is_numeric($pool) || ($act == "newpool")) {
554
			if (is_inrange_v4($_POST['range_from'],
555
				config_get_path("dhcpd/{$if}/range/from"),
556
				config_get_path("dhcpd/{$if}/range/to") ||
557
				is_inrange_v4($_POST['range_to'],
558
				config_get_path("dhcpd/{$if}/range/from"),
559
				config_get_path("dhcpd/{$if}/range/to")))) {
560
				$input_errors[] = gettext("The specified range must not be within the DHCP range for this interface.");
561
			}
562
		}
563

    
564
		foreach ($a_pools as $id => $p) {
565
			if (is_numeric($pool) && ($id == $pool)) {
566
				continue;
567
			}
568

    
569
			if (is_inrange_v4($_POST['range_from'],
570
				$p['range']['from'], $p['range']['to']) ||
571
				is_inrange_v4($_POST['range_to'],
572
				$p['range']['from'], $p['range']['to'])) {
573
				$input_errors[] = gettext("The specified range must not be within the range configured on a DHCP pool for this interface.");
574
				break;
575
			}
576
		}
577

    
578
		if (is_array($a_maps)) {
579
			foreach ($a_maps as $map) {
580
				if (empty($map['ipaddr'])) {
581
					continue;
582
				}
583
				if (is_inrange_v4($map['ipaddr'], $_POST['range_from'], $_POST['range_to'])) {
584
					$input_errors[] = sprintf(gettext("The DHCP range cannot overlap any static DHCP mappings."));
585
					break;
586
				}
587
			}
588
		}
589
	}
590

    
591
	if (!$input_errors) {
592
		if (!is_numeric($pool)) {
593
			if ($act == "newpool") {
594
				$dhcpdconf = array();
595
			} else {
596
				config_init_path("dhcpd/{$if}");
597
				$dhcpdconf = config_get_path("dhcpd/{$if}");
598
			}
599
		} else {
600
			if (is_array($a_pools[$pool])) {
601
				$dhcpdconf = $a_pools[$pool];
602
			} else {
603
				// Someone specified a pool but it doesn't exist. Punt.
604
				header("Location: services_dhcp.php");
605
				exit;
606
			}
607
		}
608
		if (!is_array($dhcpdconf)) {
609
			$dhcpdconf = array();
610
		}
611
		if (!is_array($dhcpdconf['range'])) {
612
			$dhcpdconf['range'] = array();
613
		}
614

    
615
		$dhcpd_enable_changed = false;
616

    
617
		// Global Options
618
		if (!is_numeric($pool) && !($act == "newpool")) {
619
			$old_dhcpd_enable = isset($dhcpdconf['enable']);
620
			$new_dhcpd_enable = ($_POST['enable']) ? true : false;
621
			if ($old_dhcpd_enable != $new_dhcpd_enable) {
622
				/* DHCP has been enabled or disabled. The pf ruleset will need to be rebuilt to allow or disallow DHCP. */
623
				$dhcpd_enable_changed = true;
624
			}
625

    
626
			$dhcpdconf['enable'] = $new_dhcpd_enable;
627
			$dhcpdconf['staticarp'] = ($_POST['staticarp']) ? true : false;
628
			$previous = $dhcpdconf['failover_peerip'];
629
			if ($previous != $_POST['failover_peerip']) {
630
				mwexec("/bin/rm -rf /var/dhcpd/var/db/*");
631
			}
632

    
633
			$dhcpdconf['failover_peerip'] = $_POST['failover_peerip'];
634
			// dhcpleaseinlocaltime is global to all interfaces. So update the setting on all interfaces.
635
			foreach (config_get_path('dhcpd', []) as $dhcpdifkey => $keyvalue) {
636
				if (empty($keyvalue)) {
637
					continue;
638
				}
639
				if (isset($_POST['dhcpleaseinlocaltime'])) {
640
					config_set_path("dhcpd/{$dhcpdifkey}/dhcpleaseinlocaltime", $_POST['dhcpleaseinlocaltime']);
641
				} else {
642
					config_del_path("dhcpd/{$dhcpdifkey}/dhcpleaseinlocaltime");
643
				}
644
			}
645
		} else {
646
			// Options that exist only in pools
647
			$dhcpdconf['descr'] = $_POST['descr'];
648
		}
649

    
650
		// Options that can be global or per-pool.
651
		$dhcpdconf['range']['from'] = $_POST['range_from'];
652
		$dhcpdconf['range']['to'] = $_POST['range_to'];
653
		$dhcpdconf['defaultleasetime'] = $_POST['deftime'];
654
		$dhcpdconf['maxleasetime'] = $_POST['maxtime'];
655
		$dhcpdconf['netmask'] = $_POST['netmask'];
656

    
657
		unset($dhcpdconf['winsserver']);
658
		if ($_POST['wins1']) {
659
			$dhcpdconf['winsserver'][] = $_POST['wins1'];
660
		}
661
		if ($_POST['wins2']) {
662
			$dhcpdconf['winsserver'][] = $_POST['wins2'];
663
		}
664

    
665
		unset($dhcpdconf['dnsserver']);
666
		if ($_POST['dns1']) {
667
			$dhcpdconf['dnsserver'][] = $_POST['dns1'];
668
		}
669
		if ($_POST['dns2']) {
670
			$dhcpdconf['dnsserver'][] = $_POST['dns2'];
671
		}
672
		if ($_POST['dns3']) {
673
			$dhcpdconf['dnsserver'][] = $_POST['dns3'];
674
		}
675
		if ($_POST['dns4']) {
676
			$dhcpdconf['dnsserver'][] = $_POST['dns4'];
677
		}
678

    
679
		$dhcpdconf['gateway'] = $_POST['gateway'];
680
		$dhcpdconf['domain'] = $_POST['domain'];
681
		$dhcpdconf['domainsearchlist'] = $_POST['domainsearchlist'];
682
		$dhcpdconf['ignorebootp'] = ($_POST['ignorebootp']) ? true : false;
683

    
684
		if (in_array($_POST['denyunknown'], array("enabled", "class"))) {
685
			$dhcpdconf['denyunknown'] = $_POST['denyunknown'];
686
		} else {
687
			unset($dhcpdconf['denyunknown']);
688
		}
689

    
690
		$dhcpdconf['ignoreclientuids'] = ($_POST['ignoreclientuids']) ? true : false;
691
		$dhcpdconf['nonak'] = ($_POST['nonak']) ? true : false;
692
		$dhcpdconf['ddnsdomain'] = $_POST['ddnsdomain'];
693
		$dhcpdconf['ddnsdomainprimary'] = $_POST['ddnsdomainprimary'];
694
		$dhcpdconf['ddnsdomainsecondary'] = (!empty($_POST['ddnsdomainsecondary'])) ? $_POST['ddnsdomainsecondary'] : '';
695
		$dhcpdconf['ddnsdomainkeyname'] = $_POST['ddnsdomainkeyname'];
696
		$dhcpdconf['ddnsdomainkeyalgorithm'] = $_POST['ddnsdomainkeyalgorithm'];
697
		$dhcpdconf['ddnsdomainkey'] = $_POST['ddnsdomainkey'];
698
		$dhcpdconf['ddnsupdate'] = ($_POST['ddnsupdate']) ? true : false;
699
		$dhcpdconf['ddnsforcehostname'] = ($_POST['ddnsforcehostname']) ? true : false;
700
		$dhcpdconf['mac_allow'] = $_POST['mac_allow'];
701
		$dhcpdconf['mac_deny'] = $_POST['mac_deny'];
702
		if ($_POST['disablepingcheck']) {
703
			$dhcpdconf['disablepingcheck'] = $_POST['disablepingcheck'];
704
		} else {
705
			unset($dhcpdconf['disablepingcheck']);
706
		}
707
		$dhcpdconf['ddnsclientupdates'] = $_POST['ddnsclientupdates'];
708

    
709
		unset($dhcpdconf['ntpserver']);
710
		if ($_POST['ntp1']) {
711
			$dhcpdconf['ntpserver'][] = $_POST['ntp1'];
712
		}
713
		if ($_POST['ntp2']) {
714
			$dhcpdconf['ntpserver'][] = $_POST['ntp2'];
715
		}
716
		if ($_POST['ntp3']) {
717
			$dhcpdconf['ntpserver'][] = $_POST['ntp3'];
718
		}
719

    
720
		$dhcpdconf['tftp'] = $_POST['tftp'];
721
		$dhcpdconf['ldap'] = $_POST['ldap'];
722
		$dhcpdconf['netboot'] = ($_POST['netboot']) ? true : false;
723
		$dhcpdconf['nextserver'] = $_POST['nextserver'];
724
		$dhcpdconf['filename'] = $_POST['filename'];
725
		$dhcpdconf['filename32'] = $_POST['filename32'];
726
		$dhcpdconf['filename64'] = $_POST['filename64'];
727
		$dhcpdconf['filename32arm'] = $_POST['filename32arm'];
728
		$dhcpdconf['filename64arm'] = $_POST['filename64arm'];
729
		$dhcpdconf['uefihttpboot'] = $_POST['uefihttpboot'];
730
		$dhcpdconf['rootpath'] = $_POST['rootpath'];
731

    
732
		if (empty($_POST['statsgraph']) == isset($dhcpdconf['statsgraph'])) {
733
			$enable_rrd_graphing = true;
734
		}
735
		if (!empty($_POST['statsgraph'])) {
736
			$dhcpdconf['statsgraph'] = $_POST['statsgraph'];
737
		} elseif (isset($dhcpdconf['statsgraph'])) {
738
			unset($dhcpdconf['statsgraph']);
739
		}
740
		if ($enable_rrd_graphing) {
741
			enable_rrd_graphing();
742
		}
743

    
744
		// Handle the custom options rowhelper
745
		if (isset($dhcpdconf['numberoptions']['item'])) {
746
			unset($dhcpdconf['numberoptions']['item']);
747
		}
748

    
749
		$dhcpdconf['numberoptions'] = $numberoptions;
750

    
751
		if (is_numeric($pool) && is_array($a_pools[$pool])) {
752
			$a_pools[$pool] = $dhcpdconf;
753
		} elseif ($act == "newpool") {
754
			$a_pools[] = $dhcpdconf;
755
		} else {
756
			config_set_path("dhcpd/{$if}", $dhcpdconf);
757
		}
758

    
759
		// OMAPI Settings
760
		if ($_POST['omapi_port'] == ""){
761
			unset($dhcpdconf['omapi_port']);
762
			unset($dhcpdconf['omapi_key']);
763
			unset($dhcpdconf['omapi_key_algorithm']);
764

    
765
			unset($pconfig['omapi_port']);
766
			unset($pconfig['omapi_key']);
767
			unset($pconfig['omapi_key_algorithm']);
768
		} else {
769
			$dhcpdconf['omapi_port'] = $_POST['omapi_port'];
770
			$dhcpdconf['omapi_key'] = $_POST['omapi_key'];
771
			$dhcpdconf['omapi_key_algorithm'] = $_POST['omapi_key_algorithm'];
772
		}
773

    
774
		write_config(gettext("DHCP Server - Settings changed for interface " . strtoupper($if)));
775
	}
776
}
777

    
778
if ((isset($_POST['save']) || isset($_POST['apply'])) && (!$input_errors)) {
779
	$changes_applied = true;
780
	$retval = 0;
781
	$retvaldhcp = 0;
782
	$retvaldns = 0;
783
	/* dnsmasq_configure calls dhcpd_configure */
784
	/* no need to restart dhcpd twice */
785
	if (config_path_enabled('dnsmasq') &&
786
	    config_path_enabled('dnsmasq', 'regdhcpstatic')) {
787
		$retvaldns |= services_dnsmasq_configure();
788
		if ($retvaldns == 0) {
789
			clear_subsystem_dirty('hosts');
790
			clear_subsystem_dirty('staticmaps');
791
		}
792
	} elseif (config_path_enabled('unbound') &&
793
		  config_path_enabled('unbound', 'regdhcpstatic')) {
794
		$retvaldns |= services_unbound_configure();
795
		if ($retvaldns == 0) {
796
			clear_subsystem_dirty('unbound');
797
			clear_subsystem_dirty('hosts');
798
			clear_subsystem_dirty('staticmaps');
799
		}
800
	} else {
801
		$retvaldhcp |= services_dhcpd_configure();
802
		if ($retvaldhcp == 0) {
803
			clear_subsystem_dirty('staticmaps');
804
		}
805
	}
806
	/* BIND package - Bug #3710 */
807
	if (!function_exists('is_package_installed')) {
808
		require_once('pkg-utils.inc');
809
	}
810
	if (is_package_installed('pfSense-pkg-bind') &&
811
	    config_path_enabled('installedpackages/bind/config/0', 'enable_bind')) {
812
		$reloadbind = false;
813
		$bindzone = config_get_path('installedpackages/bindzone/config', []);
814

    
815
		for ($x = 0; $x < sizeof($bindzone); $x++) {
816
			$zone = $bindzone[$x];
817
			if ($zone['regdhcpstatic'] == 'on') {
818
				$reloadbind = true;
819
				break;
820
			}
821
		}
822
		if ($reloadbind === true) {
823
			if (file_exists("/usr/local/pkg/bind.inc")) {
824
				require_once("/usr/local/pkg/bind.inc");
825
				bind_sync();
826
			}
827
		}
828
	}
829
	if ($dhcpd_enable_changed) {
830
		$retvalfc |= filter_configure();
831
	}
832

    
833
	if ($retvaldhcp == 1 || $retvaldns == 1 || $retvalfc == 1) {
834
		$retval = 1;
835
	}
836
}
837

    
838
if ($act == "delpool") {
839
	if ($a_pools[$_POST['id']]) {
840
		unset($a_pools[$_POST['id']]);
841
		write_config("DHCP Server pool deleted");
842
		header("Location: services_dhcp.php?if={$if}");
843
		exit;
844
	}
845
}
846

    
847
if ($act == "del") {
848
	if (isset($a_maps[$_POST['id']])) {
849
		/* Remove static ARP entry, if necessary */
850
		if (isset($a_maps[$_POST['id']]['arp_table_static_entry'])) {
851
			mwexec("/usr/sbin/arp -d " . escapeshellarg($a_maps[$_POST['id']]['ipaddr']));
852
		}
853
		unset($a_maps[$_POST['id']]);
854
		write_config("DHCP Server static map deleted");
855
		if (config_path_enabled("dhcpd/{$if}")) {
856
			mark_subsystem_dirty('staticmaps');
857
			if (config_path_enabled('dnsmasq') && config_get_path('dnsmasq/regdhcpstatic', false)) {
858
				mark_subsystem_dirty('hosts');
859
			}
860
		}
861

    
862
		header("Location: services_dhcp.php?if={$if}");
863
		exit;
864
	}
865
}
866

    
867
// Build an HTML table that can be inserted into a Form_StaticText element
868
function build_pooltable() {
869
	global $a_pools, $if;
870

    
871
	$pooltbl =	'<div class="table-responsive">';
872
	$pooltbl .=		'<table class="table table-striped table-hover table-condensed">';
873
	$pooltbl .=			'<thead>';
874
	$pooltbl .=				'<tr>';
875
	$pooltbl .=					'<th>' . gettext("Pool Start") . '</th>';
876
	$pooltbl .=					'<th>' . gettext("Pool End") . '</th>';
877
	$pooltbl .=					'<th>' . gettext("Description") . '</th>';
878
	$pooltbl .=					'<th>' . gettext("Actions") . '</th>';
879
	$pooltbl .=				'</tr>';
880
	$pooltbl .=			'</thead>';
881
	$pooltbl .=			'<tbody>';
882

    
883
	if (is_array($a_pools)) {
884
		$i = 0;
885
		foreach ($a_pools as $poolent) {
886
			if (!empty($poolent['range']['from']) && !empty($poolent['range']['to'])) {
887
				$pooltbl .= '<tr>';
888
				$pooltbl .= '<td ondblclick="document.location=\'services_dhcp.php?if=' . htmlspecialchars($if) . '&pool=' . $i . '\';">' .
889
							htmlspecialchars($poolent['range']['from']) . '</td>';
890

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

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

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

    
899
				$pooltbl .= ' <a class="fa fa-trash" title="'. gettext("Delete pool") . '" href="services_dhcp.php?if=' . htmlspecialchars($if) . '&act=delpool&id=' . $i . '" usepost></a></td>';
900
				$pooltbl .= '</tr>';
901
			}
902
		$i++;
903
		}
904
	}
905

    
906
	$pooltbl .=			'</tbody>';
907
	$pooltbl .=		'</table>';
908
	$pooltbl .= '</div>';
909

    
910
	return($pooltbl);
911
}
912

    
913
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
914
$pglinks = array("", "services_dhcp.php");
915

    
916
if (!empty($if) && isset($iflist[$if])) {
917
	$pgtitle[] = $iflist[$if];
918
	$pglinks[] = "@self";
919
}
920
$shortcut_section = "dhcp";
921

    
922
include("head.inc");
923

    
924
if ($input_errors) {
925
	print_input_errors($input_errors);
926
}
927

    
928
if ($changes_applied) {
929
	print_apply_result_box($retval);
930
}
931

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

    
936
/* active tabs */
937
$tab_array = array();
938
$tabscounter = 0;
939
$i = 0;
940
$have_small_subnet = false;
941

    
942
foreach ($iflist as $ifent => $ifname) {
943
	$oc = config_get_path("interfaces/{$ifent}");
944

    
945
	/* Not static IPv4 or subnet >= 31 */
946
	if ($oc['subnet'] >= 31) {
947
		$have_small_subnet = true;
948
		$example_name = $ifname;
949
		$example_cidr = $oc['subnet'];
950
		continue;
951
	}
952
	if (!is_ipaddrv4($oc['ipaddr']) || empty($oc['subnet'])) {
953
		continue;
954
	}
955

    
956
	if ($ifent == $if) {
957
		$active = true;
958
	} else {
959
		$active = false;
960
	}
961

    
962
	$tab_array[] = array($ifname, $active, "services_dhcp.php?if={$ifent}");
963
	$tabscounter++;
964
}
965

    
966
if ($tabscounter == 0) {
967
	if ($have_small_subnet) {
968
		$sentence2 = sprintf(gettext('%1$s has a CIDR mask of %2$s, which does not contain enough addresses.'), htmlspecialchars($example_name), htmlspecialchars($example_cidr));
969
	} else {
970
		$sentence2 = gettext("This system has no interfaces configured with a static IPv4 address.");
971
	}
972
	print_info_box(gettext("The DHCP Server requires a static IPv4 subnet large enough to serve addresses to clients.") . " " . $sentence2);
973
	include("foot.inc");
974
	exit;
975
}
976

    
977
display_top_tabs($tab_array);
978

    
979
$form = new Form();
980

    
981
$section = new Form_Section('General Options');
982

    
983
if (!is_numeric($pool) && !($act == "newpool")) {
984
	if (config_path_enabled('dhcrelay')) {
985
		$section->addInput(new Form_Checkbox(
986
			'enable',
987
			'Enable',
988
			gettext("DHCP Relay is currently enabled. DHCP Server canot be enabled while the DHCP Relay is enabled on any interface."),
989
			$pconfig['enable']
990
		))->setAttribute('disabled', true);
991
	} else {
992
		$section->addInput(new Form_Checkbox(
993
			'enable',
994
			'Enable',
995
			sprintf(gettext("Enable DHCP server on %s interface"), htmlspecialchars($iflist[$if])),
996
			$pconfig['enable']
997
		));
998
	}
999
} else {
1000
	print_info_box(gettext('Editing pool-specific options. To return to the Interface, click its tab above.'), 'info', false);
1001
}
1002

    
1003
$section->addInput(new Form_Checkbox(
1004
	'ignorebootp',
1005
	'BOOTP',
1006
	'Ignore BOOTP queries',
1007
	$pconfig['ignorebootp']
1008
));
1009

    
1010
$section->addInput(new Form_Select(
1011
	'denyunknown',
1012
	'Deny unknown clients',
1013
	$pconfig['denyunknown'],
1014
	array(
1015
		"disabled" => "Allow all clients",
1016
		"enabled" => "Allow known clients from any interface",
1017
		"class" => "Allow known clients from only this interface",
1018
	)
1019
))->setHelp('When set to %3$sAllow all clients%4$s, any DHCP client will get an IP address within this scope/range on this interface. '.
1020
	'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. ' .
1021
	'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.',
1022
	'<i>', '</i>', '<b>', '</b>');
1023

    
1024
$section->addInput(new Form_Checkbox(
1025
	'nonak',
1026
	'Ignore denied clients',
1027
	'Ignore denied clients rather than reject',
1028
	$pconfig['nonak']
1029
))->setHelp("This option is not compatible with failover and cannot be enabled when a Failover Peer IP address is configured.");
1030

    
1031
$section->addInput(new Form_Checkbox(
1032
	'ignoreclientuids',
1033
	'Ignore client identifiers',
1034
	'Do not record a unique identifier (UID) in client lease data if present in the client DHCP request',
1035
	$pconfig['ignoreclientuids']
1036
))->setHelp("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.");
1037

    
1038

    
1039
if (is_numeric($pool) || ($act == "newpool")) {
1040
	$section->addInput(new Form_Input(
1041
		'descr',
1042
		'Pool Description',
1043
		'text',
1044
		$pconfig['descr']
1045
	));
1046
}
1047

    
1048
$section->addInput(new Form_StaticText(
1049
	'Subnet',
1050
	gen_subnet($ifcfgip, $ifcfgsn)
1051
));
1052

    
1053
$section->addInput(new Form_StaticText(
1054
	'Subnet mask',
1055
	gen_subnet_mask($ifcfgsn)
1056
));
1057

    
1058
// Compose a string to display the required address ranges
1059
$rangestr = ip_after($subnet_start) . ' - ' . ip_before($subnet_end);
1060

    
1061
if (is_numeric($pool) || ($act == "newpool")) {
1062
	$rangestr .= '<br />' . gettext('In-use DHCP Pool Ranges:');
1063
	$rangearr = config_get_path("dhcpd/{$if}/range", []);
1064
	if (!empty($rangearr)) {
1065
		$rangestr .= '<br />' . array_get_path($rangearr, 'from') . ' - ' . array_get_path($rangearr, 'to');
1066
	}
1067

    
1068
	foreach ($a_pools as $p) {
1069
		$pa = array_get_path($p, 'range', []);
1070
		if (!empty($pa)) {
1071
			$rangestr .= '<br />' . array_get_path($pa, 'from') . ' - ' . array_get_path($pa, 'to');
1072
		}
1073
	}
1074
}
1075

    
1076
$section->addInput(new Form_StaticText(
1077
	'Available range',
1078
	$rangestr
1079
));
1080

    
1081
$group = new Form_Group('*Range');
1082

    
1083
$group->add(new Form_IpAddress(
1084
	'range_from',
1085
	null,
1086
	$pconfig['range_from'],
1087
	'V4'
1088
))->setHelp('From');
1089

    
1090
$group->add(new Form_IpAddress(
1091
	'range_to',
1092
	null,
1093
	$pconfig['range_to'],
1094
	'V4'
1095
))->setHelp('To');
1096

    
1097
$section->add($group);
1098

    
1099
$form->add($section);
1100

    
1101
if (!is_numeric($pool) && !($act == "newpool")) {
1102
	$section = new Form_Section('Additional Pools');
1103

    
1104
	$btnaddpool = new Form_Button(
1105
		'btnaddpool',
1106
		'Add pool',
1107
		'services_dhcp.php?if=' . htmlspecialchars($if) . '&act=newpool',
1108
		'fa-plus'
1109
	);
1110
	$btnaddpool->addClass('btn-success');
1111

    
1112
	$section->addInput(new Form_StaticText(
1113
		'Add',
1114
		$btnaddpool
1115
	))->setHelp('If additional pools of addresses are needed inside of this subnet outside the above Range, they may be specified here.');
1116

    
1117
	if (is_array($a_pools)) {
1118
		$section->addInput(new Form_StaticText(
1119
			null,
1120
			build_pooltable()
1121
		));
1122
	}
1123

    
1124
	$form->add($section);
1125
}
1126

    
1127
$section = new Form_Section('Servers');
1128

    
1129
$section->addInput(new Form_IpAddress(
1130
	'wins1',
1131
	'WINS servers',
1132
	$pconfig['wins1'],
1133
	'V4'
1134
))->setAttribute('placeholder', 'WINS Server 1');
1135

    
1136
$section->addInput(new Form_IpAddress(
1137
	'wins2',
1138
	null,
1139
	$pconfig['wins2'],
1140
	'V4'
1141
))->setAttribute('placeholder', 'WINS Server 2');
1142

    
1143
for ($idx=1; $idx<=4; $idx++) {
1144
	$section->addInput(new Form_IpAddress(
1145
		'dns' . $idx,
1146
		($idx == 1) ? 'DNS servers':null,
1147
		$pconfig['dns' . $idx],
1148
		'V4'
1149
	))->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.':'');
1150
}
1151

    
1152
$form->add($section);
1153

    
1154
//OMAPI
1155
$section = new Form_Section('OMAPI');
1156

    
1157
$section->addInput(new Form_Input(
1158
	'omapi_port',
1159
	'OMAPI Port',
1160
	'text',
1161
	$pconfig['omapi_port']
1162
))->setAttribute('placeholder', 'OMAPI Port')
1163
  ->setHelp('Set the port that OMAPI will listen on. The default port is 7911, leave blank to disable.' .
1164
	    'Only the first OMAPI configuration is used.');
1165

    
1166
$group = new Form_Group('OMAPI Key');
1167

    
1168
$group->add(new Form_Input(
1169
	'omapi_key',
1170
	'OMAPI Key',
1171
	'text',
1172
	$pconfig['omapi_key']
1173
))->setAttribute('placeholder', 'OMAPI Key')
1174
  ->setHelp('Enter a key matching the selected algorithm<br />to secure connections to the OMAPI endpoint.');
1175

    
1176
$group->add(new Form_Checkbox(
1177
	'omapi_gen_key',
1178
	'',
1179
	'Generate New Key',
1180
	$pconfig['omapi_gen_key']
1181
))->setHelp('Generate a new key based<br />on the selected algorithm.');
1182

    
1183
$section->add($group);
1184

    
1185
$section->addInput(new Form_Select(
1186
	'omapi_key_algorithm',
1187
	'Key Algorithm',
1188
	empty($pconfig['omapi_key_algorithm']) ? 'hmac-sha256' : $pconfig['omapi_key_algorithm'], // Set the default algorithm if not previous defined
1189
	array(
1190
		'hmac-md5' => 'HMAC-MD5 (legacy default)',
1191
		'hmac-sha1' => 'HMAC-SHA1',
1192
		'hmac-sha224' => 'HMAC-SHA224',
1193
		'hmac-sha256' => 'HMAC-SHA256 (current bind9 default)',
1194
		'hmac-sha384' => 'HMAC-SHA384',
1195
		'hmac-sha512' => 'HMAC-SHA512 (most secure)',
1196
	)
1197
))->setHelp('Set the algorithm that OMAPI key will use.');
1198

    
1199
$form->add($section);
1200

    
1201
$section = new Form_Section('Other Options');
1202

    
1203
$section->addInput(new Form_IpAddress(
1204
	'gateway',
1205
	'Gateway',
1206
	$pconfig['gateway'],
1207
	'V4'
1208
))->setPattern('[.a-zA-Z0-9_]+')
1209
  ->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.');
1210

    
1211
$section->addInput(new Form_Input(
1212
	'domain',
1213
	'Domain name',
1214
	'text',
1215
	$pconfig['domain']
1216
))->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.');
1217

    
1218
$section->addInput(new Form_Input(
1219
	'domainsearchlist',
1220
	'Domain search list',
1221
	'text',
1222
	$pconfig['domainsearchlist']
1223
))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator.');
1224

    
1225
$section->addInput(new Form_Input(
1226
	'deftime',
1227
	'Default lease time',
1228
	'number',
1229
	$pconfig['deftime']
1230
))->setHelp('This is used for clients that do not ask for a specific expiration time. The default is 7200 seconds.');
1231

    
1232
$section->addInput(new Form_Input(
1233
	'maxtime',
1234
	'Maximum lease time',
1235
	'number',
1236
	$pconfig['maxtime']
1237
))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds.');
1238

    
1239
if (!is_numeric($pool) && !($act == "newpool")) {
1240
	$section->addInput(new Form_IpAddress(
1241
		'failover_peerip',
1242
		'Failover peer IP',
1243
		$pconfig['failover_peerip'],
1244
		'V4'
1245
	))->setHelp('Leave blank to disable. Enter the interface IP address of the other firewall (failover peer) in this subnet. Firewalls must be using CARP. ' .
1246
			'Advertising skew of the CARP VIP on this interface determines whether the DHCP daemon is Primary or Secondary. ' .
1247
			'Ensure the advertising skew for the VIP on one firewall is &lt; 20 and the other is &gt; 20.');
1248

    
1249
	$section->addInput(new Form_Checkbox(
1250
		'staticarp',
1251
		'Static ARP',
1252
		'Enable Static ARP entries',
1253
		$pconfig['staticarp']
1254
	))->setHelp('Restricts communication with the firewall to only hosts listed in static mappings containing both IP addresses and MAC addresses. ' .
1255
			'No other hosts will be able to communicate with the firewall on this interface. ' .
1256
			'This behavior is enforced even when DHCP server is disabled.');
1257

    
1258
	$section->addInput(new Form_Checkbox(
1259
		'dhcpleaseinlocaltime',
1260
		'Time format change',
1261
		'Change DHCP display lease time from UTC to local time',
1262
		$pconfig['dhcpleaseinlocaltime']
1263
	))->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.' .
1264
				' This will be used for all DHCP interfaces lease time.');
1265

    
1266
	$section->addInput(new Form_Checkbox(
1267
		'statsgraph',
1268
		'Statistics graphs',
1269
		'Enable monitoring graphs for DHCP lease statistics',
1270
		$pconfig['statsgraph']
1271
	))->setHelp('Enable this to add DHCP leases statistics to the Monitoring graphs. Disabled by default.');
1272

    
1273
	$section->addInput(new Form_Checkbox(
1274
		'disablepingcheck',
1275
		'Ping check',
1276
		'Disable ping check',
1277
		$pconfig['disablepingcheck']
1278
	))->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.');
1279
}
1280

    
1281
// DDNS
1282
$btnadv = new Form_Button(
1283
	'btnadvdns',
1284
	'Display Advanced',
1285
	null,
1286
	'fa-cog'
1287
);
1288

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

    
1291
$section->addInput(new Form_StaticText(
1292
	'Dynamic DNS',
1293
	$btnadv
1294
));
1295

    
1296
$section->addInput(new Form_Checkbox(
1297
	'ddnsupdate',
1298
	null,
1299
	'Enable registration of DHCP client names in DNS',
1300
	$pconfig['ddnsupdate']
1301
));
1302

    
1303
$section->addInput(new Form_Input(
1304
	'ddnsdomain',
1305
	'DDNS Domain',
1306
	'text',
1307
	$pconfig['ddnsdomain']
1308
))->setHelp('Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
1309

    
1310
$section->addInput(new Form_Checkbox(
1311
	'ddnsforcehostname',
1312
	'DDNS Hostnames',
1313
	'Force dynamic DNS hostname to be the same as configured hostname for Static Mappings',
1314
	$pconfig['ddnsforcehostname']
1315
))->setHelp('Default registers host name option supplied by DHCP client.');
1316

    
1317
$section->addInput(new Form_IpAddress(
1318
	'ddnsdomainprimary',
1319
	'Primary DDNS address',
1320
	$pconfig['ddnsdomainprimary'],
1321
	'BOTH'
1322
))->setHelp('Primary domain name server IP address for the dynamic domain name.');
1323

    
1324
$section->addInput(new Form_IpAddress(
1325
	'ddnsdomainsecondary',
1326
	'Secondary DDNS address',
1327
	$pconfig['ddnsdomainsecondary'],
1328
	'BOTH'
1329
))->setHelp('Secondary domain name server IP address for the dynamic domain name.');
1330

    
1331
$section->addInput(new Form_Input(
1332
	'ddnsdomainkeyname',
1333
	'DNS Domain key',
1334
	'text',
1335
	$pconfig['ddnsdomainkeyname']
1336
))->setHelp('Dynamic DNS domain key name which will be used to register client names in the DNS server.');
1337

    
1338
$section->addInput(new Form_Select(
1339
	'ddnsdomainkeyalgorithm',
1340
	'Key algorithm',
1341
	$pconfig['ddnsdomainkeyalgorithm'],
1342
	$ddnsdomainkeyalgorithms
1343
));
1344

    
1345
$section->addInput(new Form_Input(
1346
	'ddnsdomainkey',
1347
	'DNS Domain key secret',
1348
	'text',
1349
	$pconfig['ddnsdomainkey']
1350
))->setAttribute('placeholder', 'Base64 encoded string')
1351
->setHelp('Dynamic DNS domain key secret which will be used to register client names in the DNS server.');
1352

    
1353
$section->addInput(new Form_Select(
1354
	'ddnsclientupdates',
1355
	'DDNS Client Updates',
1356
	$pconfig['ddnsclientupdates'],
1357
	array(
1358
	    'allow' => gettext('Allow'),
1359
	    'deny' => gettext('Deny'),
1360
	    'ignore' => gettext('Ignore'))
1361
))->setHelp('How Forward entries are handled when client indicates they wish to update DNS.  ' .
1362
	    'Allow prevents DHCP from updating Forward entries, Deny indicates that DHCP will ' .
1363
	    'do the updates and the client should not, Ignore specifies that DHCP will do the ' .
1364
	    'update and the client can also attempt the update usually using a different domain name.');
1365

    
1366
// Advanced MAC
1367
$btnadv = new Form_Button(
1368
	'btnadvmac',
1369
	'Display Advanced',
1370
	null,
1371
	'fa-cog'
1372
);
1373

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

    
1376
$section->addInput(new Form_StaticText(
1377
	'MAC address control',
1378
	$btnadv
1379
));
1380

    
1381
$section->addInput(new Form_Input(
1382
	'mac_allow',
1383
	'MAC Allow',
1384
	'text',
1385
	$pconfig['mac_allow']
1386
))->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');
1387

    
1388
$section->addInput(new Form_Input(
1389
	'mac_deny',
1390
	'MAC Deny',
1391
	'text',
1392
	$pconfig['mac_deny']
1393
))->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');
1394

    
1395
// Advanced NTP
1396
$btnadv = new Form_Button(
1397
	'btnadvntp',
1398
	'Display Advanced',
1399
	null,
1400
	'fa-cog'
1401
);
1402

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

    
1405
$section->addInput(new Form_StaticText(
1406
	'NTP',
1407
	$btnadv
1408
));
1409

    
1410
$section->addInput(new Form_IpAddress(
1411
	'ntp1',
1412
	'NTP Server 1',
1413
	$pconfig['ntp1'],
1414
	'HOSTV4'
1415
));
1416

    
1417
$section->addInput(new Form_IpAddress(
1418
	'ntp2',
1419
	'NTP Server 2',
1420
	$pconfig['ntp2'],
1421
	'HOSTV4'
1422
));
1423

    
1424
$section->addInput(new Form_IpAddress(
1425
	'ntp3',
1426
	'NTP Server 3',
1427
	$pconfig['ntp3'],
1428
	'HOSTV4'
1429
));
1430

    
1431
// Advanced TFTP
1432
$btnadv = new Form_Button(
1433
	'btnadvtftp',
1434
	'Display Advanced',
1435
	null,
1436
	'fa-cog'
1437
);
1438

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

    
1441
$section->addInput(new Form_StaticText(
1442
	'TFTP',
1443
	$btnadv
1444
));
1445

    
1446
$section->addInput(new Form_Input(
1447
	'tftp',
1448
	'TFTP Server',
1449
	'text',
1450
	$pconfig['tftp']
1451
))->setHelp('Leave blank to disable. Enter a valid IP address, hostname or URL for the TFTP server.');
1452

    
1453
// Advanced LDAP
1454
$btnadv = new Form_Button(
1455
	'btnadvldap',
1456
	'Display Advanced',
1457
	null,
1458
	'fa-cog'
1459
);
1460

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

    
1463
$section->addInput(new Form_StaticText(
1464
	'LDAP',
1465
	$btnadv
1466
));
1467

    
1468
$section->addInput(new Form_Input(
1469
	'ldap',
1470
	'LDAP Server URI',
1471
	'text',
1472
	$pconfig['ldap']
1473
))->setHelp('Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com ');
1474

    
1475
// Advanced Network Booting options
1476
$btnadv = new Form_Button(
1477
	'btnadvnwkboot',
1478
	'Display Advanced',
1479
	null,
1480
	'fa-cog'
1481
);
1482

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

    
1485
$section->addInput(new Form_StaticText(
1486
	'Network Booting',
1487
	$btnadv
1488
));
1489

    
1490
$section->addInput(new Form_Checkbox(
1491
	'netboot',
1492
	'Enable',
1493
	'Enable network booting',
1494
	$pconfig['netboot']
1495
));
1496

    
1497
$section->addInput(new Form_IpAddress(
1498
	'nextserver',
1499
	'Next Server',
1500
	$pconfig['nextserver'],
1501
	'V4'
1502
))->setHelp('Enter the IP address of the next server');
1503

    
1504
$section->addInput(new Form_Input(
1505
	'filename',
1506
	'Default BIOS file name',
1507
	'text',
1508
	$pconfig['filename']
1509
));
1510

    
1511
$section->addInput(new Form_Input(
1512
	'filename32',
1513
	'UEFI 32 bit file name',
1514
	'text',
1515
	$pconfig['filename32']
1516
));
1517

    
1518
$section->addInput(new Form_Input(
1519
	'filename64',
1520
	'UEFI 64 bit file name',
1521
	'text',
1522
	$pconfig['filename64']
1523
));
1524

    
1525
$section->addInput(new Form_Input(
1526
	'filename32arm',
1527
	'ARM 32 bit file name',
1528
	'text',
1529
	$pconfig['filename32arm']
1530
));
1531

    
1532
$section->addInput(new Form_Input(
1533
	'filename64arm',
1534
	'ARM 64 bit file name',
1535
	'text',
1536
	$pconfig['filename64arm']
1537
))->setHelp('Both a filename and a boot server must be configured for this to work! ' .
1538
			'All five filenames and a configured boot server are necessary for UEFI & ARM to work! ');
1539

    
1540
$section->addInput(new Form_Input(
1541
	'uefihttpboot',
1542
	'UEFI HTTPBoot URL',
1543
	'text',
1544
	$pconfig['uefihttpboot']
1545
))->setHelp('string-format: http://(servername)/(firmwarepath)');
1546

    
1547
$section->addInput(new Form_Input(
1548
	'rootpath',
1549
	'Root path',
1550
	'text',
1551
	$pconfig['rootpath']
1552
))->setHelp('string-format: iscsi:(servername):(protocol):(port):(LUN):targetname ');
1553

    
1554
// Advanced Additional options
1555
$btnadv = new Form_Button(
1556
	'btnadvopts',
1557
	'Display Advanced',
1558
	null,
1559
	'fa-cog'
1560
);
1561

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

    
1564
$section->addInput(new Form_StaticText(
1565
	'Additional BOOTP/DHCP Options',
1566
	$btnadv
1567
));
1568

    
1569
$form->add($section);
1570

    
1571
$section = new Form_Section('Additional BOOTP/DHCP Options');
1572
$section->addClass('adnlopts');
1573

    
1574
$section->addInput(new Form_StaticText(
1575
	null,
1576
	'<div class="alert alert-info"> ' . gettext('Enter the DHCP option number and the value for each item to include in the DHCP lease information.') . ' ' .
1577
	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>')
1578
));
1579

    
1580
if (!$pconfig['numberoptions']) {
1581
	$pconfig['numberoptions'] = array();
1582
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1583
}
1584

    
1585
$customitemtypes = array(
1586
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1587
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1588
	'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')
1589
);
1590

    
1591
$numrows = count($item) -1;
1592
$counter = 0;
1593

    
1594
$numrows = count($pconfig['numberoptions']['item']) -1;
1595

    
1596
foreach ($pconfig['numberoptions']['item'] as $item) {
1597
	$number = $item['number'];
1598
	$itemtype = $item['type'];
1599
	$value = base64_decode($item['value']);
1600

    
1601
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1602
	$group->addClass('repeatable');
1603

    
1604
	$group->add(new Form_Input(
1605
		'number' . $counter,
1606
		null,
1607
		'number',
1608
		$number,
1609
		['min'=>'1', 'max'=>'254']
1610
	))->setHelp($numrows == $counter ? 'Number':null);
1611

    
1612

    
1613
	$group->add(new Form_Select(
1614
		'itemtype' . $counter,
1615
		null,
1616
		$itemtype,
1617
		$customitemtypes
1618
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1619

    
1620
	$group->add(new Form_Input(
1621
		'value' . $counter,
1622
		null,
1623
		'text',
1624
		$value
1625
	))->setHelp($numrows == $counter ? 'Value':null);
1626

    
1627
	$group->add(new Form_Button(
1628
		'deleterow' . $counter,
1629
		'Delete',
1630
		null,
1631
		'fa-trash'
1632
	))->addClass('btn-warning');
1633

    
1634
	$section->add($group);
1635

    
1636
	$counter++;
1637
}
1638

    
1639
$section->addInput(new Form_Button(
1640
	'addrow',
1641
	'Add',
1642
	null,
1643
	'fa-plus'
1644
))->addClass('btn-success');
1645

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

    
1648
if ($act == "newpool") {
1649
	$form->addGlobal(new Form_Input(
1650
		'act',
1651
		null,
1652
		'hidden',
1653
		'newpool'
1654
	));
1655
}
1656

    
1657
if (is_numeric($pool)) {
1658
	$form->addGlobal(new Form_Input(
1659
		'pool',
1660
		null,
1661
		'hidden',
1662
		$pool
1663
	));
1664
}
1665

    
1666
$form->addGlobal(new Form_Input(
1667
	'if',
1668
	null,
1669
	'hidden',
1670
	$if
1671
));
1672

    
1673
print($form);
1674

    
1675
// DHCP Static Mappings table
1676

    
1677
if (!is_numeric($pool) && !($act == "newpool")) {
1678

    
1679
	// Decide whether display of the Client Id column is needed.
1680
	$got_cid = false;
1681
	if (is_array($a_maps)) {
1682
		foreach ($a_maps as $map) {
1683
			if (!empty($map['cid'])) {
1684
				$got_cid = true;
1685
				break;
1686
			}
1687
		}
1688
	}
1689
?>
1690

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

    
1771
<nav class="action-buttons">
1772
	<a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>" class="btn btn-sm btn-success">
1773
		<i class="fa fa-plus icon-embed-btn"></i>
1774
		<?=gettext("Add")?>
1775
	</a>
1776
</nav>
1777
<?php
1778
}
1779
?>
1780

    
1781
<script type="text/javascript">
1782
//<![CDATA[
1783
events.push(function() {
1784

    
1785
	// Show advanced DNS options ======================================================================================
1786
	var showadvdns = false;
1787

    
1788
	function show_advdns(ispageload) {
1789
		var text;
1790
		// On page load decide the initial state based on the data.
1791
		if (ispageload) {
1792
<?php
1793
			if (!$pconfig['ddnsupdate'] &&
1794
				!$pconfig['ddnsforcehostname'] &&
1795
				empty($pconfig['ddnsdomain']) &&
1796
				empty($pconfig['ddnsdomainprimary']) &&
1797
				empty($pconfig['ddnsdomainsecondary']) &&
1798
			    empty($pconfig['ddnsdomainkeyname']) &&
1799
			    (empty($pconfig['ddnsdomainkeyalgorithm']) || ($pconfig['ddnsdomainkeyalgorithm'] == "hmac-md5")) &&
1800
			    (empty($pconfig['ddnsclientupdates']) || ($pconfig['ddnsclientupdates'] == "allow")) &&
1801
			    empty($pconfig['ddnsdomainkey'])) {
1802
				$showadv = false;
1803
			} else {
1804
				$showadv = true;
1805
			}
1806
?>
1807
			showadvdns = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1808
		} else {
1809
			// It was a click, swap the state.
1810
			showadvdns = !showadvdns;
1811
		}
1812

    
1813
		hideCheckbox('ddnsupdate', !showadvdns);
1814
		hideInput('ddnsdomain', !showadvdns);
1815
		hideCheckbox('ddnsforcehostname', !showadvdns);
1816
		hideInput('ddnsdomainprimary', !showadvdns);
1817
		hideInput('ddnsdomainsecondary', !showadvdns);
1818
		hideInput('ddnsdomainkeyname', !showadvdns);
1819
		hideInput('ddnsdomainkeyalgorithm', !showadvdns);
1820
		hideInput('ddnsdomainkey', !showadvdns);
1821
		hideInput('ddnsclientupdates', !showadvdns);
1822

    
1823
		if (showadvdns) {
1824
			text = "<?=gettext('Hide Advanced');?>";
1825
		} else {
1826
			text = "<?=gettext('Display Advanced');?>";
1827
		}
1828
		$('#btnadvdns').html('<i class="fa fa-cog"></i> ' + text);
1829
	}
1830

    
1831
	$('#btnadvdns').click(function(event) {
1832
		show_advdns();
1833
	});
1834

    
1835
	// Show advanced MAC options ======================================================================================
1836
	var showadvmac = false;
1837

    
1838
	function show_advmac(ispageload) {
1839
		var text;
1840
		// On page load decide the initial state based on the data.
1841
		if (ispageload) {
1842
<?php
1843
			if (empty($pconfig['mac_allow']) && empty($pconfig['mac_deny'])) {
1844
				$showadv = false;
1845
			} else {
1846
				$showadv = true;
1847
			}
1848
?>
1849
			showadvmac = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1850
		} else {
1851
			// It was a click, swap the state.
1852
			showadvmac = !showadvmac;
1853
		}
1854

    
1855
		hideInput('mac_allow', !showadvmac);
1856
		hideInput('mac_deny', !showadvmac);
1857

    
1858
		if (showadvmac) {
1859
			text = "<?=gettext('Hide Advanced');?>";
1860
		} else {
1861
			text = "<?=gettext('Display Advanced');?>";
1862
		}
1863
		$('#btnadvmac').html('<i class="fa fa-cog"></i> ' + text);
1864
	}
1865

    
1866
	$('#btnadvmac').click(function(event) {
1867
		show_advmac();
1868
	});
1869

    
1870
	// Show advanced NTP options ======================================================================================
1871
	var showadvntp = false;
1872

    
1873
	function show_advntp(ispageload) {
1874
		var text;
1875
		// On page load decide the initial state based on the data.
1876
		if (ispageload) {
1877
<?php
1878
			if (empty($pconfig['ntp1']) && empty($pconfig['ntp2']) && empty($pconfig['ntp3']) ) {
1879
				$showadv = false;
1880
			} else {
1881
				$showadv = true;
1882
			}
1883
?>
1884
			showadvntp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1885
		} else {
1886
			// It was a click, swap the state.
1887
			showadvntp = !showadvntp;
1888
		}
1889

    
1890
		hideInput('ntp1', !showadvntp);
1891
		hideInput('ntp2', !showadvntp);
1892
		hideInput('ntp3', !showadvntp);
1893

    
1894
		if (showadvntp) {
1895
			text = "<?=gettext('Hide Advanced');?>";
1896
		} else {
1897
			text = "<?=gettext('Display Advanced');?>";
1898
		}
1899
		$('#btnadvntp').html('<i class="fa fa-cog"></i> ' + text);
1900
	}
1901

    
1902
	$('#btnadvntp').click(function(event) {
1903
		show_advntp();
1904
	});
1905

    
1906
	// Show advanced TFTP options ======================================================================================
1907
	var showadvtftp = false;
1908

    
1909
	function show_advtftp(ispageload) {
1910
		var text;
1911
		// On page load decide the initial state based on the data.
1912
		if (ispageload) {
1913
<?php
1914
			if (empty($pconfig['tftp'])) {
1915
				$showadv = false;
1916
			} else {
1917
				$showadv = true;
1918
			}
1919
?>
1920
			showadvtftp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1921
		} else {
1922
			// It was a click, swap the state.
1923
			showadvtftp = !showadvtftp;
1924
		}
1925

    
1926
		hideInput('tftp', !showadvtftp);
1927

    
1928
		if (showadvtftp) {
1929
			text = "<?=gettext('Hide Advanced');?>";
1930
		} else {
1931
			text = "<?=gettext('Display Advanced');?>";
1932
		}
1933
		$('#btnadvtftp').html('<i class="fa fa-cog"></i> ' + text);
1934
	}
1935

    
1936
	$('#btnadvtftp').click(function(event) {
1937
		show_advtftp();
1938
	});
1939

    
1940
	// Show advanced LDAP options ======================================================================================
1941
	var showadvldap = false;
1942

    
1943
	function show_advldap(ispageload) {
1944
		var text;
1945
		// On page load decide the initial state based on the data.
1946
		if (ispageload) {
1947
<?php
1948
			if (empty($pconfig['ldap'])) {
1949
				$showadv = false;
1950
			} else {
1951
				$showadv = true;
1952
			}
1953
?>
1954
			showadvldap = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1955
		} else {
1956
			// It was a click, swap the state.
1957
			showadvldap = !showadvldap;
1958
		}
1959

    
1960
		hideInput('ldap', !showadvldap);
1961

    
1962
		if (showadvldap) {
1963
			text = "<?=gettext('Hide Advanced');?>";
1964
		} else {
1965
			text = "<?=gettext('Display Advanced');?>";
1966
		}
1967
		$('#btnadvldap').html('<i class="fa fa-cog"></i> ' + text);
1968
	}
1969

    
1970
	$('#btnadvldap').click(function(event) {
1971
		show_advldap();
1972
	});
1973

    
1974
	// Show advanced additional opts options ===========================================================================
1975
	var showadvopts = false;
1976

    
1977
	function show_advopts(ispageload) {
1978
		var text;
1979
		// On page load decide the initial state based on the data.
1980
		if (ispageload) {
1981
<?php
1982
			if (empty($pconfig['numberoptions']) ||
1983
			    (empty($pconfig['numberoptions']['item'][0]['number']) && (empty($pconfig['numberoptions']['item'][0]['value'])))) {
1984
				$showadv = false;
1985
			} else {
1986
				$showadv = true;
1987
			}
1988
?>
1989
			showadvopts = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1990
		} else {
1991
			// It was a click, swap the state.
1992
			showadvopts = !showadvopts;
1993
		}
1994

    
1995
		hideClass('adnlopts', !showadvopts);
1996

    
1997
		if (showadvopts) {
1998
			text = "<?=gettext('Hide Advanced');?>";
1999
		} else {
2000
			text = "<?=gettext('Display Advanced');?>";
2001
		}
2002
		$('#btnadvopts').html('<i class="fa fa-cog"></i> ' + text);
2003
	}
2004

    
2005
	$('#btnadvopts').click(function(event) {
2006
		show_advopts();
2007
	});
2008

    
2009
	// Show advanced Network Booting options ===========================================================================
2010
	var showadvnwkboot = false;
2011

    
2012
	function show_advnwkboot(ispageload) {
2013
		var text;
2014
		// On page load decide the initial state based on the data.
2015
		if (ispageload) {
2016
<?php
2017
			if (empty($pconfig['netboot'])) {
2018
				$showadv = false;
2019
			} else {
2020
				$showadv = true;
2021
			}
2022
?>
2023
			showadvnwkboot = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
2024
		} else {
2025
			// It was a click, swap the state.
2026
			showadvnwkboot = !showadvnwkboot;
2027
		}
2028

    
2029
		hideCheckbox('netboot', !showadvnwkboot);
2030
		hideInput('nextserver', !showadvnwkboot);
2031
		hideInput('filename', !showadvnwkboot);
2032
		hideInput('filename32', !showadvnwkboot);
2033
		hideInput('filename64', !showadvnwkboot);
2034
		hideInput('filename32arm', !showadvnwkboot);
2035
		hideInput('filename64arm', !showadvnwkboot);
2036
		hideInput('uefihttpboot', !showadvnwkboot);
2037
		hideInput('rootpath', !showadvnwkboot);
2038

    
2039
		if (showadvnwkboot) {
2040
			text = "<?=gettext('Hide Advanced');?>";
2041
		} else {
2042
			text = "<?=gettext('Display Advanced');?>";
2043
		}
2044
		$('#btnadvnwkboot').html('<i class="fa fa-cog"></i> ' + text);
2045
	}
2046

    
2047
	$('#btnadvnwkboot').click(function(event) {
2048
		show_advnwkboot();
2049
	});
2050

    
2051
	// ---------- On initial page load ------------------------------------------------------------
2052

    
2053
	show_advdns(true);
2054
	show_advmac(true);
2055
	show_advntp(true);
2056
	show_advtftp(true);
2057
	show_advldap(true);
2058
	show_advopts(true);
2059
	show_advnwkboot(true);
2060

    
2061
	// Suppress "Delete row" button if there are fewer than two rows
2062
	checkLastRow();
2063
});
2064
//]]>
2065
</script>
2066

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