Project

General

Profile

Download (65.1 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-2022 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['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['dhcpd'] as $dhcpdifitem) {
140
			$dhcpleaseinlocaltime = $dhcpdifitem['dhcpleaseinlocaltime'];
141
			if ($dhcpleaseinlocaltime) {
142
				break;
143
			}
144
		}
145

    
146
		$pconfig['dhcpleaseinlocaltime'] = $dhcpleaseinlocaltime;
147
	} else {
148
		// Options that exist only in pools
149
		$pconfig['descr'] = $dhcpdconf['descr'];
150
	}
151

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

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

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

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

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

    
211
$ifcfgip = $config['interfaces'][$if]['ipaddr'];
212
$ifcfgsn = $config['interfaces'][$if]['subnet'];
213

    
214
$subnet_start = gen_subnetv4($ifcfgip, $ifcfgsn);
215
$subnet_end = gen_subnetv4_max($ifcfgip, $ifcfgsn);
216

    
217
function validate_partial_mac_list($maclist) {
218
	$macs = explode(',', $maclist);
219

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

    
227
	return true;
228
}
229

    
230
if (isset($_POST['save'])) {
231

    
232
	unset($input_errors);
233

    
234
	$pconfig = $_POST;
235

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

    
251
	// Reload the new pconfig variable that the form uses.
252
	$pconfig['numberoptions'] = $numberoptions;
253

    
254
	/* input validation */
255

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

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

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

    
298
			// Convert the bits to bytes
299
			$key_bytes_len = $key_bit_len / 8; // 8 bits = 1 Byte
300

    
301
			// Generate random bytes based on key length
302
			$ran_bytes = openssl_random_pseudo_bytes($key_bytes_len);
303

    
304
			// Encode the bytes to get the key string
305
			$key_str = base64_encode($ran_bytes);
306

    
307
			// Set the key
308
			$_POST['omapi_key'] = $key_str;
309
			$pconfig['omapi_key'] = $key_str;
310

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

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

    
328
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
329
	}
330

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

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

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

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

    
366
	if (isset($config['captiveportal']) && is_array($config['captiveportal'])) {
367
		$deftime = 7200; // Default value if it's empty
368
		if (is_numeric($_POST['deftime'])) {
369
			$deftime = $_POST['deftime'];
370
		}
371

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

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

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

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

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

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

    
464
	$noip = false;
465
	if (is_array($a_maps)) {
466
		foreach ($a_maps as $map) {
467
			if (empty($map['ipaddr'])) {
468
				$noip = true;
469
			}
470
		}
471
	}
472

    
473
	if ($_POST['staticarp'] && $noip) {
474
		$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.");
475
	}
476

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

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

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

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

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

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

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

    
568
		foreach ($a_pools as $id => $p) {
569
			if (is_numeric($pool) && ($id == $pool)) {
570
				continue;
571
			}
572

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

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

    
595
	if (!$input_errors) {
596
		if (!is_numeric($pool)) {
597
			if ($act == "newpool") {
598
				$dhcpdconf = array();
599
			} else {
600
				if (!is_array($config['dhcpd'])) {
601
					$config['dhcpd']= array();
602
				}
603
				if (!is_array($config['dhcpd'][$if])) {
604
					$config['dhcpd'][$if] = array();
605
				}
606
				$dhcpdconf = $config['dhcpd'][$if];
607
			}
608
		} else {
609
			if (is_array($a_pools[$pool])) {
610
				$dhcpdconf = $a_pools[$pool];
611
			} else {
612
				// Someone specified a pool but it doesn't exist. Punt.
613
				header("Location: services_dhcp.php");
614
				exit;
615
			}
616
		}
617
		if (!is_array($dhcpdconf)) {
618
			$dhcpdconf = array();
619
		}
620
		if (!is_array($dhcpdconf['range'])) {
621
			$dhcpdconf['range'] = array();
622
		}
623

    
624
		$dhcpd_enable_changed = false;
625

    
626
		// Global Options
627
		if (!is_numeric($pool) && !($act == "newpool")) {
628
			$old_dhcpd_enable = isset($dhcpdconf['enable']);
629
			$new_dhcpd_enable = ($_POST['enable']) ? true : false;
630
			if ($old_dhcpd_enable != $new_dhcpd_enable) {
631
				/* DHCP has been enabled or disabled. The pf ruleset will need to be rebuilt to allow or disallow DHCP. */
632
				$dhcpd_enable_changed = true;
633
			}
634

    
635
			$dhcpdconf['enable'] = $new_dhcpd_enable;
636
			$dhcpdconf['staticarp'] = ($_POST['staticarp']) ? true : false;
637
			$previous = $dhcpdconf['failover_peerip'];
638
			if ($previous != $_POST['failover_peerip']) {
639
				mwexec("/bin/rm -rf /var/dhcpd/var/db/*");
640
			}
641

    
642
			$dhcpdconf['failover_peerip'] = $_POST['failover_peerip'];
643
			// dhcpleaseinlocaltime is global to all interfaces. So update the setting on all interfaces.
644
			foreach ($config['dhcpd'] as &$dhcpdifitem) {
645
				$dhcpdifitem['dhcpleaseinlocaltime'] = $_POST['dhcpleaseinlocaltime'];
646
			}
647
		} else {
648
			// Options that exist only in pools
649
			$dhcpdconf['descr'] = $_POST['descr'];
650
		}
651

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

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

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

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

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

    
692
		$dhcpdconf['ignoreclientuids'] = ($_POST['ignoreclientuids']) ? true : false;
693
		$dhcpdconf['nonak'] = ($_POST['nonak']) ? true : false;
694
		$dhcpdconf['ddnsdomain'] = $_POST['ddnsdomain'];
695
		$dhcpdconf['ddnsdomainprimary'] = $_POST['ddnsdomainprimary'];
696
		$dhcpdconf['ddnsdomainsecondary'] = (!empty($_POST['ddnsdomainsecondary'])) ? $_POST['ddnsdomainsecondary'] : '';
697
		$dhcpdconf['ddnsdomainkeyname'] = $_POST['ddnsdomainkeyname'];
698
		$dhcpdconf['ddnsdomainkeyalgorithm'] = $_POST['ddnsdomainkeyalgorithm'];
699
		$dhcpdconf['ddnsdomainkey'] = $_POST['ddnsdomainkey'];
700
		$dhcpdconf['ddnsupdate'] = ($_POST['ddnsupdate']) ? true : false;
701
		$dhcpdconf['ddnsforcehostname'] = ($_POST['ddnsforcehostname']) ? true : false;
702
		$dhcpdconf['mac_allow'] = $_POST['mac_allow'];
703
		$dhcpdconf['mac_deny'] = $_POST['mac_deny'];
704
		$dhcpdconf['ddnsclientupdates'] = $_POST['ddnsclientupdates'];
705

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

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

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

    
741
		// Handle the custom options rowhelper
742
		if (isset($dhcpdconf['numberoptions']['item'])) {
743
			unset($dhcpdconf['numberoptions']['item']);
744
		}
745

    
746
		$dhcpdconf['numberoptions'] = $numberoptions;
747

    
748
		if (is_numeric($pool) && is_array($a_pools[$pool])) {
749
			$a_pools[$pool] = $dhcpdconf;
750
		} elseif ($act == "newpool") {
751
			$a_pools[] = $dhcpdconf;
752
		} else {
753
			$config['dhcpd'][$if] = $dhcpdconf;
754
		}
755

    
756
		// OMAPI Settings
757
		if ($_POST['omapi_port'] == ""){
758
			unset($dhcpdconf['omapi_port']);
759
			unset($dhcpdconf['omapi_key']);
760
			unset($dhcpdconf['omapi_key_algorithm']);
761

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

    
771
		write_config(gettext("DHCP Server - Settings changed for interface " . strtoupper($if)));
772
	}
773
}
774

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

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

    
830
	if ($retvaldhcp == 1 || $retvaldns == 1 || $retvalfc == 1) {
831
		$retval = 1;
832
	}
833
}
834

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

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

    
859
		header("Location: services_dhcp.php?if={$if}");
860
		exit;
861
	}
862
}
863

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

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

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

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

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

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

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

    
903
	$pooltbl .=			'</tbody>';
904
	$pooltbl .=		'</table>';
905
	$pooltbl .= '</div>';
906

    
907
	return($pooltbl);
908
}
909

    
910
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
911
$pglinks = array("", "services_dhcp.php");
912

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

    
919
include("head.inc");
920

    
921
if ($input_errors) {
922
	print_input_errors($input_errors);
923
}
924

    
925
if ($changes_applied) {
926
	print_apply_result_box($retval);
927
}
928

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

    
933
/* active tabs */
934
$tab_array = array();
935
$tabscounter = 0;
936
$i = 0;
937
$have_small_subnet = false;
938

    
939
foreach ($iflist as $ifent => $ifname) {
940
	$oc = $config['interfaces'][$ifent];
941

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

    
953
	if ($ifent == $if) {
954
		$active = true;
955
	} else {
956
		$active = false;
957
	}
958

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

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

    
974
display_top_tabs($tab_array);
975

    
976
$form = new Form();
977

    
978
$section = new Form_Section('General Options');
979

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

    
1000
$section->addInput(new Form_Checkbox(
1001
	'ignorebootp',
1002
	'BOOTP',
1003
	'Ignore BOOTP queries',
1004
	$pconfig['ignorebootp']
1005
));
1006

    
1007
$section->addInput(new Form_Select(
1008
	'denyunknown',
1009
	'Deny unknown clients',
1010
	$pconfig['denyunknown'],
1011
	array(
1012
		"disabled" => "Allow all clients",
1013
		"enabled" => "Allow known clients from any interface",
1014
		"class" => "Allow known clients from only this interface",
1015
	)
1016
))->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. '.
1017
	'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. ' .
1018
	'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.',
1019
	'<i>', '</i>', '<b>', '</b>');
1020

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

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

    
1035

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

    
1045
$section->addInput(new Form_StaticText(
1046
	'Subnet',
1047
	gen_subnet($ifcfgip, $ifcfgsn)
1048
));
1049

    
1050
$section->addInput(new Form_StaticText(
1051
	'Subnet mask',
1052
	gen_subnet_mask($ifcfgsn)
1053
));
1054

    
1055
// Compose a string to display the required address ranges
1056
$rangestr = ip_after($subnet_start) . ' - ' . ip_before($subnet_end);
1057

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

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

    
1073
$section->addInput(new Form_StaticText(
1074
	'Available range',
1075
	$rangestr
1076
));
1077

    
1078
$group = new Form_Group('*Range');
1079

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

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

    
1094
$section->add($group);
1095

    
1096
$form->add($section);
1097

    
1098
if (!is_numeric($pool) && !($act == "newpool")) {
1099
	$section = new Form_Section('Additional Pools');
1100

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

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

    
1114
	if (is_array($a_pools)) {
1115
		$section->addInput(new Form_StaticText(
1116
			null,
1117
			build_pooltable()
1118
		));
1119
	}
1120

    
1121
	$form->add($section);
1122
}
1123

    
1124
$section = new Form_Section('Servers');
1125

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

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

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

    
1149
$form->add($section);
1150

    
1151
//OMAPI
1152
$section = new Form_Section('OMAPI');
1153

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

    
1163
$group = new Form_Group('OMAPI Key');
1164

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

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

    
1180
$section->add($group);
1181

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

    
1196
$form->add($section);
1197

    
1198
$section = new Form_Section('Other Options');
1199

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

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

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

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

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

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

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

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

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

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

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

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

    
1288
$section->addInput(new Form_StaticText(
1289
	'Dynamic DNS',
1290
	$btnadv
1291
));
1292

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

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

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

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

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

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

    
1335
$section->addInput(new Form_Select(
1336
	'ddnsdomainkeyalgorithm',
1337
	'Key algorithm',
1338
	$pconfig['ddnsdomainkeyalgorithm'],
1339
	$ddnsdomainkeyalgorithms
1340
));
1341

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

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

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

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

    
1373
$section->addInput(new Form_StaticText(
1374
	'MAC address control',
1375
	$btnadv
1376
));
1377

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

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

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

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

    
1402
$section->addInput(new Form_StaticText(
1403
	'NTP',
1404
	$btnadv
1405
));
1406

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

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

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

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

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

    
1438
$section->addInput(new Form_StaticText(
1439
	'TFTP',
1440
	$btnadv
1441
));
1442

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

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

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

    
1460
$section->addInput(new Form_StaticText(
1461
	'LDAP',
1462
	$btnadv
1463
));
1464

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

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

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

    
1482
$section->addInput(new Form_StaticText(
1483
	'Network Booting',
1484
	$btnadv
1485
));
1486

    
1487
$section->addInput(new Form_Checkbox(
1488
	'netboot',
1489
	'Enable',
1490
	'Enable network booting',
1491
	$pconfig['netboot']
1492
));
1493

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

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

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

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

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

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

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

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

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

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

    
1561
$section->addInput(new Form_StaticText(
1562
	'Additional BOOTP/DHCP Options',
1563
	$btnadv
1564
));
1565

    
1566
$form->add($section);
1567

    
1568
$section = new Form_Section('Additional BOOTP/DHCP Options');
1569
$section->addClass('adnlopts');
1570

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

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

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

    
1588
$numrows = count($item) -1;
1589
$counter = 0;
1590

    
1591
$numrows = count($pconfig['numberoptions']['item']) -1;
1592

    
1593
foreach ($pconfig['numberoptions']['item'] as $item) {
1594
	$number = $item['number'];
1595
	$itemtype = $item['type'];
1596
	$value = base64_decode($item['value']);
1597

    
1598
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1599
	$group->addClass('repeatable');
1600

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

    
1609

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

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

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

    
1631
	$section->add($group);
1632

    
1633
	$counter++;
1634
}
1635

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

    
1643
$form->add($section);
1644

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

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

    
1663
$form->addGlobal(new Form_Input(
1664
	'if',
1665
	null,
1666
	'hidden',
1667
	$if
1668
));
1669

    
1670
print($form);
1671

    
1672
// DHCP Static Mappings table
1673

    
1674
if (!is_numeric($pool) && !($act == "newpool")) {
1675

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

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

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

    
1778
<script type="text/javascript">
1779
//<![CDATA[
1780
events.push(function() {
1781

    
1782
	// Show advanced DNS options ======================================================================================
1783
	var showadvdns = false;
1784

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

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

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

    
1828
	$('#btnadvdns').click(function(event) {
1829
		show_advdns();
1830
	});
1831

    
1832
	// Show advanced MAC options ======================================================================================
1833
	var showadvmac = false;
1834

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

    
1852
		hideInput('mac_allow', !showadvmac);
1853
		hideInput('mac_deny', !showadvmac);
1854

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

    
1863
	$('#btnadvmac').click(function(event) {
1864
		show_advmac();
1865
	});
1866

    
1867
	// Show advanced NTP options ======================================================================================
1868
	var showadvntp = false;
1869

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

    
1887
		hideInput('ntp1', !showadvntp);
1888
		hideInput('ntp2', !showadvntp);
1889
		hideInput('ntp3', !showadvntp);
1890

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

    
1899
	$('#btnadvntp').click(function(event) {
1900
		show_advntp();
1901
	});
1902

    
1903
	// Show advanced TFTP options ======================================================================================
1904
	var showadvtftp = false;
1905

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

    
1923
		hideInput('tftp', !showadvtftp);
1924

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

    
1933
	$('#btnadvtftp').click(function(event) {
1934
		show_advtftp();
1935
	});
1936

    
1937
	// Show advanced LDAP options ======================================================================================
1938
	var showadvldap = false;
1939

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

    
1957
		hideInput('ldap', !showadvldap);
1958

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

    
1967
	$('#btnadvldap').click(function(event) {
1968
		show_advldap();
1969
	});
1970

    
1971
	// Show advanced additional opts options ===========================================================================
1972
	var showadvopts = false;
1973

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

    
1992
		hideClass('adnlopts', !showadvopts);
1993

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

    
2002
	$('#btnadvopts').click(function(event) {
2003
		show_advopts();
2004
	});
2005

    
2006
	// Show advanced Network Booting options ===========================================================================
2007
	var showadvnwkboot = false;
2008

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

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

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

    
2044
	$('#btnadvnwkboot').click(function(event) {
2045
		show_advnwkboot();
2046
	});
2047

    
2048
	// ---------- On initial page load ------------------------------------------------------------
2049

    
2050
	show_advdns(true);
2051
	show_advmac(true);
2052
	show_advntp(true);
2053
	show_advtftp(true);
2054
	show_advldap(true);
2055
	show_advopts(true);
2056
	show_advnwkboot(true);
2057

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

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