Project

General

Profile

Download (64.6 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 ($iflist as $ifent => $ifname) {
56
		$oc = $config['interfaces'][$ifent];
57
		if (is_array($config['dhcpd'][$ifent]) &&
58
		    isset($config['dhcpd'][$ifent]['enable']) &&
59
		    is_ipaddrv4($oc['ipaddr']) && $oc['subnet'] < 31) {
60
			$if = $ifent;
61
			$found_starting_if = true;
62
			break;
63
		}
64
	}
65

    
66
	/*
67
	 * If there is no DHCP-enabled interface and LAN is a candidate,
68
	 * then choose LAN.
69
	 */
70
	if (!$found_starting_if && isset($iflist['lan']) &&
71
	    is_ipaddrv4($config['interfaces']['lan']['ipaddr']) &&
72
	    $config['interfaces']['lan']['subnet'] < 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 ($iflist as $ifent => $ifname) {
80
			$oc = $config['interfaces'][$ifent];
81

    
82
			/* Not static IPv4 or subnet >= 31 */
83
			if (!is_ipaddrv4($oc['ipaddr']) ||
84
			    empty($oc['subnet']) || $oc['subnet'] < 31) {
85
				continue;
86
			}
87

    
88
			if (!is_array($config['dhcpd'][$ifent]) ||
89
			    !isset($config['dhcpd'][$ifent]['enable'])) {
90
				continue;
91
			}
92

    
93
			$if = $ifent;
94
			break;
95
		}
96
	}
97
}
98

    
99
$act = $_REQUEST['act'];
100

    
101
$a_pools = array();
102

    
103
if (is_array($config['dhcpd'][$if])) {
104
	$pool = $_REQUEST['pool'];
105
	if (is_numeric($_POST['pool'])) {
106
		$pool = $_POST['pool'];
107
	}
108

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

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

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

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

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

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

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

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

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

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

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

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

    
213
$ifcfgip = $config['interfaces'][$if]['ipaddr'];
214
$ifcfgsn = $config['interfaces'][$if]['subnet'];
215

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

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

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

    
229
	return true;
230
}
231

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

    
234
	unset($input_errors);
235

    
236
	$pconfig = $_POST;
237

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

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

    
256
	/* input validation */
257

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
626
		$dhcpd_enable_changed = false;
627

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
777
if ((isset($_POST['save']) || isset($_POST['apply'])) && (!$input_errors)) {
778
	$changes_applied = true;
779
	$retval = 0;
780
	$retvaldhcp = 0;
781
	$retvaldns = 0;
782
	/* dnsmasq_configure calls dhcpd_configure */
783
	/* no need to restart dhcpd twice */
784
	if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic']))	{
785
		$retvaldns |= services_dnsmasq_configure();
786
		if ($retvaldns == 0) {
787
			clear_subsystem_dirty('hosts');
788
			clear_subsystem_dirty('staticmaps');
789
		}
790
	} else if (isset($config['unbound']['enable']) && isset($config['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') && isset($config['installedpackages']['bind']['config'][0]['enable_bind'])) {
808
		$reloadbind = false;
809
		if (is_array($config['installedpackages']['bindzone'])) {
810
			$bindzone = $config['installedpackages']['bindzone']['config'];
811
		} else {
812
			$bindzone = array();
813
		}
814
		for ($x = 0; $x < sizeof($bindzone); $x++) {
815
			$zone = $bindzone[$x];
816
			if ($zone['regdhcpstatic'] == 'on') {
817
				$reloadbind = true;
818
				break;
819
			}
820
		}
821
		if ($reloadbind === true) {
822
			if (file_exists("/usr/local/pkg/bind.inc")) {
823
				require_once("/usr/local/pkg/bind.inc");
824
				bind_sync();
825
			}
826
		}
827
	}
828
	if ($dhcpd_enable_changed) {
829
		$retvalfc |= filter_configure();
830
	}
831

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

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

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

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

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

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

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

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

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

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

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

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

    
909
	return($pooltbl);
910
}
911

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

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

    
921
include("head.inc");
922

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

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

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

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

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

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

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

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

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

    
976
display_top_tabs($tab_array);
977

    
978
$form = new Form();
979

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

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

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

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

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

    
1030
$section->addInput(new Form_Checkbox(
1031
	'ignoreclientuids',
1032
	'Ignore client identifiers',
1033
	'If a client includes a unique identifier in its DHCP request, that UID will not be recorded in its lease.',
1034
	$pconfig['ignoreclientuids']
1035
))->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.");
1036

    
1037

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

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

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

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

    
1060
if (is_numeric($pool) || ($act == "newpool")) {
1061
	$rangestr .= '<br />' . gettext('In-use DHCP Pool Ranges:');
1062
	if (is_array($config['dhcpd'][$if]['range'])) {
1063
		$rangestr .= '<br />' . $config['dhcpd'][$if]['range']['from'] . ' - ' . $config['dhcpd'][$if]['range']['to'];
1064
	}
1065

    
1066
	foreach ($a_pools as $p) {
1067
		if (is_array($p['range'])) {
1068
			$rangestr .= '<br />' . $p['range']['from'] . ' - ' . $p['range']['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: this interface\'s IP if DNS Forwarder or Resolver is enabled, otherwise the servers configured on the System / General Setup page.':'');
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 on this interface of the firewall as the gateway. Specify an alternate gateway here if this is not the correct gateway for the network. Type "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 system 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 machine. Machines must be using CARP. ' .
1243
				'Interface\'s advskew determines whether the DHCPd process is Primary or Secondary. Ensure one machine\'s advskew &lt; 20 (and the other is &gt; 20).');
1244

    
1245
	$section->addInput(new Form_Checkbox(
1246
		'staticarp',
1247
		'Static ARP',
1248
		'Enable Static ARP entries',
1249
		$pconfig['staticarp']
1250
	))->setHelp('This option persists even if DHCP server is disabled. Only the machines listed below will be able to communicate with the firewall on this interface.');
1251

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

    
1260
	$section->addInput(new Form_Checkbox(
1261
		'statsgraph',
1262
		'Statistics graphs',
1263
		'Enable RRD statistics graphs',
1264
		$pconfig['statsgraph']
1265
	))->setHelp('Enable this to add DHCP leases statistics to the RRD graphs. Disabled by default.');
1266

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

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

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

    
1285
$section->addInput(new Form_StaticText(
1286
	'Dynamic DNS',
1287
	$btnadv
1288
));
1289

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

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

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

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

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

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

    
1332
$section->addInput(new Form_Select(
1333
	'ddnsdomainkeyalgorithm',
1334
	'Key algorithm',
1335
	$pconfig['ddnsdomainkeyalgorithm'],
1336
	$ddnsdomainkeyalgorithms
1337
));
1338

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

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

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

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

    
1370
$section->addInput(new Form_StaticText(
1371
	'MAC address control',
1372
	$btnadv
1373
));
1374

    
1375
$section->addInput(new Form_Input(
1376
	'mac_allow',
1377
	'MAC Allow',
1378
	'text',
1379
	$pconfig['mac_allow']
1380
))->setHelp('List of partial MAC addresses to allow, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1381

    
1382
$section->addInput(new Form_Input(
1383
	'mac_deny',
1384
	'MAC Deny',
1385
	'text',
1386
	$pconfig['mac_deny']
1387
))->setHelp('List of partial MAC addresses to deny access, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1388

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

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

    
1399
$section->addInput(new Form_StaticText(
1400
	'NTP',
1401
	$btnadv
1402
));
1403

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

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

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

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

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

    
1435
$section->addInput(new Form_StaticText(
1436
	'TFTP',
1437
	$btnadv
1438
));
1439

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

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

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

    
1457
$section->addInput(new Form_StaticText(
1458
	'LDAP',
1459
	$btnadv
1460
));
1461

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

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

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

    
1479
$section->addInput(new Form_StaticText(
1480
	'Network Booting',
1481
	$btnadv
1482
));
1483

    
1484
$section->addInput(new Form_Checkbox(
1485
	'netboot',
1486
	'Enable',
1487
	'Enables network booting',
1488
	$pconfig['netboot']
1489
));
1490

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

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

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

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

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

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

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

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

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

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

    
1558
$section->addInput(new Form_StaticText(
1559
	'Additional BOOTP/DHCP Options',
1560
	$btnadv
1561
));
1562

    
1563
$form->add($section);
1564

    
1565
$section = new Form_Section('Additional BOOTP/DHCP Options');
1566
$section->addClass('adnlopts');
1567

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

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

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

    
1585
$numrows = count($item) -1;
1586
$counter = 0;
1587

    
1588
$numrows = count($pconfig['numberoptions']['item']) -1;
1589

    
1590
foreach ($pconfig['numberoptions']['item'] as $item) {
1591
	$number = $item['number'];
1592
	$itemtype = $item['type'];
1593
	$value = base64_decode($item['value']);
1594

    
1595
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1596
	$group->addClass('repeatable');
1597

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

    
1606

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

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

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

    
1628
	$section->add($group);
1629

    
1630
	$counter++;
1631
}
1632

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

    
1640
$form->add($section);
1641

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

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

    
1660
$form->addGlobal(new Form_Input(
1661
	'if',
1662
	null,
1663
	'hidden',
1664
	$if
1665
));
1666

    
1667
print($form);
1668

    
1669
// DHCP Static Mappings table
1670

    
1671
if (!is_numeric($pool) && !($act == "newpool")) {
1672

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

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

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

    
1775
<script type="text/javascript">
1776
//<![CDATA[
1777
events.push(function() {
1778

    
1779
	// Show advanced DNS options ======================================================================================
1780
	var showadvdns = false;
1781

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

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

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

    
1825
	$('#btnadvdns').click(function(event) {
1826
		show_advdns();
1827
	});
1828

    
1829
	// Show advanced MAC options ======================================================================================
1830
	var showadvmac = false;
1831

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

    
1849
		hideInput('mac_allow', !showadvmac);
1850
		hideInput('mac_deny', !showadvmac);
1851

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

    
1860
	$('#btnadvmac').click(function(event) {
1861
		show_advmac();
1862
	});
1863

    
1864
	// Show advanced NTP options ======================================================================================
1865
	var showadvntp = false;
1866

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

    
1884
		hideInput('ntp1', !showadvntp);
1885
		hideInput('ntp2', !showadvntp);
1886
		hideInput('ntp3', !showadvntp);
1887

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

    
1896
	$('#btnadvntp').click(function(event) {
1897
		show_advntp();
1898
	});
1899

    
1900
	// Show advanced TFTP options ======================================================================================
1901
	var showadvtftp = false;
1902

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

    
1920
		hideInput('tftp', !showadvtftp);
1921

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

    
1930
	$('#btnadvtftp').click(function(event) {
1931
		show_advtftp();
1932
	});
1933

    
1934
	// Show advanced LDAP options ======================================================================================
1935
	var showadvldap = false;
1936

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

    
1954
		hideInput('ldap', !showadvldap);
1955

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

    
1964
	$('#btnadvldap').click(function(event) {
1965
		show_advldap();
1966
	});
1967

    
1968
	// Show advanced additional opts options ===========================================================================
1969
	var showadvopts = false;
1970

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

    
1989
		hideClass('adnlopts', !showadvopts);
1990

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

    
1999
	$('#btnadvopts').click(function(event) {
2000
		show_advopts();
2001
	});
2002

    
2003
	// Show advanced Network Booting options ===========================================================================
2004
	var showadvnwkboot = false;
2005

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

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

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

    
2041
	$('#btnadvnwkboot').click(function(event) {
2042
		show_advnwkboot();
2043
	});
2044

    
2045
	// ---------- On initial page load ------------------------------------------------------------
2046

    
2047
	show_advdns(true);
2048
	show_advmac(true);
2049
	show_advntp(true);
2050
	show_advtftp(true);
2051
	show_advldap(true);
2052
	show_advopts(true);
2053
	show_advnwkboot(true);
2054

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

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