Project

General

Profile

Download (64.2 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-2021 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['rootpath'] = $dhcpdconf['rootpath'];
198
	$pconfig['netmask'] = $dhcpdconf['netmask'];
199
	$pconfig['numberoptions'] = $dhcpdconf['numberoptions'];
200
	$pconfig['statsgraph'] = $dhcpdconf['statsgraph'];
201
	$pconfig['disablepingcheck'] = $dhcpdconf['disablepingcheck'];
202
	$pconfig['ddnsclientupdates'] = $dhcpdconf['ddnsclientupdates'];
203

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

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

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

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

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

    
228
	return true;
229
}
230

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

    
233
	unset($input_errors);
234

    
235
	$pconfig = $_POST;
236

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

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

    
255
	/* input validation */
256

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
625
		$dhcpd_enable_changed = false;
626

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

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

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

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

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

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

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

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

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

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

    
718
		$dhcpdconf['tftp'] = $_POST['tftp'];
719
		$dhcpdconf['ldap'] = $_POST['ldap'];
720
		$dhcpdconf['netboot'] = ($_POST['netboot']) ? true : false;
721
		$dhcpdconf['nextserver'] = $_POST['nextserver'];
722
		$dhcpdconf['filename'] = $_POST['filename'];
723
		$dhcpdconf['filename32'] = $_POST['filename32'];
724
		$dhcpdconf['filename64'] = $_POST['filename64'];
725
		$dhcpdconf['filename32arm'] = $_POST['filename32arm'];
726
		$dhcpdconf['filename64arm'] = $_POST['filename64arm'];
727
		$dhcpdconf['rootpath'] = $_POST['rootpath'];
728
		unset($dhcpdconf['statsgraph']);
729
		if ($_POST['statsgraph']) {
730
			$dhcpdconf['statsgraph'] = $_POST['statsgraph'];
731
			enable_rrd_graphing();
732
		}
733
		unset($dhcpdconf['disablepingcheck']);
734
		if ($_POST['disablepingcheck']) {
735
			$dhcpdconf['disablepingcheck'] = $_POST['disablepingcheck'];
736
		}
737

    
738
		// Handle the custom options rowhelper
739
		if (isset($dhcpdconf['numberoptions']['item'])) {
740
			unset($dhcpdconf['numberoptions']['item']);
741
		}
742

    
743
		$dhcpdconf['numberoptions'] = $numberoptions;
744

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

    
753
		// OMAPI Settings
754
		if ($_POST['omapi_port'] == ""){
755
			unset($dhcpdconf['omapi_port']);
756
			unset($dhcpdconf['omapi_key']);
757
			unset($dhcpdconf['omapi_key_algorithm']);
758

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

    
768
		write_config(gettext("DHCP Server - Settings changed for interface " . strtoupper($if)));
769
	}
770
}
771

    
772
if ((isset($_POST['save']) || isset($_POST['apply'])) && (!$input_errors)) {
773
	$changes_applied = true;
774
	$retval = 0;
775
	$retvaldhcp = 0;
776
	$retvaldns = 0;
777
	/* dnsmasq_configure calls dhcpd_configure */
778
	/* no need to restart dhcpd twice */
779
	if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic']))	{
780
		$retvaldns |= services_dnsmasq_configure();
781
		if ($retvaldns == 0) {
782
			clear_subsystem_dirty('hosts');
783
			clear_subsystem_dirty('staticmaps');
784
		}
785
	} else if (isset($config['unbound']['enable']) && isset($config['unbound']['regdhcpstatic'])) {
786
		$retvaldns |= services_unbound_configure();
787
		if ($retvaldns == 0) {
788
			clear_subsystem_dirty('unbound');
789
			clear_subsystem_dirty('hosts');
790
			clear_subsystem_dirty('staticmaps');
791
		}
792
	} else {
793
		$retvaldhcp |= services_dhcpd_configure();
794
		if ($retvaldhcp == 0) {
795
			clear_subsystem_dirty('staticmaps');
796
		}
797
	}
798
	/* BIND package - Bug #3710 */
799
	if (!function_exists('is_package_installed')) {
800
		require_once('pkg-utils.inc');
801
	}
802
	if (is_package_installed('pfSense-pkg-bind') && isset($config['installedpackages']['bind']['config'][0]['enable_bind'])) {
803
		$reloadbind = false;
804
		if (is_array($config['installedpackages']['bindzone'])) {
805
			$bindzone = $config['installedpackages']['bindzone']['config'];
806
		} else {
807
			$bindzone = array();
808
		}
809
		for ($x = 0; $x < sizeof($bindzone); $x++) {
810
			$zone = $bindzone[$x];
811
			if ($zone['regdhcpstatic'] == 'on') {
812
				$reloadbind = true;
813
				break;
814
			}
815
		}
816
		if ($reloadbind === true) {
817
			if (file_exists("/usr/local/pkg/bind.inc")) {
818
				require_once("/usr/local/pkg/bind.inc");
819
				bind_sync();
820
			}
821
		}
822
	}
823
	if ($dhcpd_enable_changed) {
824
		$retvalfc |= filter_configure();
825
	}
826

    
827
	if ($retvaldhcp == 1 || $retvaldns == 1 || $retvalfc == 1) {
828
		$retval = 1;
829
	}
830
}
831

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

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

    
856
		header("Location: services_dhcp.php?if={$if}");
857
		exit;
858
	}
859
}
860

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

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

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

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

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

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

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

    
900
	$pooltbl .=			'</tbody>';
901
	$pooltbl .=		'</table>';
902
	$pooltbl .= '</div>';
903

    
904
	return($pooltbl);
905
}
906

    
907
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
908
$pglinks = array("", "services_dhcp.php");
909

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

    
916
include("head.inc");
917

    
918
if ($input_errors) {
919
	print_input_errors($input_errors);
920
}
921

    
922
if ($changes_applied) {
923
	print_apply_result_box($retval);
924
}
925

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

    
930
/* active tabs */
931
$tab_array = array();
932
$tabscounter = 0;
933
$i = 0;
934
$have_small_subnet = false;
935

    
936
foreach ($iflist as $ifent => $ifname) {
937
	$oc = $config['interfaces'][$ifent];
938

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

    
950
	if ($ifent == $if) {
951
		$active = true;
952
	} else {
953
		$active = false;
954
	}
955

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

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

    
971
display_top_tabs($tab_array);
972

    
973
$form = new Form();
974

    
975
$section = new Form_Section('General Options');
976

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

    
997
$section->addInput(new Form_Checkbox(
998
	'ignorebootp',
999
	'BOOTP',
1000
	'Ignore BOOTP queries',
1001
	$pconfig['ignorebootp']
1002
));
1003

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

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

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

    
1032

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

    
1042
$section->addInput(new Form_StaticText(
1043
	'Subnet',
1044
	gen_subnet($ifcfgip, $ifcfgsn)
1045
));
1046

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

    
1052
// Compose a string to display the required address ranges
1053
$rangestr = ip_after($subnet_start) . ' - ' . ip_before($subnet_end);
1054

    
1055
if (is_numeric($pool) || ($act == "newpool")) {
1056
	$rangestr .= '<br />' . gettext('In-use DHCP Pool Ranges:');
1057
	if (is_array($config['dhcpd'][$if]['range'])) {
1058
		$rangestr .= '<br />' . $config['dhcpd'][$if]['range']['from'] . ' - ' . $config['dhcpd'][$if]['range']['to'];
1059
	}
1060

    
1061
	foreach ($a_pools as $p) {
1062
		if (is_array($p['range'])) {
1063
			$rangestr .= '<br />' . $p['range']['from'] . ' - ' . $p['range']['to'];
1064
		}
1065
	}
1066
}
1067

    
1068
$section->addInput(new Form_StaticText(
1069
	'Available range',
1070
	$rangestr
1071
));
1072

    
1073
$group = new Form_Group('*Range');
1074

    
1075
$group->add(new Form_IpAddress(
1076
	'range_from',
1077
	null,
1078
	$pconfig['range_from'],
1079
	'V4'
1080
))->setHelp('From');
1081

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

    
1089
$section->add($group);
1090

    
1091
$form->add($section);
1092

    
1093
if (!is_numeric($pool) && !($act == "newpool")) {
1094
	$section = new Form_Section('Additional Pools');
1095

    
1096
	$btnaddpool = new Form_Button(
1097
		'btnaddpool',
1098
		'Add pool',
1099
		'services_dhcp.php?if=' . htmlspecialchars($if) . '&act=newpool',
1100
		'fa-plus'
1101
	);
1102
	$btnaddpool->addClass('btn-success');
1103

    
1104
	$section->addInput(new Form_StaticText(
1105
		'Add',
1106
		$btnaddpool
1107
	))->setHelp('If additional pools of addresses are needed inside of this subnet outside the above Range, they may be specified here.');
1108

    
1109
	if (is_array($a_pools)) {
1110
		$section->addInput(new Form_StaticText(
1111
			null,
1112
			build_pooltable()
1113
		));
1114
	}
1115

    
1116
	$form->add($section);
1117
}
1118

    
1119
$section = new Form_Section('Servers');
1120

    
1121
$section->addInput(new Form_IpAddress(
1122
	'wins1',
1123
	'WINS servers',
1124
	$pconfig['wins1'],
1125
	'V4'
1126
))->setAttribute('placeholder', 'WINS Server 1');
1127

    
1128
$section->addInput(new Form_IpAddress(
1129
	'wins2',
1130
	null,
1131
	$pconfig['wins2'],
1132
	'V4'
1133
))->setAttribute('placeholder', 'WINS Server 2');
1134

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

    
1144
$form->add($section);
1145

    
1146
//OMAPI
1147
$section = new Form_Section('OMAPI');
1148

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

    
1158
$group = new Form_Group('OMAPI Key');
1159

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

    
1168
$group->add(new Form_Checkbox(
1169
	'omapi_gen_key',
1170
	'',
1171
	'Generate New Key',
1172
	$pconfig['omapi_gen_key']
1173
))->setHelp('Generate a new key based<br />on the selected algorithm.');
1174

    
1175
$section->add($group);
1176

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

    
1191
$form->add($section);
1192

    
1193
$section = new Form_Section('Other Options');
1194

    
1195
$section->addInput(new Form_IpAddress(
1196
	'gateway',
1197
	'Gateway',
1198
	$pconfig['gateway'],
1199
	'V4'
1200
))->setPattern('[.a-zA-Z0-9_]+')
1201
  ->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.');
1202

    
1203
$section->addInput(new Form_Input(
1204
	'domain',
1205
	'Domain name',
1206
	'text',
1207
	$pconfig['domain']
1208
))->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.');
1209

    
1210
$section->addInput(new Form_Input(
1211
	'domainsearchlist',
1212
	'Domain search list',
1213
	'text',
1214
	$pconfig['domainsearchlist']
1215
))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator.');
1216

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

    
1224
$section->addInput(new Form_Input(
1225
	'maxtime',
1226
	'Maximum lease time',
1227
	'number',
1228
	$pconfig['maxtime']
1229
))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds.');
1230

    
1231
if (!is_numeric($pool) && !($act == "newpool")) {
1232
	$section->addInput(new Form_IpAddress(
1233
		'failover_peerip',
1234
		'Failover peer IP',
1235
		$pconfig['failover_peerip'],
1236
		'V4'
1237
	))->setHelp('Leave blank to disable. Enter the interface IP address of the other machine. Machines must be using CARP. ' .
1238
				'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).');
1239

    
1240
	$section->addInput(new Form_Checkbox(
1241
		'staticarp',
1242
		'Static ARP',
1243
		'Enable Static ARP entries',
1244
		$pconfig['staticarp']
1245
	))->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.');
1246

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

    
1255
	$section->addInput(new Form_Checkbox(
1256
		'statsgraph',
1257
		'Statistics graphs',
1258
		'Enable RRD statistics graphs',
1259
		$pconfig['statsgraph']
1260
	))->setHelp('Enable this to add DHCP leases statistics to the RRD graphs. Disabled by default.');
1261

    
1262
	$section->addInput(new Form_Checkbox(
1263
		'disablepingcheck',
1264
		'Ping check',
1265
		'Disable ping check',
1266
		$pconfig['disablepingcheck']
1267
	))->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.');
1268
}
1269

    
1270
// DDNS
1271
$btnadv = new Form_Button(
1272
	'btnadvdns',
1273
	'Display Advanced',
1274
	null,
1275
	'fa-cog'
1276
);
1277

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

    
1280
$section->addInput(new Form_StaticText(
1281
	'Dynamic DNS',
1282
	$btnadv
1283
));
1284

    
1285
$section->addInput(new Form_Checkbox(
1286
	'ddnsupdate',
1287
	null,
1288
	'Enable registration of DHCP client names in DNS',
1289
	$pconfig['ddnsupdate']
1290
));
1291

    
1292
$section->addInput(new Form_Input(
1293
	'ddnsdomain',
1294
	'DDNS Domain',
1295
	'text',
1296
	$pconfig['ddnsdomain']
1297
))->setHelp('Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
1298

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

    
1306
$section->addInput(new Form_IpAddress(
1307
	'ddnsdomainprimary',
1308
	'Primary DDNS address',
1309
	$pconfig['ddnsdomainprimary'],
1310
	'BOTH'
1311
))->setHelp('Primary domain name server IP address for the dynamic domain name.');
1312

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

    
1320
$section->addInput(new Form_Input(
1321
	'ddnsdomainkeyname',
1322
	'DNS Domain key',
1323
	'text',
1324
	$pconfig['ddnsdomainkeyname']
1325
))->setHelp('Dynamic DNS domain key name which will be used to register client names in the DNS server.');
1326

    
1327
$section->addInput(new Form_Select(
1328
	'ddnsdomainkeyalgorithm',
1329
	'Key algorithm',
1330
	$pconfig['ddnsdomainkeyalgorithm'],
1331
	$ddnsdomainkeyalgorithms
1332
));
1333

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

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

    
1355
// Advanced MAC
1356
$btnadv = new Form_Button(
1357
	'btnadvmac',
1358
	'Display Advanced',
1359
	null,
1360
	'fa-cog'
1361
);
1362

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

    
1365
$section->addInput(new Form_StaticText(
1366
	'MAC address control',
1367
	$btnadv
1368
));
1369

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

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

    
1384
// Advanced NTP
1385
$btnadv = new Form_Button(
1386
	'btnadvntp',
1387
	'Display Advanced',
1388
	null,
1389
	'fa-cog'
1390
);
1391

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

    
1394
$section->addInput(new Form_StaticText(
1395
	'NTP',
1396
	$btnadv
1397
));
1398

    
1399
$section->addInput(new Form_IpAddress(
1400
	'ntp1',
1401
	'NTP Server 1',
1402
	$pconfig['ntp1'],
1403
	'HOSTV4'
1404
));
1405

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

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

    
1420
// Advanced TFTP
1421
$btnadv = new Form_Button(
1422
	'btnadvtftp',
1423
	'Display Advanced',
1424
	null,
1425
	'fa-cog'
1426
);
1427

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

    
1430
$section->addInput(new Form_StaticText(
1431
	'TFTP',
1432
	$btnadv
1433
));
1434

    
1435
$section->addInput(new Form_Input(
1436
	'tftp',
1437
	'TFTP Server',
1438
	'text',
1439
	$pconfig['tftp']
1440
))->setHelp('Leave blank to disable. Enter a valid IP address, hostname or URL for the TFTP server.');
1441

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

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

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

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

    
1464
// Advanced Network Booting options
1465
$btnadv = new Form_Button(
1466
	'btnadvnwkboot',
1467
	'Display Advanced',
1468
	null,
1469
	'fa-cog'
1470
);
1471

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

    
1474
$section->addInput(new Form_StaticText(
1475
	'Network Booting',
1476
	$btnadv
1477
));
1478

    
1479
$section->addInput(new Form_Checkbox(
1480
	'netboot',
1481
	'Enable',
1482
	'Enables network booting',
1483
	$pconfig['netboot']
1484
));
1485

    
1486
$section->addInput(new Form_IpAddress(
1487
	'nextserver',
1488
	'Next Server',
1489
	$pconfig['nextserver'],
1490
	'V4'
1491
))->setHelp('Enter the IP address of the next server');
1492

    
1493
$section->addInput(new Form_Input(
1494
	'filename',
1495
	'Default BIOS file name',
1496
	'text',
1497
	$pconfig['filename']
1498
));
1499

    
1500
$section->addInput(new Form_Input(
1501
	'filename32',
1502
	'UEFI 32 bit file name',
1503
	'text',
1504
	$pconfig['filename32']
1505
));
1506

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

    
1514
$section->addInput(new Form_Input(
1515
	'filename32arm',
1516
	'ARM 32 bit file name',
1517
	'text',
1518
	$pconfig['filename32arm']
1519
));
1520

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

    
1529
$section->addInput(new Form_Input(
1530
	'rootpath',
1531
	'Root path',
1532
	'text',
1533
	$pconfig['rootpath']
1534
))->setHelp('string-format: iscsi:(servername):(protocol):(port):(LUN):targetname ');
1535

    
1536
// Advanced Additional options
1537
$btnadv = new Form_Button(
1538
	'btnadvopts',
1539
	'Display Advanced',
1540
	null,
1541
	'fa-cog'
1542
);
1543

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

    
1546
$section->addInput(new Form_StaticText(
1547
	'Additional BOOTP/DHCP Options',
1548
	$btnadv
1549
));
1550

    
1551
$form->add($section);
1552

    
1553
$section = new Form_Section('Additional BOOTP/DHCP Options');
1554
$section->addClass('adnlopts');
1555

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

    
1562
if (!$pconfig['numberoptions']) {
1563
	$pconfig['numberoptions'] = array();
1564
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1565
}
1566

    
1567
$customitemtypes = array(
1568
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1569
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1570
	'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')
1571
);
1572

    
1573
$numrows = count($item) -1;
1574
$counter = 0;
1575

    
1576
$numrows = count($pconfig['numberoptions']['item']) -1;
1577

    
1578
foreach ($pconfig['numberoptions']['item'] as $item) {
1579
	$number = $item['number'];
1580
	$itemtype = $item['type'];
1581
	$value = base64_decode($item['value']);
1582

    
1583
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1584
	$group->addClass('repeatable');
1585

    
1586
	$group->add(new Form_Input(
1587
		'number' . $counter,
1588
		null,
1589
		'number',
1590
		$number,
1591
		['min'=>'1', 'max'=>'254']
1592
	))->setHelp($numrows == $counter ? 'Number':null);
1593

    
1594

    
1595
	$group->add(new Form_Select(
1596
		'itemtype' . $counter,
1597
		null,
1598
		$itemtype,
1599
		$customitemtypes
1600
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1601

    
1602
	$group->add(new Form_Input(
1603
		'value' . $counter,
1604
		null,
1605
		'text',
1606
		$value
1607
	))->setHelp($numrows == $counter ? 'Value':null);
1608

    
1609
	$group->add(new Form_Button(
1610
		'deleterow' . $counter,
1611
		'Delete',
1612
		null,
1613
		'fa-trash'
1614
	))->addClass('btn-warning');
1615

    
1616
	$section->add($group);
1617

    
1618
	$counter++;
1619
}
1620

    
1621
$section->addInput(new Form_Button(
1622
	'addrow',
1623
	'Add',
1624
	null,
1625
	'fa-plus'
1626
))->addClass('btn-success');
1627

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

    
1630
if ($act == "newpool") {
1631
	$form->addGlobal(new Form_Input(
1632
		'act',
1633
		null,
1634
		'hidden',
1635
		'newpool'
1636
	));
1637
}
1638

    
1639
if (is_numeric($pool)) {
1640
	$form->addGlobal(new Form_Input(
1641
		'pool',
1642
		null,
1643
		'hidden',
1644
		$pool
1645
	));
1646
}
1647

    
1648
$form->addGlobal(new Form_Input(
1649
	'if',
1650
	null,
1651
	'hidden',
1652
	$if
1653
));
1654

    
1655
print($form);
1656

    
1657
// DHCP Static Mappings table
1658

    
1659
if (!is_numeric($pool) && !($act == "newpool")) {
1660

    
1661
	// Decide whether display of the Client Id column is needed.
1662
	$got_cid = false;
1663
	if (is_array($a_maps)) {
1664
		foreach ($a_maps as $map) {
1665
			if (!empty($map['cid'])) {
1666
				$got_cid = true;
1667
				break;
1668
			}
1669
		}
1670
	}
1671
?>
1672

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

    
1753
<nav class="action-buttons">
1754
	<a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>" class="btn btn-sm btn-success">
1755
		<i class="fa fa-plus icon-embed-btn"></i>
1756
		<?=gettext("Add")?>
1757
	</a>
1758
</nav>
1759
<?php
1760
}
1761
?>
1762

    
1763
<script type="text/javascript">
1764
//<![CDATA[
1765
events.push(function() {
1766

    
1767
	// Show advanced DNS options ======================================================================================
1768
	var showadvdns = false;
1769

    
1770
	function show_advdns(ispageload) {
1771
		var text;
1772
		// On page load decide the initial state based on the data.
1773
		if (ispageload) {
1774
<?php
1775
			if (!$pconfig['ddnsupdate'] &&
1776
				!$pconfig['ddnsforcehostname'] &&
1777
				empty($pconfig['ddnsdomain']) &&
1778
				empty($pconfig['ddnsdomainprimary']) &&
1779
				empty($pconfig['ddnsdomainsecondary']) &&
1780
			    empty($pconfig['ddnsdomainkeyname']) &&
1781
			    (empty($pconfig['ddnsdomainkeyalgorithm']) || ($pconfig['ddnsdomainkeyalgorithm'] == "hmac-md5")) &&
1782
			    (empty($pconfig['ddnsclientupdates']) || ($pconfig['ddnsclientupdates'] == "allow")) &&
1783
			    empty($pconfig['ddnsdomainkey'])) {
1784
				$showadv = false;
1785
			} else {
1786
				$showadv = true;
1787
			}
1788
?>
1789
			showadvdns = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1790
		} else {
1791
			// It was a click, swap the state.
1792
			showadvdns = !showadvdns;
1793
		}
1794

    
1795
		hideCheckbox('ddnsupdate', !showadvdns);
1796
		hideInput('ddnsdomain', !showadvdns);
1797
		hideCheckbox('ddnsforcehostname', !showadvdns);
1798
		hideInput('ddnsdomainprimary', !showadvdns);
1799
		hideInput('ddnsdomainsecondary', !showadvdns);
1800
		hideInput('ddnsdomainkeyname', !showadvdns);
1801
		hideInput('ddnsdomainkeyalgorithm', !showadvdns);
1802
		hideInput('ddnsdomainkey', !showadvdns);
1803
		hideInput('ddnsclientupdates', !showadvdns);
1804

    
1805
		if (showadvdns) {
1806
			text = "<?=gettext('Hide Advanced');?>";
1807
		} else {
1808
			text = "<?=gettext('Display Advanced');?>";
1809
		}
1810
		$('#btnadvdns').html('<i class="fa fa-cog"></i> ' + text);
1811
	}
1812

    
1813
	$('#btnadvdns').click(function(event) {
1814
		show_advdns();
1815
	});
1816

    
1817
	// Show advanced MAC options ======================================================================================
1818
	var showadvmac = false;
1819

    
1820
	function show_advmac(ispageload) {
1821
		var text;
1822
		// On page load decide the initial state based on the data.
1823
		if (ispageload) {
1824
<?php
1825
			if (empty($pconfig['mac_allow']) && empty($pconfig['mac_deny'])) {
1826
				$showadv = false;
1827
			} else {
1828
				$showadv = true;
1829
			}
1830
?>
1831
			showadvmac = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1832
		} else {
1833
			// It was a click, swap the state.
1834
			showadvmac = !showadvmac;
1835
		}
1836

    
1837
		hideInput('mac_allow', !showadvmac);
1838
		hideInput('mac_deny', !showadvmac);
1839

    
1840
		if (showadvmac) {
1841
			text = "<?=gettext('Hide Advanced');?>";
1842
		} else {
1843
			text = "<?=gettext('Display Advanced');?>";
1844
		}
1845
		$('#btnadvmac').html('<i class="fa fa-cog"></i> ' + text);
1846
	}
1847

    
1848
	$('#btnadvmac').click(function(event) {
1849
		show_advmac();
1850
	});
1851

    
1852
	// Show advanced NTP options ======================================================================================
1853
	var showadvntp = false;
1854

    
1855
	function show_advntp(ispageload) {
1856
		var text;
1857
		// On page load decide the initial state based on the data.
1858
		if (ispageload) {
1859
<?php
1860
			if (empty($pconfig['ntp1']) && empty($pconfig['ntp2']) && empty($pconfig['ntp3']) ) {
1861
				$showadv = false;
1862
			} else {
1863
				$showadv = true;
1864
			}
1865
?>
1866
			showadvntp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1867
		} else {
1868
			// It was a click, swap the state.
1869
			showadvntp = !showadvntp;
1870
		}
1871

    
1872
		hideInput('ntp1', !showadvntp);
1873
		hideInput('ntp2', !showadvntp);
1874
		hideInput('ntp3', !showadvntp);
1875

    
1876
		if (showadvntp) {
1877
			text = "<?=gettext('Hide Advanced');?>";
1878
		} else {
1879
			text = "<?=gettext('Display Advanced');?>";
1880
		}
1881
		$('#btnadvntp').html('<i class="fa fa-cog"></i> ' + text);
1882
	}
1883

    
1884
	$('#btnadvntp').click(function(event) {
1885
		show_advntp();
1886
	});
1887

    
1888
	// Show advanced TFTP options ======================================================================================
1889
	var showadvtftp = false;
1890

    
1891
	function show_advtftp(ispageload) {
1892
		var text;
1893
		// On page load decide the initial state based on the data.
1894
		if (ispageload) {
1895
<?php
1896
			if (empty($pconfig['tftp'])) {
1897
				$showadv = false;
1898
			} else {
1899
				$showadv = true;
1900
			}
1901
?>
1902
			showadvtftp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1903
		} else {
1904
			// It was a click, swap the state.
1905
			showadvtftp = !showadvtftp;
1906
		}
1907

    
1908
		hideInput('tftp', !showadvtftp);
1909

    
1910
		if (showadvtftp) {
1911
			text = "<?=gettext('Hide Advanced');?>";
1912
		} else {
1913
			text = "<?=gettext('Display Advanced');?>";
1914
		}
1915
		$('#btnadvtftp').html('<i class="fa fa-cog"></i> ' + text);
1916
	}
1917

    
1918
	$('#btnadvtftp').click(function(event) {
1919
		show_advtftp();
1920
	});
1921

    
1922
	// Show advanced LDAP options ======================================================================================
1923
	var showadvldap = false;
1924

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

    
1942
		hideInput('ldap', !showadvldap);
1943

    
1944
		if (showadvldap) {
1945
			text = "<?=gettext('Hide Advanced');?>";
1946
		} else {
1947
			text = "<?=gettext('Display Advanced');?>";
1948
		}
1949
		$('#btnadvldap').html('<i class="fa fa-cog"></i> ' + text);
1950
	}
1951

    
1952
	$('#btnadvldap').click(function(event) {
1953
		show_advldap();
1954
	});
1955

    
1956
	// Show advanced additional opts options ===========================================================================
1957
	var showadvopts = false;
1958

    
1959
	function show_advopts(ispageload) {
1960
		var text;
1961
		// On page load decide the initial state based on the data.
1962
		if (ispageload) {
1963
<?php
1964
			if (empty($pconfig['numberoptions']) ||
1965
			    (empty($pconfig['numberoptions']['item'][0]['number']) && (empty($pconfig['numberoptions']['item'][0]['value'])))) {
1966
				$showadv = false;
1967
			} else {
1968
				$showadv = true;
1969
			}
1970
?>
1971
			showadvopts = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1972
		} else {
1973
			// It was a click, swap the state.
1974
			showadvopts = !showadvopts;
1975
		}
1976

    
1977
		hideClass('adnlopts', !showadvopts);
1978

    
1979
		if (showadvopts) {
1980
			text = "<?=gettext('Hide Advanced');?>";
1981
		} else {
1982
			text = "<?=gettext('Display Advanced');?>";
1983
		}
1984
		$('#btnadvopts').html('<i class="fa fa-cog"></i> ' + text);
1985
	}
1986

    
1987
	$('#btnadvopts').click(function(event) {
1988
		show_advopts();
1989
	});
1990

    
1991
	// Show advanced Network Booting options ===========================================================================
1992
	var showadvnwkboot = false;
1993

    
1994
	function show_advnwkboot(ispageload) {
1995
		var text;
1996
		// On page load decide the initial state based on the data.
1997
		if (ispageload) {
1998
<?php
1999
			if (empty($pconfig['netboot'])) {
2000
				$showadv = false;
2001
			} else {
2002
				$showadv = true;
2003
			}
2004
?>
2005
			showadvnwkboot = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
2006
		} else {
2007
			// It was a click, swap the state.
2008
			showadvnwkboot = !showadvnwkboot;
2009
		}
2010

    
2011
		hideCheckbox('netboot', !showadvnwkboot);
2012
		hideInput('nextserver', !showadvnwkboot);
2013
		hideInput('filename', !showadvnwkboot);
2014
		hideInput('filename32', !showadvnwkboot);
2015
		hideInput('filename64', !showadvnwkboot);
2016
		hideInput('filename32arm', !showadvnwkboot);
2017
		hideInput('filename64arm', !showadvnwkboot);
2018
		hideInput('rootpath', !showadvnwkboot);
2019

    
2020
		if (showadvnwkboot) {
2021
			text = "<?=gettext('Hide Advanced');?>";
2022
		} else {
2023
			text = "<?=gettext('Display Advanced');?>";
2024
		}
2025
		$('#btnadvnwkboot').html('<i class="fa fa-cog"></i> ' + text);
2026
	}
2027

    
2028
	$('#btnadvnwkboot').click(function(event) {
2029
		show_advnwkboot();
2030
	});
2031

    
2032
	// ---------- On initial page load ------------------------------------------------------------
2033

    
2034
	show_advdns(true);
2035
	show_advmac(true);
2036
	show_advntp(true);
2037
	show_advtftp(true);
2038
	show_advldap(true);
2039
	show_advopts(true);
2040
	show_advnwkboot(true);
2041

    
2042
	// Suppress "Delete row" button if there are fewer than two rows
2043
	checkLastRow();
2044
});
2045
//]]>
2046
</script>
2047

    
2048
<?php include("foot.inc");
(120-120/229)