Project

General

Profile

Download (50.6 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	services_dhcp.php
4
*/
5
/* ====================================================================
6
 *	Copyright (c)  2004-2015  Electric Sheep Fencing, LLC. All rights reserved.
7
 *
8
 *	Some or all of this file is based on the m0n0wall project which is
9
 *	Copyright (c)  2004 Manuel Kasper (BSD 2 clause)
10
 *
11
 *	Redistribution and use in source and binary forms, with or without modification,
12
 *	are permitted provided that the following conditions are met:
13
 *
14
 *	1. Redistributions of source code must retain the above copyright notice,
15
 *		this list of conditions and the following disclaimer.
16
 *
17
 *	2. Redistributions in binary form must reproduce the above copyright
18
 *		notice, this list of conditions and the following disclaimer in
19
 *		the documentation and/or other materials provided with the
20
 *		distribution.
21
 *
22
 *	3. All advertising materials mentioning features or use of this software
23
 *		must display the following acknowledgment:
24
 *		"This product includes software developed by the pfSense Project
25
 *		 for use in the pfSense software distribution. (http://www.pfsense.org/).
26
 *
27
 *	4. The names "pfSense" and "pfSense Project" must not be used to
28
 *		 endorse or promote products derived from this software without
29
 *		 prior written permission. For written permission, please contact
30
 *		 coreteam@pfsense.org.
31
 *
32
 *	5. Products derived from this software may not be called "pfSense"
33
 *		nor may "pfSense" appear in their names without prior written
34
 *		permission of the Electric Sheep Fencing, LLC.
35
 *
36
 *	6. Redistributions of any form whatsoever must retain the following
37
 *		acknowledgment:
38
 *
39
 *	"This product includes software developed by the pfSense Project
40
 *	for use in the pfSense software distribution (http://www.pfsense.org/).
41
 *
42
 *	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
43
 *	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44
 *	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
45
 *	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
46
 *	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
47
 *	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
48
 *	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
49
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50
 *	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
51
 *	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
52
 *	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
53
 *	OF THE POSSIBILITY OF SUCH DAMAGE.
54
 *
55
 *	====================================================================
56
 *
57
 */
58

    
59
##|+PRIV
60
##|*IDENT=page-services-dhcpserver
61
##|*NAME=Services: DHCP Server
62
##|*DESCR=Allow access to the 'Services: DHCP Server' page.
63
##|*MATCH=services_dhcp.php*
64
##|-PRIV
65

    
66
require("guiconfig.inc");
67
require_once("filter.inc");
68
require_once('rrd.inc');
69
require_once("shaper.inc");
70

    
71
if (!$g['services_dhcp_server_enable']) {
72
	header("Location: /");
73
	exit;
74
}
75

    
76
$if = $_GET['if'];
77
if (!empty($_POST['if'])) {
78
	$if = $_POST['if'];
79
}
80

    
81
/* if OLSRD is enabled, allow WAN to house DHCP. */
82
if ($config['installedpackages']['olsrd']) {
83
	foreach ($config['installedpackages']['olsrd']['config'] as $olsrd) {
84
		if ($olsrd['enable']) {
85
			$is_olsr_enabled = true;
86
			break;
87
		}
88
	}
89
}
90

    
91
$iflist = get_configured_interface_with_descr();
92

    
93
/* set the starting interface */
94
if (!$if || !isset($iflist[$if])) {
95
	$found_starting_if = false;
96
	// First look for an interface with DHCP already enabled.
97
	foreach ($iflist as $ifent => $ifname) {
98
		$oc = $config['interfaces'][$ifent];
99
		if (is_array($config['dhcpd'][$ifent]) && isset($config['dhcpd'][$ifent]['enable']) && (is_ipaddrv4($oc['ipaddr']))) {
100
			$if = $ifent;
101
			$found_starting_if = true;
102
			break;
103
		}
104
	}
105

    
106
	// If there is no DHCP-enabled interface and LAN is a candidate, then choose LAN.
107
	if (!$found_starting_if && isset($iflist['lan']) && is_ipaddrv4($config['interfaces']['lan']['ipaddr'])) {
108
		$if = 'lan';
109
		$found_starting_if = true;
110
	}
111

    
112
	// At the last select whatever can be found.
113
	if (!$found_starting_if) {
114
		foreach ($iflist as $ifent => $ifname) {
115
			$oc = $config['interfaces'][$ifent];
116
			if ((is_array($config['dhcpd'][$ifent]) && !isset($config['dhcpd'][$ifent]['enable']) && (!is_ipaddrv4($oc['ipaddr']))) ||
117
				(!is_array($config['dhcpd'][$ifent]) && (!is_ipaddrv4($oc['ipaddr'])))) {
118
				continue;
119
			}
120

    
121
			$if = $ifent;
122
			break;
123
		}
124
	}
125
}
126

    
127
$act = $_GET['act'];
128
if (!empty($_POST['act'])) {
129
	$act = $_POST['act'];
130
}
131

    
132
$a_pools = array();
133

    
134
if (is_array($config['dhcpd'][$if])) {
135
	$pool = $_GET['pool'];
136
	if (is_numeric($_POST['pool'])) {
137
		$pool = $_POST['pool'];
138
	}
139

    
140
	// If we have a pool but no interface name, that's not valid. Redirect away.
141
	if (is_numeric($pool) && empty($if)) {
142
		header("Location: services_dhcp.php");
143
		exit;
144
	}
145

    
146
	if (!is_array($config['dhcpd'][$if]['pool'])) {
147
		$config['dhcpd'][$if]['pool'] = array();
148
	}
149

    
150
	$a_pools = &$config['dhcpd'][$if]['pool'];
151

    
152
	if (is_numeric($pool) && $a_pools[$pool]) {
153
		$dhcpdconf = &$a_pools[$pool];
154
	} elseif ($act == "newpool") {
155
		$dhcpdconf = array();
156
	} else {
157
		$dhcpdconf = &$config['dhcpd'][$if];
158
	}
159

    
160
	if (!is_array($config['dhcpd'][$if]['staticmap'])) {
161
		$dhcpdconf['staticmap'] = array();
162
	}
163

    
164
	$a_maps = &$config['dhcpd'][$if]['staticmap'];
165
}
166
if (is_array($dhcpdconf)) {
167
	// Global Options
168
	if (!is_numeric($pool) && !($act == "newpool")) {
169
		$pconfig['enable'] = isset($dhcpdconf['enable']);
170
		$pconfig['staticarp'] = isset($dhcpdconf['staticarp']);
171
		// No reason to specify this per-pool, per the dhcpd.conf man page it needs to be in every
172
		//	 pool and should be specified in every pool both nodes share, so we'll treat it as global
173
		$pconfig['failover_peerip'] = $dhcpdconf['failover_peerip'];
174

    
175
		// dhcpleaseinlocaltime is global to all interfaces. So if it is selected on any interface,
176
		// then show it true/checked.
177
		foreach ($config['dhcpd'] as $dhcpdifitem) {
178
			$dhcpleaseinlocaltime = $dhcpdifitem['dhcpleaseinlocaltime'];
179
			if ($dhcpleaseinlocaltime) {
180
				break;
181
			}
182
		}
183

    
184
		$pconfig['dhcpleaseinlocaltime'] = $dhcpleaseinlocaltime;
185
	} else {
186
		// Options that exist only in pools
187
		$pconfig['descr'] = $dhcpdconf['descr'];
188
	}
189

    
190
	// Options that can be global or per-pool.
191
	if (is_array($dhcpdconf['range'])) {
192
		$pconfig['range_from'] = $dhcpdconf['range']['from'];
193
		$pconfig['range_to'] = $dhcpdconf['range']['to'];
194
	}
195

    
196
	$pconfig['deftime'] = $dhcpdconf['defaultleasetime'];
197
	$pconfig['maxtime'] = $dhcpdconf['maxleasetime'];
198
	$pconfig['gateway'] = $dhcpdconf['gateway'];
199
	$pconfig['domain'] = $dhcpdconf['domain'];
200
	$pconfig['domainsearchlist'] = $dhcpdconf['domainsearchlist'];
201
	list($pconfig['wins1'], $pconfig['wins2']) = $dhcpdconf['winsserver'];
202
	list($pconfig['dns1'], $pconfig['dns2'], $pconfig['dns3'], $pconfig['dns4']) = $dhcpdconf['dnsserver'];
203
	$pconfig['denyunknown'] = isset($dhcpdconf['denyunknown']);
204
	$pconfig['nonak'] = isset($dhcpdconf['nonak']);
205
	$pconfig['ddnsdomain'] = $dhcpdconf['ddnsdomain'];
206
	$pconfig['ddnsdomainprimary'] = $dhcpdconf['ddnsdomainprimary'];
207
	$pconfig['ddnsdomainkeyname'] = $dhcpdconf['ddnsdomainkeyname'];
208
	$pconfig['ddnsdomainkey'] = $dhcpdconf['ddnsdomainkey'];
209
	$pconfig['ddnsupdate'] = isset($dhcpdconf['ddnsupdate']);
210
	$pconfig['mac_allow'] = $dhcpdconf['mac_allow'];
211
	$pconfig['mac_deny'] = $dhcpdconf['mac_deny'];
212
	list($pconfig['ntp1'], $pconfig['ntp2']) = $dhcpdconf['ntpserver'];
213
	$pconfig['tftp'] = $dhcpdconf['tftp'];
214
	$pconfig['ldap'] = $dhcpdconf['ldap'];
215
	$pconfig['netboot'] = isset($dhcpdconf['netboot']);
216
	$pconfig['nextserver'] = $dhcpdconf['nextserver'];
217
	$pconfig['filename'] = $dhcpdconf['filename'];
218
	$pconfig['filename32'] = $dhcpdconf['filename32'];
219
	$pconfig['filename64'] = $dhcpdconf['filename64'];
220
	$pconfig['rootpath'] = $dhcpdconf['rootpath'];
221
	$pconfig['netmask'] = $dhcpdconf['netmask'];
222
	$pconfig['numberoptions'] = $dhcpdconf['numberoptions'];
223
	$pconfig['statsgraph'] = $dhcpdconf['statsgraph'];
224
}
225

    
226
$ifcfgip = $config['interfaces'][$if]['ipaddr'];
227
$ifcfgsn = $config['interfaces'][$if]['subnet'];
228

    
229
$subnet_start = gen_subnetv4($ifcfgip, $ifcfgsn);
230
$subnet_end = gen_subnetv4_max($ifcfgip, $ifcfgsn);
231

    
232
function validate_partial_mac_list($maclist) {
233
	$macs = explode(',', $maclist);
234

    
235
	// Loop through and look for invalid MACs.
236
	foreach ($macs as $mac) {
237
		if (!is_macaddr($mac, true)) {
238
			return false;
239
		}
240
	}
241

    
242
	return true;
243
}
244

    
245
if (isset($_POST['save'])) {
246

    
247
	unset($input_errors);
248

    
249
	$pconfig = $_POST;
250

    
251
	$numberoptions = array();
252
	for ($x = 0; $x < 99; $x++) {
253
		if (isset($_POST["number{$x}"]) && ctype_digit($_POST["number{$x}"])) {
254
			$numbervalue = array();
255
			$numbervalue['number'] = htmlspecialchars($_POST["number{$x}"]);
256
			$numbervalue['type'] = htmlspecialchars($_POST["itemtype{$x}"]);
257
			$numbervalue['value'] = base64_encode($_POST["value{$x}"]);
258
			$numberoptions['item'][] = $numbervalue;
259
		}
260
	}
261

    
262
	// Reload the new pconfig variable that the form uses.
263
	$pconfig['numberoptions'] = $numberoptions;
264

    
265
	/* input validation */
266
	if ($_POST['enable'] || is_numeric($pool) || $act == "newpool") {
267
		$reqdfields = explode(" ", "range_from range_to");
268
		$reqdfieldsn = array(gettext("Range begin"), gettext("Range end"));
269

    
270
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
271

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

    
276
		if (($_POST['range_from'] && !is_ipaddrv4($_POST['range_from']))) {
277
			$input_errors[] = gettext("A valid range must be specified.");
278
		}
279
		if (($_POST['range_to'] && !is_ipaddrv4($_POST['range_to']))) {
280
			$input_errors[] = gettext("A valid range must be specified.");
281
		}
282
		if (($_POST['gateway'] && $_POST['gateway'] != "none" && !is_ipaddrv4($_POST['gateway']))) {
283
			$input_errors[] = gettext("A valid IP address must be specified for the gateway.");
284
		}
285
		if (($_POST['wins1'] && !is_ipaddrv4($_POST['wins1'])) || ($_POST['wins2'] && !is_ipaddrv4($_POST['wins2']))) {
286
			$input_errors[] = gettext("A valid IP address must be specified for the primary/secondary WINS servers.");
287
		}
288
		$parent_ip = get_interface_ip($_POST['if']);
289
		if (is_ipaddrv4($parent_ip) && $_POST['gateway'] && $_POST['gateway'] != "none") {
290
			$parent_sn = get_interface_subnet($_POST['if']);
291
			if (!ip_in_subnet($_POST['gateway'], gen_subnet($parent_ip, $parent_sn) . "/" . $parent_sn) && !ip_in_interface_alias_subnet($_POST['if'], $_POST['gateway'])) {
292
				$input_errors[] = sprintf(gettext("The gateway address %s does not lie within the chosen interface's subnet."), $_POST['gateway']);
293
			}
294
		}
295

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

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

    
304
		if (isset($config['captiveportal']) && is_array($config['captiveportal'])) {
305
			$deftime = 7200; // Default value if it's empty
306
			if (is_numeric($_POST['deftime'])) {
307
				$deftime = $_POST['deftime'];
308
			}
309

    
310
			foreach ($config['captiveportal'] as $cpZone => $cpdata) {
311
				if (!isset($cpdata['enable'])) {
312
					continue;
313
				}
314
				if (!isset($cpdata['timeout']) || !is_numeric($cpdata['timeout'])) {
315
					continue;
316
				}
317
				$cp_ifs = explode(',', $cpdata['interface']);
318
				if (!in_array($if, $cp_ifs)) {
319
					continue;
320
				}
321
				if ($cpdata['timeout'] > $deftime) {
322
					$input_errors[] = sprintf(gettext(
323
						'The Captive Portal zone (%1$s) has Hard Timeout parameter set to a value bigger than Default lease time (%2$s).'), $cpZone, $deftime);
324
				}
325
			}
326
		}
327

    
328
		if ($_POST['maxtime'] && (!is_numeric($_POST['maxtime']) || ($_POST['maxtime'] < 60) || ($_POST['maxtime'] <= $_POST['deftime']))) {
329
			$input_errors[] = gettext("The maximum lease time must be at least 60 seconds and higher than the default lease time.");
330
		}
331
		if (($_POST['ddnsdomain'] && !is_domain($_POST['ddnsdomain']))) {
332
			$input_errors[] = gettext("A valid domain name must be specified for the dynamic DNS registration.");
333
		}
334
		if (($_POST['ddnsdomain'] && !is_ipaddrv4($_POST['ddnsdomainprimary']))) {
335
			$input_errors[] = gettext("A valid primary domain name server IP address must be specified for the dynamic domain name.");
336
		}
337
		if (($_POST['ddnsdomainkey'] && !$_POST['ddnsdomainkeyname']) ||
338
		    ($_POST['ddnsdomainkeyname'] && !$_POST['ddnsdomainkey'])) {
339
			$input_errors[] = gettext("Both a valid domain key and key name must be specified.");
340
		}
341
		if ($_POST['domainsearchlist']) {
342
			$domain_array = preg_split("/[ ;]+/", $_POST['domainsearchlist']);
343
			foreach ($domain_array as $curdomain) {
344
				if (!is_domain($curdomain)) {
345
					$input_errors[] = gettext("A valid domain search list must be specified.");
346
					break;
347
				}
348
			}
349
		}
350

    
351
		// Validate MACs
352
		if (!empty($_POST['mac_allow']) && !validate_partial_mac_list($_POST['mac_allow'])) {
353
			$input_errors[] = gettext("If a mac allow list is specified, it must contain only valid partial MAC addresses.");
354
		}
355
		if (!empty($_POST['mac_deny']) && !validate_partial_mac_list($_POST['mac_deny'])) {
356
			$input_errors[] = gettext("If a mac deny list is specified, it must contain only valid partial MAC addresses.");
357
		}
358

    
359
		if (($_POST['ntp1'] && (!is_ipaddrv4($_POST['ntp1']) && !is_hostname($_POST['ntp1']))) || ($_POST['ntp2'] && (!is_ipaddrv4($_POST['ntp2']) && !is_hostname($_POST['ntp2'])))) {
360
			$input_errors[] = gettext("A valid IP address or hostname must be specified for the primary/secondary NTP servers.");
361
		}
362
		if (($_POST['domain'] && !is_domain($_POST['domain']))) {
363
			$input_errors[] = gettext("A valid domain name must be specified for the DNS domain.");
364
		}
365
		if ($_POST['tftp'] && !is_ipaddrv4($_POST['tftp']) && !is_domain($_POST['tftp']) && !is_URL($_POST['tftp'])) {
366
			$input_errors[] = gettext("A valid IP address or hostname must be specified for the TFTP server.");
367
		}
368
		if (($_POST['nextserver'] && !is_ipaddrv4($_POST['nextserver']))) {
369
			$input_errors[] = gettext("A valid IP address must be specified for the network boot server.");
370
		}
371

    
372
		if (gen_subnet($ifcfgip, $ifcfgsn) == $_POST['range_from']) {
373
			$input_errors[] = gettext("The network address cannot be used in the starting subnet range.");
374
		}
375
		if (gen_subnet_max($ifcfgip, $ifcfgsn) == $_POST['range_to']) {
376
			$input_errors[] = gettext("The broadcast address cannot be used in the ending subnet range.");
377
		}
378

    
379
		// Disallow a range that includes the virtualip
380
		if (is_array($config['virtualip']['vip'])) {
381
			foreach ($config['virtualip']['vip'] as $vip) {
382
				if ($vip['interface'] == $if) {
383
					if ($vip['subnet'] && is_inrange_v4($vip['subnet'], $_POST['range_from'], $_POST['range_to'])) {
384
						$input_errors[] = sprintf(gettext("The subnet range cannot overlap with virtual IP address %s."), $vip['subnet']);
385
					}
386
				}
387
			}
388
		}
389

    
390
		$noip = false;
391
		if (is_array($a_maps)) {
392
			foreach ($a_maps as $map) {
393
				if (empty($map['ipaddr'])) {
394
					$noip = true;
395
				}
396
			}
397
		}
398

    
399
		if ($_POST['staticarp'] && $noip) {
400
			$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.");
401
		}
402

    
403
		if (is_array($pconfig['numberoptions']['item'])) {
404
			foreach ($pconfig['numberoptions']['item'] as $numberoption) {
405
				$numberoption_value = base64_decode($numberoption['value']);
406
				if ($numberoption['type'] == 'text' && strstr($numberoption_value, '"')) {
407
					$input_errors[] = gettext("Text type cannot include quotation marks.");
408
				} else if ($numberoption['type'] == 'string' && !preg_match('/^"[^"]*"$/', $numberoption_value) && !preg_match('/^[0-9a-f]{2}(?:\:[0-9a-f]{2})*$/i', $numberoption_value)) {
409
					$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");
410
				} else if ($numberoption['type'] == 'boolean' && $numberoption_value != 'true' && $numberoption_value != 'false' && $numberoption_value != 'on' && $numberoption_value != 'off') {
411
					$input_errors[] = gettext("Boolean type must be true, false, on, or off.");
412
				} else if ($numberoption['type'] == 'unsigned integer 8' && (!is_numeric($numberoption_value) || $numberoption_value < 0 || $numberoption_value > 255)) {
413
					$input_errors[] = gettext("Unsigned 8-bit integer type must be a number in the range 0 to 255.");
414
				} else if ($numberoption['type'] == 'unsigned integer 16' && (!is_numeric($numberoption_value) || $numberoption_value < 0 || $numberoption_value > 65535)) {
415
					$input_errors[] = gettext("Unsigned 16-bit integer type must be a number in the range 0 to 65535.");
416
				} else if ($numberoption['type'] == 'unsigned integer 32' && (!is_numeric($numberoption_value) || $numberoption_value < 0 || $numberoption_value > 4294967295)) {
417
					$input_errors[] = gettext("Unsigned 32-bit integer type must be a number in the range 0 to 4294967295.");
418
				} else if ($numberoption['type'] == 'signed integer 8' && (!is_numeric($numberoption_value) || $numberoption_value < -128 || $numberoption_value > 127)) {
419
					$input_errors[] = gettext("Signed 8-bit integer type must be a number in the range -128 to 127.");
420
				} else if ($numberoption['type'] == 'signed integer 16' && (!is_numeric($numberoption_value) || $numberoption_value < -32768 || $numberoption_value > 32767)) {
421
					$input_errors[] = gettext("Signed 16-bit integer type must be a number in the range -32768 to 32767.");
422
				} else if ($numberoption['type'] == 'signed integer 32' && (!is_numeric($numberoption_value) || $numberoption_value < -2147483648 || $numberoption_value > 2147483647)) {
423
					$input_errors[] = gettext("Signed 32-bit integer type must be a number in the range -2147483648 to 2147483647.");
424
				} else if ($numberoption['type'] == 'ip-address' && !is_ipaddrv4($numberoption_value) && !is_hostname($numberoption_value)) {
425
					$input_errors[] = gettext("IP address or host type must be an IP address or host name.");
426
				}
427
			}
428
		}
429

    
430
		if (!$input_errors) {
431
			/* make sure the range lies within the current subnet */
432
			if (ip_greater_than($_POST['range_from'], $_POST['range_to'])) {
433
				$input_errors[] = gettext("The range is invalid (first element higher than second element).");
434
			}
435

    
436
			if (!is_inrange_v4($_POST['range_from'], $subnet_start, $subnet_end) ||
437
			    !is_inrange_v4($_POST['range_to'], $subnet_start, $subnet_end)) {
438
				$input_errors[] = gettext("The specified range lies outside of the current subnet.");
439
			}
440

    
441
			if (is_numeric($pool) || ($act == "newpool")) {
442
				if (is_inrange_v4($_POST['range_from'],
443
				    $config['dhcpd'][$if]['range']['from'],
444
				    $config['dhcpd'][$if]['range']['to']) ||
445
				    is_inrange_v4($_POST['range_to'],
446
				    $config['dhcpd'][$if]['range']['from'],
447
				    $config['dhcpd'][$if]['range']['to'])) {
448
					$input_errors[] = gettext("The specified range must not be within the DHCP range for this interface.");
449
				}
450
			}
451

    
452
			foreach ($a_pools as $id => $p) {
453
				if (is_numeric($pool) && ($id == $pool)) {
454
					continue;
455
				}
456

    
457
				if (is_inrange_v4($_POST['range_from'],
458
				    $p['range']['from'], $p['range']['to']) ||
459
				    is_inrange_v4($_POST['range_to'],
460
				    $p['range']['from'], $p['range']['to'])) {
461
					$input_errors[] = gettext("The specified range must not be within the range configured on a DHCP pool for this interface.");
462
					break;
463
				}
464
			}
465

    
466
			/* make sure that the DHCP Relay isn't enabled on this interface */
467
			if (isset($config['dhcrelay']['enable']) && (stristr($config['dhcrelay']['interface'], $if) !== false)) {
468
				$input_errors[] = sprintf(gettext("The DHCP relay on the %s interface must be disabled before enabling the DHCP server."), $iflist[$if]);
469
			}
470

    
471
			if (is_array($a_maps)) {
472
				foreach ($a_maps as $map) {
473
					if (empty($map['ipaddr'])) {
474
						continue;
475
					}
476
					if (is_inrange_v4($map['ipaddr'], $_POST['range_from'], $_POST['range_to'])) {
477
						$input_errors[] = sprintf(gettext("The DHCP range cannot overlap any static DHCP mappings."));
478
						break;
479
					}
480
				}
481
			}
482
		}
483
	}
484

    
485
	if (!$input_errors) {
486
		if (!is_numeric($pool)) {
487
			if ($act == "newpool") {
488
				$dhcpdconf = array();
489
			} else {
490
				if (!is_array($config['dhcpd'][$if])) {
491
					$config['dhcpd'][$if] = array();
492
				}
493
				$dhcpdconf = $config['dhcpd'][$if];
494
			}
495
		} else {
496
			if (is_array($a_pools[$pool])) {
497
				$dhcpdconf = $a_pools[$pool];
498
			} else {
499
				// Someone specified a pool but it doesn't exist. Punt.
500
				header("Location: services_dhcp.php");
501
				exit;
502
			}
503
		}
504
		if (!is_array($dhcpdconf['range'])) {
505
			$dhcpdconf['range'] = array();
506
		}
507

    
508
		$dhcpd_enable_changed = false;
509

    
510
		// Global Options
511
		if (!is_numeric($pool) && !($act == "newpool")) {
512
			$old_dhcpd_enable = isset($dhcpdconf['enable']);
513
			$new_dhcpd_enable = ($_POST['enable']) ? true : false;
514
			if ($old_dhcpd_enable != $new_dhcpd_enable) {
515
				/* DHCP has been enabled or disabled. The pf ruleset will need to be rebuilt to allow or disallow DHCP. */
516
				$dhcpd_enable_changed = true;
517
			}
518

    
519
			$dhcpdconf['enable'] = $new_dhcpd_enable;
520
			$dhcpdconf['staticarp'] = ($_POST['staticarp']) ? true : false;
521
			$previous = $dhcpdconf['failover_peerip'];
522
			if ($previous != $_POST['failover_peerip']) {
523
				mwexec("/bin/rm -rf /var/dhcpd/var/db/*");
524
			}
525

    
526
			$dhcpdconf['failover_peerip'] = $_POST['failover_peerip'];
527
			// dhcpleaseinlocaltime is global to all interfaces. So update the setting on all interfaces.
528
			foreach ($config['dhcpd'] as &$dhcpdifitem) {
529
				$dhcpdifitem['dhcpleaseinlocaltime'] = $_POST['dhcpleaseinlocaltime'];
530
			}
531
		} else {
532
			// Options that exist only in pools
533
			$dhcpdconf['descr'] = $_POST['descr'];
534
		}
535

    
536
		// Options that can be global or per-pool.
537
		$dhcpdconf['range']['from'] = $_POST['range_from'];
538
		$dhcpdconf['range']['to'] = $_POST['range_to'];
539
		$dhcpdconf['defaultleasetime'] = $_POST['deftime'];
540
		$dhcpdconf['maxleasetime'] = $_POST['maxtime'];
541
		$dhcpdconf['netmask'] = $_POST['netmask'];
542

    
543
		unset($dhcpdconf['winsserver']);
544
		if ($_POST['wins1']) {
545
			$dhcpdconf['winsserver'][] = $_POST['wins1'];
546
		}
547
		if ($_POST['wins2']) {
548
			$dhcpdconf['winsserver'][] = $_POST['wins2'];
549
		}
550

    
551
		unset($dhcpdconf['dnsserver']);
552
		if ($_POST['dns1']) {
553
			$dhcpdconf['dnsserver'][] = $_POST['dns1'];
554
		}
555
		if ($_POST['dns2']) {
556
			$dhcpdconf['dnsserver'][] = $_POST['dns2'];
557
		}
558
		if ($_POST['dns3']) {
559
			$dhcpdconf['dnsserver'][] = $_POST['dns3'];
560
		}
561
		if ($_POST['dns4']) {
562
			$dhcpdconf['dnsserver'][] = $_POST['dns4'];
563
		}
564

    
565
		$dhcpdconf['gateway'] = $_POST['gateway'];
566
		$dhcpdconf['domain'] = $_POST['domain'];
567
		$dhcpdconf['domainsearchlist'] = $_POST['domainsearchlist'];
568
		$dhcpdconf['denyunknown'] = ($_POST['denyunknown']) ? true : false;
569
		$dhcpdconf['nonak'] = ($_POST['nonak']) ? true : false;
570
		$dhcpdconf['ddnsdomain'] = $_POST['ddnsdomain'];
571
		$dhcpdconf['ddnsdomainprimary'] = $_POST['ddnsdomainprimary'];
572
		$dhcpdconf['ddnsdomainkeyname'] = $_POST['ddnsdomainkeyname'];
573
		$dhcpdconf['ddnsdomainkey'] = $_POST['ddnsdomainkey'];
574
		$dhcpdconf['ddnsupdate'] = ($_POST['ddnsupdate']) ? true : false;
575
		$dhcpdconf['mac_allow'] = $_POST['mac_allow'];
576
		$dhcpdconf['mac_deny'] = $_POST['mac_deny'];
577

    
578
		unset($dhcpdconf['ntpserver']);
579
		if ($_POST['ntp1']) {
580
			$dhcpdconf['ntpserver'][] = $_POST['ntp1'];
581
		}
582
		if ($_POST['ntp2']) {
583
			$dhcpdconf['ntpserver'][] = $_POST['ntp2'];
584
		}
585

    
586
		$dhcpdconf['tftp'] = $_POST['tftp'];
587
		$dhcpdconf['ldap'] = $_POST['ldap'];
588
		$dhcpdconf['netboot'] = ($_POST['netboot']) ? true : false;
589
		$dhcpdconf['nextserver'] = $_POST['nextserver'];
590
		$dhcpdconf['filename'] = $_POST['filename'];
591
		$dhcpdconf['filename32'] = $_POST['filename32'];
592
		$dhcpdconf['filename64'] = $_POST['filename64'];
593
		$dhcpdconf['rootpath'] = $_POST['rootpath'];
594
		unset($dhcpdconf['statsgraph']);
595
		if ($_POST['statsgraph']) {
596
			$dhcpdconf['statsgraph'] = $_POST['statsgraph'];
597
			enable_rrd_graphing();
598
		}
599

    
600
		// Handle the custom options rowhelper
601
		if (isset($dhcpdconf['numberoptions']['item'])) {
602
			unset($dhcpdconf['numberoptions']['item']);
603
		}
604

    
605
		$dhcpdconf['numberoptions'] = $numberoptions;
606

    
607
		if (is_numeric($pool) && is_array($a_pools[$pool])) {
608
			$a_pools[$pool] = $dhcpdconf;
609
		} elseif ($act == "newpool") {
610
			$a_pools[] = $dhcpdconf;
611
		} else {
612
			$config['dhcpd'][$if] = $dhcpdconf;
613
		}
614

    
615
		write_config();
616
	}
617
}
618

    
619
if ((isset($_POST['save']) || isset($_POST['apply'])) && (!$input_errors)) {
620
	$retval = 0;
621
	$retvaldhcp = 0;
622
	$retvaldns = 0;
623
	/* dnsmasq_configure calls dhcpd_configure */
624
	/* no need to restart dhcpd twice */
625
	if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic']))	{
626
		$retvaldns = services_dnsmasq_configure();
627
		if ($retvaldns == 0) {
628
			clear_subsystem_dirty('hosts');
629
			clear_subsystem_dirty('staticmaps');
630
		}
631
	} else if (isset($config['unbound']['enable']) && isset($config['unbound']['regdhcpstatic'])) {
632
		$retvaldns = services_unbound_configure();
633
		if ($retvaldns == 0) {
634
			clear_subsystem_dirty('unbound');
635
			clear_subsystem_dirty('hosts');
636
			clear_subsystem_dirty('staticmaps');
637
		}
638
	} else {
639
		$retvaldhcp = services_dhcpd_configure();
640
		if ($retvaldhcp == 0) {
641
			clear_subsystem_dirty('staticmaps');
642
		}
643
	}
644
	if ($dhcpd_enable_changed) {
645
		$retvalfc = filter_configure();
646
	}
647

    
648
	if ($retvaldhcp == 1 || $retvaldns == 1 || $retvalfc == 1) {
649
		$retval = 1;
650
	}
651

    
652
	$savemsg = get_std_save_message($retval);
653
}
654

    
655
if ($act == "delpool") {
656
	if ($a_pools[$_GET['id']]) {
657
		unset($a_pools[$_GET['id']]);
658
		write_config();
659
		header("Location: services_dhcp.php?if={$if}");
660
		exit;
661
	}
662
}
663

    
664
if ($act == "del") {
665
	if ($a_maps[$_GET['id']]) {
666
		unset($a_maps[$_GET['id']]);
667
		write_config();
668
		if (isset($config['dhcpd'][$if]['enable'])) {
669
			mark_subsystem_dirty('staticmaps');
670
			if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic'])) {
671
				mark_subsystem_dirty('hosts');
672
			}
673
		}
674

    
675
		header("Location: services_dhcp.php?if={$if}");
676
		exit;
677
	}
678
}
679

    
680
// Build an HTML table that can be inserted into a Form_StaticText element
681
function build_pooltable() {
682
	global $a_pools, $if;
683

    
684
	$pooltbl =	'<div class="table-responsive">';
685
	$pooltbl .=		'<table class="table table-striped table-hover table-condensed">';
686
	$pooltbl .=			'<thead>';
687
	$pooltbl .=				'<tr>';
688
	$pooltbl .=					'<th>' . gettext("Pool Start") . '</th>';
689
	$pooltbl .=					'<th>' . gettext("Pool End") . '</th>';
690
	$pooltbl .=					'<th>' . gettext("Description") . '</th>';
691
	$pooltbl .=					'<th>' . gettext("Actions") . '</th>';
692
	$pooltbl .=				'</tr>';
693
	$pooltbl .=			'</thead>';
694
	$pooltbl .=			'<tbody>';
695

    
696
	if (is_array($a_pools)) {
697
		$i = 0;
698
		foreach ($a_pools as $poolent) {
699
			if (!empty($poolent['range']['from']) && !empty($poolent['range']['to'])) {
700
				$pooltbl .= '<tr>';
701
				$pooltbl .= '<td ondblclick="document.location=\'services_dhcp.php?if=' . htmlspecialchars($if) . '&pool=' . $i . '\';">' .
702
							htmlspecialchars($poolent['range']['from']) . '</td>';
703

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

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

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

    
712
				$pooltbl .= ' <a class="fa fa-trash" title="'. gettext("Delete pool") . '" href="services_dhcp.php?if=' . htmlspecialchars($if) . '&act=delpool&id=' . $i . '"></a></td>';
713
				$pooltbl .= '</tr>';
714
			}
715
		$i++;
716
		}
717
	}
718

    
719
	$pooltbl .=			'</tbody>';
720
	$pooltbl .=		'</table>';
721
	$pooltbl .= '</div>';
722

    
723
	return($pooltbl);
724
}
725

    
726
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
727

    
728
if (!empty($if) && !isset($config['dhcrelay']['enable']) && isset($iflist[$if])) {
729
	$pgtitle[] = $iflist[$if];
730
}
731
$shortcut_section = "dhcp";
732

    
733
include("head.inc");
734

    
735
if ($input_errors) {
736
	print_input_errors($input_errors);
737
}
738

    
739
if ($savemsg) {
740
	print_info_box($savemsg, 'success');
741
}
742

    
743
if (isset($config['dhcrelay']['enable'])) {
744
	print_info_box(gettext("DHCP Relay is currently enabled. Cannot enable the DHCP Server service while the DHCP Relay is enabled on any interface."));
745
	include("foot.inc");
746
	exit;
747
}
748

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

    
753
/* active tabs */
754
$tab_array = array();
755
$tabscounter = 0;
756
$i = 0;
757

    
758
foreach ($iflist as $ifent => $ifname) {
759
	$oc = $config['interfaces'][$ifent];
760
	if ((is_array($config['dhcpd'][$ifent]) && !isset($config['dhcpd'][$ifent]['enable']) && (!is_ipaddrv4($oc['ipaddr']))) ||
761
	    (!is_array($config['dhcpd'][$ifent]) && (!is_ipaddrv4($oc['ipaddr'])))) {
762
		continue;
763
	}
764

    
765
	if ($ifent == $if) {
766
		$active = true;
767
	} else {
768
		$active = false;
769
	}
770

    
771
	$tab_array[] = array($ifname, $active, "services_dhcp.php?if={$ifent}");
772
	$tabscounter++;
773
}
774

    
775
if ($tabscounter == 0) {
776
	print_info_box(gettext("The DHCP Server can only be enabled on interfaces configured with a static IPv4 address. This system has none."));
777
	include("foot.inc");
778
	exit;
779
}
780

    
781
display_top_tabs($tab_array);
782

    
783
$form = new Form();
784

    
785
$section = new Form_Section('General Options');
786

    
787
if (!is_numeric($pool) && !($act == "newpool")) {
788
	$section->addInput(new Form_Checkbox(
789
		'enable',
790
		'Enable',
791
		sprintf(gettext("Enable DHCP server on %s interface"), htmlspecialchars($iflist[$if])),
792
		$pconfig['enable']
793
	));
794
} else {
795
	print_info_box(gettext('Editing pool-specific options. To return to the Interface, click its tab above.'), 'info', false);
796
}
797

    
798
$section->addInput(new Form_Checkbox(
799
	'denyunknown',
800
	'Deny unknown clients',
801
	'Only the clients defined below will get DHCP leases from this server.',
802
	$pconfig['denyunknown']
803
));
804

    
805
$section->addInput(new Form_Checkbox(
806
	'nonak',
807
	'Ignore denied clients',
808
	'Denied clients will be ignored rather than rejected.',
809
	$pconfig['nonak']
810
))->setHelp("This option is not compatible with failover and cannot be enabled when a Failover Peer IP address is configured.");
811

    
812

    
813
if (is_numeric($pool) || ($act == "newpool")) {
814
	$section->addInput(new Form_Input(
815
		'descr',
816
		'Pool Description',
817
		'text',
818
		$pconfig['descr']
819
	));
820
}
821

    
822
$section->addInput(new Form_StaticText(
823
	'Subnet',
824
	gen_subnet($ifcfgip, $ifcfgsn)
825
));
826

    
827
$section->addInput(new Form_StaticText(
828
	'Subnet mask',
829
	gen_subnet_mask($ifcfgsn)
830
));
831

    
832
// Compose a string to display the required address ranges
833
$rangestr = ip_after($subnet_start) . ' - ' . ip_before($subnet_end);
834

    
835
if (is_numeric($pool) || ($act == "newpool")) {
836
	$rangestr .= '<br />' . gettext('In-use DHCP Pool Ranges:');
837
	if (is_array($config['dhcpd'][$if]['range'])) {
838
		$rangestr .= '<br />' . $config['dhcpd'][$if]['range']['from'] . ' - ' . $config['dhcpd'][$if]['range']['to'];
839
	}
840

    
841
	foreach ($a_pools as $p) {
842
		if (is_array($p['range'])) {
843
			$rangestr .= '<br />' . $p['range']['from'] . ' - ' . $p['range']['to'];
844
		}
845
	}
846
}
847

    
848
$section->addInput(new Form_StaticText(
849
	'Available range',
850
	$rangestr
851
));
852

    
853
if ($is_olsr_enabled) {
854
	$section->addInput(new Form_Select(
855
		'netmask',
856
		'Subnet mask',
857
		$pconfig['netmask'],
858
		array_combine(range(32, 1, -1), range(32, 1, -1))
859
	));
860
}
861

    
862
$group = new Form_Group('Range');
863

    
864
$group->add(new Form_IpAddress(
865
	'range_from',
866
	null,
867
	$pconfig['range_from']
868
))->setHelp('From');
869

    
870
$group->add(new Form_IpAddress(
871
	'range_to',
872
	null,
873
	$pconfig['range_to']
874
))->setHelp('To');
875

    
876
$section->add($group);
877

    
878
$form->add($section);
879

    
880
if (!is_numeric($pool) && !($act == "newpool")) {
881
	$section = new Form_Section('Additional Pools');
882

    
883
	$btnaddpool = new Form_Button(
884
		'btnaddpool',
885
		'Add pool',
886
		'services_dhcp.php?if=' . htmlspecialchars($if) . '&act=newpool',
887
		'fa-plus'
888
	);
889
	$btnaddpool->addClass('btn-success');
890

    
891
	$section->addInput(new Form_StaticText(
892
		'Add',
893
		$btnaddpool
894
	))->setHelp('If additional pools of addresses are needed inside of this subnet outside the above Range, they may be specified here.');
895

    
896
	if (is_array($a_pools)) {
897
		$section->addInput(new Form_StaticText(
898
			null,
899
			build_pooltable()
900
		));
901
	}
902

    
903
	$form->add($section);
904
}
905

    
906
$section = new Form_Section('Servers');
907

    
908
$section->addInput(new Form_IpAddress(
909
	'wins1',
910
	'WINS servers',
911
	$pconfig['wins1']
912
))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'WINS Server 1');
913

    
914
$section->addInput(new Form_IpAddress(
915
	'wins2',
916
	null,
917
	$pconfig['wins2']
918
))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'WINS Server 2');
919

    
920
for ($idx=1; $idx<=4; $idx++) {
921
	$section->addInput(new Form_IpAddress(
922
		'dns' . $idx,
923
		($idx == 1) ? 'DNS servers':null,
924
		$pconfig['dns' . $idx]
925
	))->setPattern('[.a-zA-Z0-9_]+')->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.':'');
926
}
927

    
928
$form->add($section);
929

    
930
$section = new Form_Section('Other Options');
931

    
932
$section->addInput(new Form_IpAddress(
933
	'gateway',
934
	'Gateway',
935
	$pconfig['gateway']
936
))->setPattern('[.a-zA-Z0-9_]+')
937
  ->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.');
938

    
939
$section->addInput(new Form_Input(
940
	'domain',
941
	'Domain name',
942
	'text',
943
	$pconfig['domain']
944
))->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.');
945

    
946
$section->addInput(new Form_Input(
947
	'domainsearchlist',
948
	'Domain search list',
949
	'text',
950
	$pconfig['domainsearchlist']
951
))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator.');
952

    
953
$section->addInput(new Form_Input(
954
	'deftime',
955
	'Default lease time',
956
	'number',
957
	$pconfig['deftime']
958
))->setHelp('This is used for clients that do not ask for a specific expiration time. The default is 7200 seconds.');
959

    
960
$section->addInput(new Form_Input(
961
	'maxtime',
962
	'Maximum lease time',
963
	'number',
964
	$pconfig['maxtime']
965
))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds.');
966

    
967
if (!is_numeric($pool) && !($act == "newpool")) {
968
	$section->addInput(new Form_IpAddress(
969
		'failover_peerip',
970
		'Failover peer IP',
971
		$pconfig['failover_peerip']
972
	))->setHelp('Leave blank to disable. Enter the interface IP address of the other machine. Machines must be using CARP. ' .
973
				'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).');
974
}
975

    
976
if (!is_numeric($pool) && !($act == "newpool")) {
977
	$section->addInput(new Form_Checkbox(
978
		'staticarp',
979
		'Static ARP',
980
		'Enable Static ARP entries',
981
		$pconfig['staticarp']
982
	))->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.');
983

    
984
	$section->addInput(new Form_Checkbox(
985
		'dhcpleaseinlocaltime',
986
		'Time format change',
987
		'Change DHCP display lease time from UTC to local time',
988
		$pconfig['dhcpleaseinlocaltime']
989
	))->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.' .
990
				' This will be used for all DHCP interfaces lease time.');
991
	$section->addInput(new Form_Checkbox(
992
		'statsgraph',
993
		'Statistics graphs',
994
		'Enable RRD statistics graphs',
995
		$pconfig['statsgraph']
996
	))->setHelp('Enable this to add DHCP leases statistics to the RRD graphs. Disabled by default.');
997
}
998

    
999
// DDNS
1000
$btnadv = new Form_Button(
1001
	'btnadvdns',
1002
	'Display Advanced',
1003
	null,
1004
	'fa-cog'
1005
);
1006

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

    
1009
$section->addInput(new Form_StaticText(
1010
	'Dynamic DNS',
1011
	$btnadv
1012
));
1013

    
1014
$section->addInput(new Form_Checkbox(
1015
	'ddnsupdate',
1016
	null,
1017
	'Enable registration of DHCP client names in DNS',
1018
	$pconfig['ddnsupdate']
1019
));
1020

    
1021
$section->addInput(new Form_Input(
1022
	'ddnsdomain',
1023
	'DDNS Domain',
1024
	'text',
1025
	$pconfig['ddnsdomain']
1026
))->setHelp('Leave blank to disable dynamic DNS registration.' . '<br />' .
1027
			'Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
1028

    
1029
$section->addInput(new Form_IpAddress(
1030
	'ddnsdomainprimary',
1031
	'Primary DDNS address',
1032
	$pconfig['ddnsdomainprimary']
1033
))->setHelp('Primary domain name server IP address for the dynamic domain name');
1034

    
1035
$section->addInput(new Form_Input(
1036
	'ddnsdomainkeyname',
1037
	'DNS Domain key',
1038
	'text',
1039
	$pconfig['ddnsdomainkeyname']
1040
))->setHelp('Dynamic DNS domain key name which will be used to register client names in the DNS server');
1041

    
1042
$section->addInput(new Form_Input(
1043
	'ddnsdomainkey',
1044
	'DNS Domain key secret',
1045
	'text',
1046
	$pconfig['ddnsdomainkey']
1047
))->setHelp('Dynamic DNS domain key secret which will be used to register client names in the DNS server');
1048

    
1049
// Advanced MAC
1050
$btnadv = new Form_Button(
1051
	'btnadvmac',
1052
	'Display Advanced',
1053
	null,
1054
	'fa-cog'
1055
);
1056

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

    
1059
$section->addInput(new Form_StaticText(
1060
	'MAC address control',
1061
	$btnadv
1062
));
1063

    
1064
$section->addInput(new Form_Input(
1065
	'mac_allow',
1066
	'MAC Allow',
1067
	'text',
1068
	$pconfig['mac_allow']
1069
))->setHelp('List of partial MAC addresses to allow, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1070

    
1071
$section->addInput(new Form_Input(
1072
	'mac_deny',
1073
	'MAC Deny',
1074
	'text',
1075
	$pconfig['mac_deny']
1076
))->setHelp('List of partial MAC addresses to deny access, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1077

    
1078
// Advanced NTP
1079
$btnadv = new Form_Button(
1080
	'btnadvntp',
1081
	'Display Advanced',
1082
	null,
1083
	'fa-cog'
1084
);
1085

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

    
1088
$section->addInput(new Form_StaticText(
1089
	'NTP',
1090
	$btnadv
1091
));
1092

    
1093
$section->addInput(new Form_IpAddress(
1094
	'ntp1',
1095
	'NTP Server 1',
1096
	$pconfig['ntp1']
1097
))->setPattern('[.a-zA-Z0-9_]+');
1098

    
1099
$section->addInput(new Form_IpAddress(
1100
	'ntp2',
1101
	'NTP Server 2',
1102
	$pconfig['ntp2']
1103
))->setPattern('[.a-zA-Z0-9_]+');
1104

    
1105
// Advanced TFTP
1106
$btnadv = new Form_Button(
1107
	'btnadvtftp',
1108
	'Display Advanced',
1109
	null,
1110
	'fa-cog'
1111
);
1112

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

    
1115
$section->addInput(new Form_StaticText(
1116
	'TFTP',
1117
	$btnadv
1118
));
1119

    
1120
$section->addInput(new Form_IpAddress(
1121
	'tftp',
1122
	'TFTP Server',
1123
	$pconfig['tftp']
1124
))->setHelp('Leave blank to disable.  Enter a full hostname or IP for the TFTP server.')->setPattern('[.a-zA-Z0-9_]+');
1125

    
1126
// Advanced LDAP
1127
$btnadv = new Form_Button(
1128
	'btnadvldap',
1129
	'Display Advanced',
1130
	null,
1131
	'fa-cog'
1132
);
1133

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

    
1136
$section->addInput(new Form_StaticText(
1137
	'LDAP',
1138
	$btnadv
1139
));
1140

    
1141
$section->addInput(new Form_Input(
1142
	'ldap',
1143
	'LDAP Server URI',
1144
	'text',
1145
	$pconfig['ldap']
1146
))->setHelp('Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com ');
1147

    
1148
// Advanced Additional options
1149
$btnadv = new Form_Button(
1150
	'btnadvopts',
1151
	'Display Advanced',
1152
	null,
1153
	'fa-cog'
1154
);
1155

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

    
1158
$section->addInput(new Form_StaticText(
1159
	'Additional BOOTP/DHCP Options',
1160
	$btnadv
1161
));
1162

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

    
1165
$section = new Form_Section('Additional BOOTP/DHCP Options');
1166
$section->addClass('adnlopts');
1167

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

    
1174
if (!$pconfig['numberoptions']) {
1175
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1176
}
1177

    
1178
$customitemtypes = array(
1179
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1180
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1181
	'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')
1182
);
1183

    
1184
$numrows = count($item) -1;
1185
$counter = 0;
1186

    
1187
$numrows = count($pconfig['numberoptions']['item']) -1;
1188

    
1189
foreach ($pconfig['numberoptions']['item'] as $item) {
1190
	$number = $item['number'];
1191
	$itemtype = $item['type'];
1192
	$value = base64_decode($item['value']);
1193

    
1194
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1195
	$group->addClass('repeatable');
1196

    
1197
	$group->add(new Form_Input(
1198
		'number' . $counter,
1199
		null,
1200
		'text',
1201
		$number
1202
	))->setHelp($numrows == $counter ? 'Number':null);
1203

    
1204

    
1205
	$group->add(new Form_Select(
1206
		'itemtype' . $counter,
1207
		null,
1208
		$itemtype,
1209
		$customitemtypes
1210
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1211

    
1212
	$group->add(new Form_Input(
1213
		'value' . $counter,
1214
		null,
1215
		'text',
1216
		$value
1217
	))->setHelp($numrows == $counter ? 'Value':null);
1218

    
1219
	$group->add(new Form_Button(
1220
		'deleterow' . $counter,
1221
		'Delete',
1222
		null,
1223
		'fa-trash'
1224
	))->addClass('btn-warning');
1225

    
1226
	$section->add($group);
1227

    
1228
	$counter++;
1229
}
1230

    
1231
$section->addInput(new Form_Button(
1232
	'addrow',
1233
	'Add',
1234
	null,
1235
	'fa-plus'
1236
))->addClass('btn-success');
1237

    
1238
$form->add($section);
1239

    
1240
if ($pconfig['netboot']) {
1241
	$sectate = COLLAPSIBLE|SEC_OPEN;
1242
} else {
1243
	$sectate = COLLAPSIBLE|SEC_CLOSED;
1244
}
1245
$section = new Form_Section("Network Booting", nwkbootsec, $sectate);
1246

    
1247
$section->addInput(new Form_Checkbox(
1248
	'netboot',
1249
	'Enable',
1250
	'Enables network booting',
1251
	$pconfig['netboot']
1252
));
1253

    
1254
$section->addInput(new Form_IpAddress(
1255
	'nextserver',
1256
	'Next Server',
1257
	$pconfig['nextserver']
1258
))->setHelp('Enter the IP address of the next server');
1259

    
1260
$section->addInput(new Form_Input(
1261
	'filename',
1262
	'Default BIOS file name',
1263
	'text',
1264
	$pconfig['filename']
1265
));
1266

    
1267
$section->addInput(new Form_Input(
1268
	'filename32',
1269
	'UEFI 32 bit file name',
1270
	'text',
1271
	$pconfig['filename32']
1272
));
1273

    
1274
$section->addInput(new Form_Input(
1275
	'filename64',
1276
	'UEFI 64 bit file name',
1277
	'text',
1278
	$pconfig['filename64']
1279
))->setHelp('Both a filename and a boot server must be configured for this to work! ' .
1280
			'All three filenames and a configured boot server are necessary for UEFI to work! ');
1281

    
1282
$section->addInput(new Form_Input(
1283
	'rootpath',
1284
	'Root path',
1285
	'text',
1286
	$pconfig['rootpath']
1287
))->setHelp('string-format: iscsi:(servername):(protocol):(port):(LUN):targetname ');
1288

    
1289
$form->add($section);
1290

    
1291
if ($act == "newpool") {
1292
	$form->addGlobal(new Form_Input(
1293
		'act',
1294
		null,
1295
		'hidden',
1296
		'newpool'
1297
	));
1298
}
1299

    
1300
if (is_numeric($pool)) {
1301
	$form->addGlobal(new Form_Input(
1302
		'pool',
1303
		null,
1304
		'hidden',
1305
		$pool
1306
	));
1307
}
1308

    
1309
$form->addGlobal(new Form_Input(
1310
	'if',
1311
	null,
1312
	'hidden',
1313
	$if
1314
));
1315

    
1316
print($form);
1317

    
1318
// DHCP Static Mappings table
1319

    
1320
if (!is_numeric($pool) && !($act == "newpool")) {
1321
?>
1322

    
1323
<div class="panel panel-default">
1324
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("DHCP Static Mappings for this Interface")?></h2></div>
1325
	<div class="table-responsive">
1326
			<table class="table table-striped table-hover table-condensed">
1327
				<thead>
1328
					<tr>
1329
						<th><?=gettext("Static ARP")?></th>
1330
						<th><?=gettext("MAC address")?></th>
1331
						<th><?=gettext("IP address")?></th>
1332
						<th><?=gettext("Hostname")?></th>
1333
						<th><?=gettext("Description")?></th>
1334
						<th></th>
1335
					</tr>
1336
				</thead>
1337
<?php
1338
	if (is_array($a_maps)) {
1339
		$i = 0;
1340
?>
1341
				<tbody>
1342
<?php
1343
		foreach ($a_maps as $mapent) {
1344
?>
1345
					<tr>
1346
						<td class="text-center" ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1347
							<?php if (isset($mapent['arp_table_static_entry'])): ?>
1348
								<i class="fa fa-check"></i>
1349
							<?php endif; ?>
1350
						</td>
1351
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1352
							<?=htmlspecialchars($mapent['mac'])?>
1353
						</td>
1354
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1355
							<?=htmlspecialchars($mapent['ipaddr'])?>
1356
						</td>
1357
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1358
							<?=htmlspecialchars($mapent['hostname'])?>
1359
						</td>
1360
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1361
							<?=htmlspecialchars($mapent['descr'])?>
1362
						</td>
1363
						<td>
1364
							<a class="fa fa-pencil"	title="<?=gettext('Edit static mapping')?>"	href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>"></a>
1365
							<a class="fa fa-trash"	title="<?=gettext('Delete static mapping')?>"	href="services_dhcp.php?if=<?=htmlspecialchars($if)?>&amp;act=del&amp;id=<?=$i?>"></a>
1366
						</td>
1367
					</tr>
1368
<?php
1369
		$i++;
1370
		}
1371
?>
1372
				</tbody>
1373
<?php
1374
	}
1375
?>
1376
		</table>
1377
	</div>
1378
</div>
1379

    
1380
<nav class="action-buttons">
1381
	<a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>" class="btn btn-sm btn-success">
1382
		<i class="fa fa-plus icon-embed-btn"></i>
1383
		<?=gettext("Add")?>
1384
	</a>
1385
</nav>
1386
<?php
1387
}
1388
?>
1389

    
1390
<script type="text/javascript">
1391
//<![CDATA[
1392
events.push(function() {
1393

    
1394
	// Show advanced DNS options ======================================================================================
1395
	var showadvdns = false;
1396

    
1397
	function show_advdns(ispageload) {
1398
		var text;
1399
		// On page load decide the initial state based on the data.
1400
		if (ispageload) {
1401
<?php
1402
			if (!$pconfig['ddnsupdate'] && empty($pconfig['ddnsdomain']) && empty($pconfig['ddnsdomainprimary']) &&
1403
			    empty($pconfig['ddnsdomainkeyname']) && empty($pconfig['ddnsdomainkey'])) {
1404
				$showadv = false;
1405
			} else {
1406
				$showadv = true;
1407
			}
1408
?>
1409
			showadvdns = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1410
		} else {
1411
			// It was a click, swap the state.
1412
			showadvdns = !showadvdns;
1413
		}
1414

    
1415
		hideCheckbox('ddnsupdate', !showadvdns);
1416
		hideInput('ddnsdomain', !showadvdns);
1417
		hideInput('ddnsdomainprimary', !showadvdns);
1418
		hideInput('ddnsdomainkeyname', !showadvdns);
1419
		hideInput('ddnsdomainkey', !showadvdns);
1420

    
1421
		if (showadvdns) {
1422
			text = "<?=gettext('Hide Advanced');?>";
1423
		} else {
1424
			text = "<?=gettext('Display Advanced');?>";
1425
		}
1426
		$('#btnadvdns').html('<i class="fa fa-cog"></i> ' + text);
1427
	}
1428

    
1429
	$('#btnadvdns').click(function(event) {
1430
		show_advdns();
1431
	});
1432

    
1433
	// Show advanced MAC options ======================================================================================
1434
	var showadvmac = false;
1435

    
1436
	function show_advmac(ispageload) {
1437
		var text;
1438
		// On page load decide the initial state based on the data.
1439
		if (ispageload) {
1440
<?php
1441
			if (empty($pconfig['mac_allow']) && empty($pconfig['mac_deny'])) {
1442
				$showadv = false;
1443
			} else {
1444
				$showadv = true;
1445
			}
1446
?>
1447
			showadvmac = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1448
		} else {
1449
			// It was a click, swap the state.
1450
			showadvmac = !showadvmac;
1451
		}
1452

    
1453
		hideInput('mac_allow', !showadvmac);
1454
		hideInput('mac_deny', !showadvmac);
1455

    
1456
		if (showadvmac) {
1457
			text = "<?=gettext('Hide Advanced');?>";
1458
		} else {
1459
			text = "<?=gettext('Display Advanced');?>";
1460
		}
1461
		$('#btnadvmac').html('<i class="fa fa-cog"></i> ' + text);
1462
	}
1463

    
1464
	$('#btnadvmac').click(function(event) {
1465
		show_advmac();
1466
	});
1467

    
1468
	// Show advanced NTP options ======================================================================================
1469
	var showadvntp = false;
1470

    
1471
	function show_advntp(ispageload) {
1472
		var text;
1473
		// On page load decide the initial state based on the data.
1474
		if (ispageload) {
1475
<?php
1476
			if (empty($pconfig['ntp1']) && empty($pconfig['ntp2'])) {
1477
				$showadv = false;
1478
			} else {
1479
				$showadv = true;
1480
			}
1481
?>
1482
			showadvntp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1483
		} else {
1484
			// It was a click, swap the state.
1485
			showadvntp = !showadvntp;
1486
		}
1487

    
1488
		hideInput('ntp1', !showadvntp);
1489
		hideInput('ntp2', !showadvntp);
1490

    
1491
		if (showadvntp) {
1492
			text = "<?=gettext('Hide Advanced');?>";
1493
		} else {
1494
			text = "<?=gettext('Display Advanced');?>";
1495
		}
1496
		$('#btnadvntp').html('<i class="fa fa-cog"></i> ' + text);
1497
	}
1498

    
1499
	$('#btnadvntp').click(function(event) {
1500
		show_advntp();
1501
	});
1502

    
1503
	// Show advanced TFTP options ======================================================================================
1504
	var showadvtftp = false;
1505

    
1506
	function show_advtftp(ispageload) {
1507
		var text;
1508
		// On page load decide the initial state based on the data.
1509
		if (ispageload) {
1510
<?php
1511
			if (empty($pconfig['tftp'])) {
1512
				$showadv = false;
1513
			} else {
1514
				$showadv = true;
1515
			}
1516
?>
1517
			showadvtftp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1518
		} else {
1519
			// It was a click, swap the state.
1520
			showadvtftp = !showadvtftp;
1521
		}
1522

    
1523
		hideInput('tftp', !showadvtftp);
1524

    
1525
		if (showadvtftp) {
1526
			text = "<?=gettext('Hide Advanced');?>";
1527
		} else {
1528
			text = "<?=gettext('Display Advanced');?>";
1529
		}
1530
		$('#btnadvtftp').html('<i class="fa fa-cog"></i> ' + text);
1531
	}
1532

    
1533
	$('#btnadvtftp').click(function(event) {
1534
		show_advtftp();
1535
	});
1536

    
1537
	// Show advanced LDAP options ======================================================================================
1538
	var showadvldap = false;
1539

    
1540
	function show_advldap(ispageload) {
1541
		var text;
1542
		// On page load decide the initial state based on the data.
1543
		if (ispageload) {
1544
<?php
1545
			if (empty($pconfig['ldap'])) {
1546
				$showadv = false;
1547
			} else {
1548
				$showadv = true;
1549
			}
1550
?>
1551
			showadvldap = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1552
		} else {
1553
			// It was a click, swap the state.
1554
			showadvldap = !showadvldap;
1555
		}
1556

    
1557
		hideInput('ldap', !showadvldap);
1558

    
1559
		if (showadvldap) {
1560
			text = "<?=gettext('Hide Advanced');?>";
1561
		} else {
1562
			text = "<?=gettext('Display Advanced');?>";
1563
		}
1564
		$('#btnadvldap').html('<i class="fa fa-cog"></i> ' + text);
1565
	}
1566

    
1567
	$('#btnadvldap').click(function(event) {
1568
		show_advldap();
1569
	});
1570

    
1571
	// Show advanced additional opts options ===========================================================================
1572
	var showadvopts = false;
1573

    
1574
	function show_advopts(ispageload) {
1575
		var text;
1576
		// On page load decide the initial state based on the data.
1577
		if (ispageload) {
1578
<?php
1579
			if (empty($pconfig['numberoptions']) ||
1580
			    (empty($pconfig['numberoptions']['item'][0]['number']) && (empty($pconfig['numberoptions']['item'][0]['value'])))) {
1581
				$showadv = false;
1582
			} else {
1583
				$showadv = true;
1584
			}
1585
?>
1586
			showadvopts = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1587
		} else {
1588
			// It was a click, swap the state.
1589
			showadvopts = !showadvopts;
1590
		}
1591

    
1592
		hideClass('adnlopts', !showadvopts);
1593

    
1594
		if (showadvopts) {
1595
			text = "<?=gettext('Hide Advanced');?>";
1596
		} else {
1597
			text = "<?=gettext('Display Advanced');?>";
1598
		}
1599
		$('#btnadvopts').html('<i class="fa fa-cog"></i> ' + text);
1600
	}
1601

    
1602
	$('#btnadvopts').click(function(event) {
1603
		show_advopts();
1604
	});
1605

    
1606
	// ---------- On initial page load ------------------------------------------------------------
1607

    
1608
	show_advdns(true);
1609
	show_advmac(true);
1610
	show_advntp(true);
1611
	show_advtftp(true);
1612
	show_advldap(true);
1613
	show_advopts(true);
1614

    
1615
	// Suppress "Delete row" button if there are fewer than two rows
1616
	checkLastRow();
1617
});
1618
//]]>
1619
</script>
1620

    
1621
<?php include("foot.inc");
(118-118/225)