Project

General

Profile

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

    
64
##|+PRIV
65
##|*IDENT=page-services-dhcpserver
66
##|*NAME=Services: DHCP server page
67
##|*DESCR=Allow access to the 'Services: DHCP server' page.
68
##|*MATCH=services_dhcp.php*
69
##|-PRIV
70

    
71
require("guiconfig.inc");
72
require_once("filter.inc");
73

    
74
if (!$g['services_dhcp_server_enable']) {
75
	header("Location: /");
76
	exit;
77
}
78

    
79
$if = $_GET['if'];
80
if (!empty($_POST['if'])) {
81
	$if = $_POST['if'];
82
}
83

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

    
94
$iflist = get_configured_interface_with_descr();
95

    
96
/* set the starting interface */
97
if (!$if || !isset($iflist[$if])) {
98
	foreach ($iflist as $ifent => $ifname) {
99
		$oc = $config['interfaces'][$ifent];
100
		if ((is_array($config['dhcpd'][$ifent]) && !isset($config['dhcpd'][$ifent]['enable']) && (!is_ipaddrv4($oc['ipaddr']))) ||
101
			(!is_array($config['dhcpd'][$ifent]) && (!is_ipaddrv4($oc['ipaddr'])))) {
102
			continue;
103
		}
104

    
105
		$if = $ifent;
106
		break;
107
	}
108
}
109

    
110
$act = $_GET['act'];
111
if (!empty($_POST['act'])) {
112
	$act = $_POST['act'];
113
}
114

    
115
$a_pools = array();
116

    
117
if (is_array($config['dhcpd'][$if])) {
118
	$pool = $_GET['pool'];
119
	if (is_numeric($_POST['pool'])) {
120
		$pool = $_POST['pool'];
121
	}
122

    
123
	// If we have a pool but no interface name, that's not valid. Redirect away.
124
	if (is_numeric($pool) && empty($if)) {
125
		header("Location: services_dhcp.php");
126
		exit;
127
	}
128

    
129
	if (!is_array($config['dhcpd'][$if]['pool'])) {
130
		$config['dhcpd'][$if]['pool'] = array();
131
	}
132

    
133
	$a_pools = &$config['dhcpd'][$if]['pool'];
134

    
135
	if (is_numeric($pool) && $a_pools[$pool]) {
136
		$dhcpdconf = &$a_pools[$pool];
137
	} elseif ($act == "newpool") {
138
		$dhcpdconf = array();
139
	} else {
140
		$dhcpdconf = &$config['dhcpd'][$if];
141
	}
142
}
143
if (is_array($dhcpdconf)) {
144
	// Global Options
145
	if (!is_numeric($pool) && !($act == "newpool")) {
146
		$pconfig['enable'] = isset($dhcpdconf['enable']);
147
		$pconfig['staticarp'] = isset($dhcpdconf['staticarp']);
148
		// No reason to specify this per-pool, per the dhcpd.conf man page it needs to be in every
149
		//	 pool and should be specified in every pool both nodes share, so we'll treat it as global
150
		$pconfig['failover_peerip'] = $dhcpdconf['failover_peerip'];
151

    
152
		// dhcpleaseinlocaltime is global to all interfaces. So if it is selected on any interface,
153
		// then show it true/checked.
154
		foreach ($config['dhcpd'] as $dhcpdifitem) {
155
			$dhcpleaseinlocaltime = $dhcpdifitem['dhcpleaseinlocaltime'];
156
			if ($dhcpleaseinlocaltime) {
157
				break;
158
			}
159
		}
160

    
161
		$pconfig['dhcpleaseinlocaltime'] = $dhcpleaseinlocaltime;
162

    
163
		if (!is_array($dhcpdconf['staticmap'])) {
164
			$dhcpdconf['staticmap'] = array();
165
		}
166

    
167
		$a_maps = &$dhcpdconf['staticmap'];
168
	} else {
169
		// Options that exist only in pools
170
		$pconfig['descr'] = $dhcpdconf['descr'];
171
	}
172

    
173
	// Options that can be global or per-pool.
174
	if (is_array($dhcpdconf['range'])) {
175
		$pconfig['range_from'] = $dhcpdconf['range']['from'];
176
		$pconfig['range_to'] = $dhcpdconf['range']['to'];
177
	}
178

    
179
	$pconfig['deftime'] = $dhcpdconf['defaultleasetime'];
180
	$pconfig['maxtime'] = $dhcpdconf['maxleasetime'];
181
	$pconfig['gateway'] = $dhcpdconf['gateway'];
182
	$pconfig['domain'] = $dhcpdconf['domain'];
183
	$pconfig['domainsearchlist'] = $dhcpdconf['domainsearchlist'];
184
	list($pconfig['wins1'], $pconfig['wins2']) = $dhcpdconf['winsserver'];
185
	list($pconfig['dns1'], $pconfig['dns2'], $pconfig['dns3'], $pconfig['dns4']) = $dhcpdconf['dnsserver'];
186
	$pconfig['denyunknown'] = isset($dhcpdconf['denyunknown']);
187
	$pconfig['ddnsdomain'] = $dhcpdconf['ddnsdomain'];
188
	$pconfig['ddnsdomainprimary'] = $dhcpdconf['ddnsdomainprimary'];
189
	$pconfig['ddnsdomainkeyname'] = $dhcpdconf['ddnsdomainkeyname'];
190
	$pconfig['ddnsdomainkey'] = $dhcpdconf['ddnsdomainkey'];
191
	$pconfig['ddnsupdate'] = isset($dhcpdconf['ddnsupdate']);
192
	$pconfig['mac_allow'] = $dhcpdconf['mac_allow'];
193
	$pconfig['mac_deny'] = $dhcpdconf['mac_deny'];
194
	list($pconfig['ntp1'], $pconfig['ntp2']) = $dhcpdconf['ntpserver'];
195
	$pconfig['tftp'] = $dhcpdconf['tftp'];
196
	$pconfig['ldap'] = $dhcpdconf['ldap'];
197
	$pconfig['netboot'] = isset($dhcpdconf['netboot']);
198
	$pconfig['nextserver'] = $dhcpdconf['nextserver'];
199
	$pconfig['filename'] = $dhcpdconf['filename'];
200
	$pconfig['filename32'] = $dhcpdconf['filename32'];
201
	$pconfig['filename64'] = $dhcpdconf['filename64'];
202
	$pconfig['rootpath'] = $dhcpdconf['rootpath'];
203
	$pconfig['netmask'] = $dhcpdconf['netmask'];
204
	$pconfig['numberoptions'] = $dhcpdconf['numberoptions'];
205
}
206

    
207
$ifcfgip = $config['interfaces'][$if]['ipaddr'];
208
$ifcfgsn = $config['interfaces'][$if]['subnet'];
209

    
210
function validate_partial_mac_list($maclist) {
211
	$macs = explode(',', $maclist);
212

    
213
	// Loop through and look for invalid MACs.
214
	foreach ($macs as $mac) {
215
		if (!is_macaddr($mac, true)) {
216
			return false;
217
		}
218
	}
219

    
220
	return true;
221
}
222

    
223
if (isset($_POST['submit'])) {
224

    
225
	unset($input_errors);
226

    
227
	$pconfig = $_POST;
228

    
229
	$numberoptions = array();
230
	for ($x = 0; $x < 99; $x++) {
231
		if (isset($_POST["number{$x}"]) && ctype_digit($_POST["number{$x}"])) {
232
			$numbervalue = array();
233
			$numbervalue['number'] = htmlspecialchars($_POST["number{$x}"]);
234
			$numbervalue['type'] = htmlspecialchars($_POST["itemtype{$x}"]);
235
			$numbervalue['value'] = str_replace('&quot;', '"', htmlspecialchars($_POST["value{$x}"]));
236
			$numberoptions['item'][] = $numbervalue;
237
		}
238
	}
239

    
240
	// Reload the new pconfig variable that the form uses.
241
	$pconfig['numberoptions'] = $numberoptions;
242

    
243
	/* input validation */
244
	if ($_POST['enable'] || is_numeric($pool) || $act == "newpool") {
245
		$reqdfields = explode(" ", "range_from range_to");
246
		$reqdfieldsn = array(gettext("Range begin"), gettext("Range end"));
247

    
248
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
249

    
250
		if (($_POST['range_from'] && !is_ipaddrv4($_POST['range_from']))) {
251
			$input_errors[] = gettext("A valid range must be specified.");
252
		}
253
		if (($_POST['range_to'] && !is_ipaddrv4($_POST['range_to']))) {
254
			$input_errors[] = gettext("A valid range must be specified.");
255
		}
256
		if (($_POST['gateway'] && $_POST['gateway'] != "none" && !is_ipaddrv4($_POST['gateway']))) {
257
			$input_errors[] = gettext("A valid IP address must be specified for the gateway.");
258
		}
259
		if (($_POST['wins1'] && !is_ipaddrv4($_POST['wins1'])) || ($_POST['wins2'] && !is_ipaddrv4($_POST['wins2']))) {
260
			$input_errors[] = gettext("A valid IP address must be specified for the primary/secondary WINS servers.");
261
		}
262
		$parent_ip = get_interface_ip($_POST['if']);
263
		if (is_ipaddrv4($parent_ip) && $_POST['gateway'] && $_POST['gateway'] != "none") {
264
			$parent_sn = get_interface_subnet($_POST['if']);
265
			if (!ip_in_subnet($_POST['gateway'], gen_subnet($parent_ip, $parent_sn) . "/" . $parent_sn) && !ip_in_interface_alias_subnet($_POST['if'], $_POST['gateway'])) {
266
				$input_errors[] = sprintf(gettext("The gateway address %s does not lie within the chosen interface's subnet."), $_POST['gateway']);
267
			}
268
		}
269

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

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

    
278
		if (isset($config['captiveportal']) && is_array($config['captiveportal'])) {
279
			$deftime = 7200; // Default value if it's empty
280
			if (is_numeric($_POST['deftime'])) {
281
				$deftime = $_POST['deftime'];
282
			}
283

    
284
			foreach ($config['captiveportal'] as $cpZone => $cpdata) {
285
				if (!isset($cpdata['enable'])) {
286
					continue;
287
				}
288
				if (!isset($cpdata['timeout']) || !is_numeric($cpdata['timeout'])) {
289
					continue;
290
				}
291
				$cp_ifs = explode(',', $cpdata['interface']);
292
				if (!in_array($if, $cp_ifs)) {
293
					continue;
294
				}
295
				if ($cpdata['timeout'] > $deftime) {
296
					$input_errors[] = sprintf(gettext(
297
						"The Captive Portal zone '%s' has Hard Timeout parameter set to a value bigger than Default lease time (%s)."), $cpZone, $deftime);
298
				}
299
			}
300
		}
301

    
302
		if ($_POST['maxtime'] && (!is_numeric($_POST['maxtime']) || ($_POST['maxtime'] < 60) || ($_POST['maxtime'] <= $_POST['deftime']))) {
303
			$input_errors[] = gettext("The maximum lease time must be at least 60 seconds and higher than the default lease time.");
304
		}
305
		if (($_POST['ddnsdomain'] && !is_domain($_POST['ddnsdomain']))) {
306
			$input_errors[] = gettext("A valid domain name must be specified for the dynamic DNS registration.");
307
		}
308
		if (($_POST['ddnsdomain'] && !is_ipaddrv4($_POST['ddnsdomainprimary']))) {
309
			$input_errors[] = gettext("A valid primary domain name server IP address must be specified for the dynamic domain name.");
310
		}
311
		if (($_POST['ddnsdomainkey'] && !$_POST['ddnsdomainkeyname']) ||
312
			($_POST['ddnsdomainkeyname'] && !$_POST['ddnsdomainkey'])) {
313
			$input_errors[] = gettext("You must specify both a valid domain key and key name.");
314
		}
315
		if ($_POST['domainsearchlist']) {
316
			$domain_array = preg_split("/[ ;]+/", $_POST['domainsearchlist']);
317
			foreach ($domain_array as $curdomain) {
318
				if (!is_domain($curdomain)) {
319
					$input_errors[] = gettext("A valid domain search list must be specified.");
320
					break;
321
				}
322
			}
323
		}
324

    
325
		// Validate MACs
326
		if (!empty($_POST['mac_allow']) && !validate_partial_mac_list($_POST['mac_allow'])) {
327
			$input_errors[] = gettext("If you specify a mac allow list, it must contain only valid partial MAC addresses.");
328
		}
329
		if (!empty($_POST['mac_deny']) && !validate_partial_mac_list($_POST['mac_deny'])) {
330
			$input_errors[] = gettext("If you specify a mac deny list, it must contain only valid partial MAC addresses.");
331
		}
332

    
333
		if (($_POST['ntp1'] && !is_ipaddrv4($_POST['ntp1'])) || ($_POST['ntp2'] && !is_ipaddrv4($_POST['ntp2']))) {
334
			$input_errors[] = gettext("A valid IP address must be specified for the primary/secondary NTP servers.");
335
		}
336
		if (($_POST['domain'] && !is_domain($_POST['domain']))) {
337
			$input_errors[] = gettext("A valid domain name must be specified for the DNS domain.");
338
		}
339
		if ($_POST['tftp'] && !is_ipaddrv4($_POST['tftp']) && !is_domain($_POST['tftp']) && !is_URL($_POST['tftp'])) {
340
			$input_errors[] = gettext("A valid IP address or hostname must be specified for the TFTP server.");
341
		}
342
		if (($_POST['nextserver'] && !is_ipaddrv4($_POST['nextserver']))) {
343
			$input_errors[] = gettext("A valid IP address must be specified for the network boot server.");
344
		}
345

    
346
		if (gen_subnet($ifcfgip, $ifcfgsn) == $_POST['range_from']) {
347
			$input_errors[] = gettext("You cannot use the network address in the starting subnet range.");
348
		}
349
		if (gen_subnet_max($ifcfgip, $ifcfgsn) == $_POST['range_to']) {
350
			$input_errors[] = gettext("You cannot use the broadcast address in the ending subnet range.");
351
		}
352

    
353
		// Disallow a range that includes the virtualip
354
		if (is_array($config['virtualip']['vip'])) {
355
			foreach ($config['virtualip']['vip'] as $vip) {
356
				if ($vip['interface'] == $if) {
357
					if ($vip['subnet'] && is_inrange_v4($vip['subnet'], $_POST['range_from'], $_POST['range_to'])) {
358
						$input_errors[] = sprintf(gettext("The subnet range cannot overlap with virtual IP address %s."), $vip['subnet']);
359
					}
360
				}
361
			}
362
		}
363

    
364
		$noip = false;
365
		if (is_array($a_maps)) {
366
			foreach ($a_maps as $map) {
367
				if (empty($map['ipaddr'])) {
368
					$noip = true;
369
				}
370
			}
371
		}
372

    
373
		if ($_POST['staticarp'] && $noip) {
374
			$input_errors[] = "Cannot enable static ARP when you have static map entries without IP addresses. Ensure all static maps have IP addresses and try again.";
375
		}
376

    
377
		if (is_array($pconfig['numberoptions']['item'])) {
378
			foreach ($pconfig['numberoptions']['item'] as $numberoption) {
379
				if ($numberoption['type'] == 'text' && strstr($numberoption['value'], '"')) {
380
					$input_errors[] = gettext("Text type cannot include quotation marks.");
381
				} else if ($numberoption['type'] == 'string' && !preg_match('/^"[^"]*"$/', $numberoption['value']) && !preg_match('/^[0-9a-f]{2}(?:\:[0-9a-f]{2})*$/i', $numberoption['value'])) {
382
					$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");
383
				} else if ($numberoption['type'] == 'boolean' && $numberoption['value'] != 'true' && $numberoption['value'] != 'false' && $numberoption['value'] != 'on' && $numberoption['value'] != 'off') {
384
					$input_errors[] = gettext("Boolean type must be true, false, on, or off.");
385
				} else if ($numberoption['type'] == 'unsigned integer 8' && (!is_numeric($numberoption['value']) || $numberoption['value'] < 0 || $numberoption['value'] > 255)) {
386
					$input_errors[] = gettext("Unsigned 8-bit integer type must be a number in the range 0 to 255.");
387
				} else if ($numberoption['type'] == 'unsigned integer 16' && (!is_numeric($numberoption['value']) || $numberoption['value'] < 0 || $numberoption['value'] > 65535)) {
388
					$input_errors[] = gettext("Unsigned 16-bit integer type must be a number in the range 0 to 65535.");
389
				} else if ($numberoption['type'] == 'unsigned integer 32' && (!is_numeric($numberoption['value']) || $numberoption['value'] < 0 || $numberoption['value'] > 4294967295)) {
390
					$input_errors[] = gettext("Unsigned 32-bit integer type must be a number in the range 0 to 4294967295.");
391
				} else if ($numberoption['type'] == 'signed integer 8' && (!is_numeric($numberoption['value']) || $numberoption['value'] < -128 || $numberoption['value'] > 127)) {
392
					$input_errors[] = gettext("Signed 8-bit integer type must be a number in the range -128 to 127.");
393
				} else if ($numberoption['type'] == 'signed integer 16' && (!is_numeric($numberoption['value']) || $numberoption['value'] < -32768 || $numberoption['value'] > 32767)) {
394
					$input_errors[] = gettext("Signed 16-bit integer type must be a number in the range -32768 to 32767.");
395
				} else if ($numberoption['type'] == 'signed integer 32' && (!is_numeric($numberoption['value']) || $numberoption['value'] < -2147483648 || $numberoption['value'] > 2147483647)) {
396
					$input_errors[] = gettext("Signed 32-bit integer type must be a number in the range -2147483648 to 2147483647.");
397
				} else if ($numberoption['type'] == 'ip-address' && !is_ipaddrv4($numberoption['value']) && !is_hostname($numberoption['value'])) {
398
					$input_errors[] = gettext("IP address or host type must be an IP address or host name.");
399
				}
400
			}
401
		}
402

    
403
		if (!$input_errors) {
404
			/* make sure the range lies within the current subnet */
405
			$subnet_start = ip2ulong(long2ip32(ip2long($ifcfgip) & gen_subnet_mask_long($ifcfgsn)));
406
			$subnet_end = ip2ulong(long2ip32(ip2long($ifcfgip) | (~gen_subnet_mask_long($ifcfgsn))));
407

    
408
			if ((ip2ulong($_POST['range_from']) < $subnet_start) || (ip2ulong($_POST['range_from']) > $subnet_end) ||
409
				(ip2ulong($_POST['range_to']) < $subnet_start) || (ip2ulong($_POST['range_to']) > $subnet_end)) {
410
				$input_errors[] = gettext("The specified range lies outside of the current subnet.");
411
			}
412

    
413
			if (ip2ulong($_POST['range_from']) > ip2ulong($_POST['range_to'])) {
414
				$input_errors[] = gettext("The range is invalid (first element higher than second element).");
415
			}
416

    
417
			if (is_numeric($pool) || ($act == "newpool")) {
418
				$rfrom = $config['dhcpd'][$if]['range']['from'];
419
				$rto = $config['dhcpd'][$if]['range']['to'];
420

    
421
				if (is_inrange_v4($_POST['range_from'], $rfrom, $rto) || is_inrange_v4($_POST['range_to'], $rfrom, $rto)) {
422
					$input_errors[] = gettext("The specified range must not be within the DHCP range for this interface.");
423
				}
424
			}
425

    
426
			foreach ($a_pools as $id => $p) {
427
				if (is_numeric($pool) && ($id == $pool)) {
428
					continue;
429
				}
430

    
431
				if (is_inrange_v4($_POST['range_from'], $p['range']['from'], $p['range']['to']) ||
432
					is_inrange_v4($_POST['range_to'], $p['range']['from'], $p['range']['to'])) {
433
					$input_errors[] = gettext("The specified range must not be within the range configured on a DHCP pool for this interface.");
434
					break;
435
				}
436
			}
437

    
438
			/* make sure that the DHCP Relay isn't enabled on this interface */
439
			if (isset($config['dhcrelay']['enable']) && (stristr($config['dhcrelay']['interface'], $if) !== false)) {
440
				$input_errors[] = sprintf(gettext("You must disable the DHCP relay on the %s interface before enabling the DHCP server."), $iflist[$if]);
441
			}
442

    
443
			$dynsubnet_start = ip2ulong($_POST['range_from']);
444
			$dynsubnet_end = ip2ulong($_POST['range_to']);
445
			if (is_array($a_maps)) {
446
				foreach ($a_maps as $map) {
447
					if (empty($map['ipaddr'])) {
448
						continue;
449
					}
450
					if ((ip2ulong($map['ipaddr']) > $dynsubnet_start) &&
451
						(ip2ulong($map['ipaddr']) < $dynsubnet_end)) {
452
						$input_errors[] = sprintf(gettext("The DHCP range cannot overlap any static DHCP mappings."));
453
						break;
454
					}
455
				}
456
			}
457
		}
458
	}
459

    
460
	if (!$input_errors) {
461
		if (!is_numeric($pool)) {
462
			if ($act == "newpool") {
463
				$dhcpdconf = array();
464
			} else {
465
				if (!is_array($config['dhcpd'][$if])) {
466
					$config['dhcpd'][$if] = array();
467
				}
468
				$dhcpdconf = $config['dhcpd'][$if];
469
			}
470
		} else {
471
			if (is_array($a_pools[$pool])) {
472
				$dhcpdconf = $a_pools[$pool];
473
			} else {
474
				// Someone specified a pool but it doesn't exist. Punt.
475
				header("Location: services_dhcp.php");
476
				exit;
477
			}
478
		}
479
		if (!is_array($dhcpdconf['range'])) {
480
			$dhcpdconf['range'] = array();
481
		}
482

    
483
		$dhcpd_enable_changed = false;
484

    
485
		// Global Options
486
		if (!is_numeric($pool) && !($act == "newpool")) {
487
			$old_dhcpd_enable = isset($dhcpdconf['enable']);
488
			$new_dhcpd_enable = ($_POST['enable']) ? true : false;
489
			if ($old_dhcpd_enable != $new_dhcpd_enable) {
490
				/* DHCP has been enabled or disabled. The pf ruleset will need to be rebuilt to allow or disallow DHCP. */
491
				$dhcpd_enable_changed = true;
492
			}
493

    
494
			$dhcpdconf['enable'] = $new_dhcpd_enable;
495
			$dhcpdconf['staticarp'] = ($_POST['staticarp']) ? true : false;
496
			$previous = $dhcpdconf['failover_peerip'];
497
			if ($previous != $_POST['failover_peerip']) {
498
				mwexec("/bin/rm -rf /var/dhcpd/var/db/*");
499
			}
500

    
501
			$dhcpdconf['failover_peerip'] = $_POST['failover_peerip'];
502
			// dhcpleaseinlocaltime is global to all interfaces. So update the setting on all interfaces.
503
			foreach ($config['dhcpd'] as &$dhcpdifitem) {
504
				$dhcpdifitem['dhcpleaseinlocaltime'] = $_POST['dhcpleaseinlocaltime'];
505
			}
506
		} else {
507
			// Options that exist only in pools
508
			$dhcpdconf['descr'] = $_POST['descr'];
509
		}
510

    
511
		// Options that can be global or per-pool.
512
		$dhcpdconf['range']['from'] = $_POST['range_from'];
513
		$dhcpdconf['range']['to'] = $_POST['range_to'];
514
		$dhcpdconf['defaultleasetime'] = $_POST['deftime'];
515
		$dhcpdconf['maxleasetime'] = $_POST['maxtime'];
516
		$dhcpdconf['netmask'] = $_POST['netmask'];
517

    
518
		unset($dhcpdconf['winsserver']);
519
		if ($_POST['wins1']) {
520
			$dhcpdconf['winsserver'][] = $_POST['wins1'];
521
		}
522
		if ($_POST['wins2']) {
523
			$dhcpdconf['winsserver'][] = $_POST['wins2'];
524
		}
525

    
526
		unset($dhcpdconf['dnsserver']);
527
		if ($_POST['dns1']) {
528
			$dhcpdconf['dnsserver'][] = $_POST['dns1'];
529
		}
530
		if ($_POST['dns2']) {
531
			$dhcpdconf['dnsserver'][] = $_POST['dns2'];
532
		}
533
		if ($_POST['dns3']) {
534
			$dhcpdconf['dnsserver'][] = $_POST['dns3'];
535
		}
536
		if ($_POST['dns4']) {
537
			$dhcpdconf['dnsserver'][] = $_POST['dns4'];
538
		}
539

    
540
		$dhcpdconf['gateway'] = $_POST['gateway'];
541
		$dhcpdconf['domain'] = $_POST['domain'];
542
		$dhcpdconf['domainsearchlist'] = $_POST['domainsearchlist'];
543
		$dhcpdconf['denyunknown'] = ($_POST['denyunknown']) ? true : false;
544
		$dhcpdconf['ddnsdomain'] = $_POST['ddnsdomain'];
545
		$dhcpdconf['ddnsdomainprimary'] = $_POST['ddnsdomainprimary'];
546
		$dhcpdconf['ddnsdomainkeyname'] = $_POST['ddnsdomainkeyname'];
547
		$dhcpdconf['ddnsdomainkey'] = $_POST['ddnsdomainkey'];
548
		$dhcpdconf['ddnsupdate'] = ($_POST['ddnsupdate']) ? true : false;
549
		$dhcpdconf['mac_allow'] = $_POST['mac_allow'];
550
		$dhcpdconf['mac_deny'] = $_POST['mac_deny'];
551

    
552
		unset($dhcpdconf['ntpserver']);
553
		if ($_POST['ntp1']) {
554
			$dhcpdconf['ntpserver'][] = $_POST['ntp1'];
555
		}
556
		if ($_POST['ntp2']) {
557
			$dhcpdconf['ntpserver'][] = $_POST['ntp2'];
558
		}
559

    
560
		$dhcpdconf['tftp'] = $_POST['tftp'];
561
		$dhcpdconf['ldap'] = $_POST['ldap'];
562
		$dhcpdconf['netboot'] = ($_POST['netboot']) ? true : false;
563
		$dhcpdconf['nextserver'] = $_POST['nextserver'];
564
		$dhcpdconf['filename'] = $_POST['filename'];
565
		$dhcpdconf['filename32'] = $_POST['filename32'];
566
		$dhcpdconf['filename64'] = $_POST['filename64'];
567
		$dhcpdconf['rootpath'] = $_POST['rootpath'];
568

    
569
		// Handle the custom options rowhelper
570
		if (isset($dhcpdconf['numberoptions']['item'])) {
571
			unset($dhcpdconf['numberoptions']['item']);
572
		}
573

    
574
		$dhcpdconf['numberoptions'] = $numberoptions;
575

    
576
		if (is_numeric($pool) && is_array($a_pools[$pool])) {
577
			$a_pools[$pool] = $dhcpdconf;
578
		} elseif ($act == "newpool") {
579
			$a_pools[] = $dhcpdconf;
580
		} else {
581
			$config['dhcpd'][$if] = $dhcpdconf;
582
		}
583

    
584
		write_config();
585
	}
586
}
587

    
588
if ((isset($_POST['submit']) || isset($_POST['apply'])) && (!$input_errors)) {
589
	$retval = 0;
590
	$retvaldhcp = 0;
591
	$retvaldns = 0;
592
	/* dnsmasq_configure calls dhcpd_configure */
593
	/* no need to restart dhcpd twice */
594
	if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic']))	{
595
		$retvaldns = services_dnsmasq_configure();
596
		if ($retvaldns == 0) {
597
			clear_subsystem_dirty('hosts');
598
			clear_subsystem_dirty('staticmaps');
599
		}
600
	} else if (isset($config['unbound']['enable']) && isset($config['unbound']['regdhcpstatic'])) {
601
		$retvaldns = services_unbound_configure();
602
		if ($retvaldns == 0) {
603
			clear_subsystem_dirty('unbound');
604
			clear_subsystem_dirty('hosts');
605
			clear_subsystem_dirty('staticmaps');
606
		}
607
	} else {
608
		$retvaldhcp = services_dhcpd_configure();
609
		if ($retvaldhcp == 0) {
610
			clear_subsystem_dirty('staticmaps');
611
		}
612
	}
613
	if ($dhcpd_enable_changed) {
614
		$retvalfc = filter_configure();
615
	}
616

    
617
	if ($retvaldhcp == 1 || $retvaldns == 1 || $retvalfc == 1) {
618
		$retval = 1;
619
	}
620

    
621
	$savemsg = get_std_save_message($retval);
622
}
623

    
624
if ($act == "delpool") {
625
	if ($a_pools[$_GET['id']]) {
626
		unset($a_pools[$_GET['id']]);
627
		write_config();
628
		header("Location: services_dhcp.php?if={$if}");
629
		exit;
630
	}
631
}
632

    
633
if ($act == "del") {
634
	if ($a_maps[$_GET['id']]) {
635
		unset($a_maps[$_GET['id']]);
636
		write_config();
637
		if (isset($config['dhcpd'][$if]['enable'])) {
638
			mark_subsystem_dirty('staticmaps');
639
			if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic'])) {
640
				mark_subsystem_dirty('hosts');
641
			}
642
		}
643

    
644
		header("Location: services_dhcp.php?if={$if}");
645
		exit;
646
	}
647
}
648

    
649
// Build an HTML table that can be inserted into a Form_StaticText element
650
function build_pooltable() {
651
	global $a_pools;
652

    
653
	$pooltbl =	'<div class="table-responsive">';
654
	$pooltbl .=		'<table class="table table-striped table-hover table-condensed">';
655
	$pooltbl .=			'<thead>';
656
	$pooltbl .=				'<tr>';
657
	$pooltbl .=					'<th>' . gettext("Pool Start") . '</th>';
658
	$pooltbl .=					'<th>' . gettext("Pool End") . '</th>';
659
	$pooltbl .=					'<th>' . gettext("Description") . '</th>';
660
	$pooltbl .=					'<th></th>';
661
	$pooltbl .=				'</tr>';
662
	$pooltbl .=			'</thead>';
663
	$pooltbl .=			'<tbody>';
664

    
665
	if (is_array($a_pools)) {
666
		$i = 0;
667
		foreach ($a_pools as $poolent) {
668
			if (!empty($poolent['range']['from']) && !empty($poolent['range']['to'])) {
669
				$pooltbl .= '<tr>';
670
				$pooltbl .= '<td ondblclick="document.location=\'services_dhcp.php?if=' . htmlspecialchars($if) . '&pool=' . $i . '\';">' .
671
							htmlspecialchars($poolent['range']['from']) . '</td>';
672

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

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

    
679
				$pooltbl .= '<td><a class="btn btn-xs btn-info" href="services_dhcp.php?if=' . htmlspecialchars($if) . '&pool=' . $i . '" />' . gettext('Edit') . '</a>';
680

    
681
				$pooltbl .= '<a class="btn btn-xs btn-danger" href="services_dhcp.php?if=' . htmlspecialchars($if) . '&act=delpool&id=' . $i . '" />' . gettext('Delete') . '</a></td>';
682
				$pooltbl .= '</tr>';
683
			}
684
		$i++;
685
		}
686
	}
687

    
688
	$pooltbl .=			'</tbody>';
689
	$pooltbl .=		'</table>';
690
	$pooltbl .= '</div>';
691

    
692
	return($pooltbl);
693
}
694

    
695
$closehead = false;
696
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
697
$shortcut_section = "dhcp";
698

    
699
include("head.inc");
700

    
701
if ($input_errors)
702
	print_input_errors($input_errors);
703

    
704
if ($savemsg)
705
	print_info_box($savemsg, 'success');
706

    
707
if (isset($config['dhcrelay']['enable'])) {
708
	print_info_box(gettext("DHCP Relay is currently enabled. Cannot enable the DHCP Server service while the DHCP Relay is enabled on any interface."));
709
	include("foot.inc");
710
	exit;
711
}
712

    
713
if (is_subsystem_dirty('staticmaps'))
714
	print_info_box_np(gettext("The static mapping configuration has been changed") . ".<br />" . gettext("You must apply the changes in order for them to take effect."));
715

    
716
/* active tabs */
717
$tab_array = array();
718
$tabscounter = 0;
719
$i = 0;
720

    
721
foreach ($iflist as $ifent => $ifname) {
722
	$oc = $config['interfaces'][$ifent];
723
	if ((is_array($config['dhcpd'][$ifent]) && !isset($config['dhcpd'][$ifent]['enable']) && (!is_ipaddrv4($oc['ipaddr']))) ||
724
		(!is_array($config['dhcpd'][$ifent]) && (!is_ipaddrv4($oc['ipaddr'])))) {
725
		continue;
726
	}
727

    
728
	if ($ifent == $if) {
729
		$active = true;
730
	} else {
731
		$active = false;
732
	}
733

    
734
	$tab_array[] = array($ifname, $active, "services_dhcp.php?if={$ifent}");
735
	$tabscounter++;
736
}
737

    
738
if ($tabscounter == 0) {
739
	print_info_box(gettext("The DHCP Server can only be enabled on interfaces configured with a static IPv4 address. This system has none."));
740
	include("foot.inc");
741
	exit;
742
}
743

    
744
display_top_tabs($tab_array);
745

    
746
require_once('classes/Form.class.php');
747

    
748
// This form uses a non-standard submit button name
749
$form = new Form(new Form_Button(
750
	'submit',
751
	gettext("Save")
752
));
753

    
754
$section = new Form_Section('General Options');
755

    
756
if (!is_numeric($pool) && !($act == "newpool")) {
757
	$section->addInput(new Form_Checkbox(
758
		'enable',
759
		'Enable',
760
		sprintf(gettext("Enable DHCP server on %s interface"), htmlspecialchars($iflist[$if])),
761
		$pconfig['enable']
762
	));
763
} else {
764
	$section->addInput(new Form_StaticText(
765
		null,
766
		'<div class="alert alert-info"> Editing Pool-Specific Options. To return to the Interface, click its tab above. </div>'
767
	));
768
}
769

    
770
$section->addInput(new Form_Checkbox(
771
	'denyunknown',
772
	'Deny unknown clients',
773
	'Only the clients defined below will get DHCP leases from this server.',
774
	$pconfig['denyunknown']
775
));
776

    
777
if (is_numeric($pool) || ($act == "newpool")) {
778
	$section->addInput(new Form_Input(
779
		'descr',
780
		'Pool Description',
781
		'text',
782
		$pconfig['descr']
783
	));
784
}
785

    
786
$section->addInput(new Form_StaticText(
787
	'Subnet',
788
	gen_subnet($ifcfgip, $ifcfgsn)
789
));
790

    
791
$section->addInput(new Form_StaticText(
792
	'Subnet mask',
793
	gen_subnet_mask($ifcfgsn)
794
));
795

    
796
// Compose a string to display the required address ranges
797
$range_from = ip2long(long2ip32(ip2long($ifcfgip) & gen_subnet_mask_long($ifcfgsn)));
798
$range_from++;
799

    
800
$range_to = ip2long(long2ip32(ip2long($ifcfgip) | (~gen_subnet_mask_long($ifcfgsn))));
801
$range_to--;
802

    
803
$rangestr = long2ip32($range_from) . ' - ' . long2ip32($range_to);
804

    
805
if (is_numeric($pool) || ($act == "newpool")) {
806
	$rangestr .= '<br />' . 'In-use DHCP Pool Ranges:';
807
	if (is_array($config['dhcpd'][$if]['range'])) {
808
		$rangestr .= '<br />' . $config['dhcpd'][$if]['range']['from'] . ' - ' . $config['dhcpd'][$if]['range']['to'];
809
	}
810

    
811
	foreach ($a_pools as $p) {
812
		if (is_array($p['range'])) {
813
			$rangestr .= '<br />' . $p['range']['from'] . ' - ' . $p['range']['to'];
814
		}
815
	}
816
}
817

    
818
$section->addInput(new Form_StaticText(
819
	'Available range',
820
	$rangestr
821
));
822

    
823
if ($is_olsr_enabled) {
824
	$section->addInput(new Form_Select(
825
		'netmask',
826
		'Subnet mask',
827
		$pconfig['netmask'],
828
		array_combine(range(32, 1, -1), range(32, 1, -1))
829
	));
830
}
831

    
832
$group = new Form_Group('Range');
833

    
834
$group->add(new Form_IpAddress(
835
	'range_from',
836
	null,
837
	$pconfig['range_from']
838
))->setHelp('From');
839

    
840
$group->add(new Form_IpAddress(
841
	'range_to',
842
	null,
843
	$pconfig['range_to']
844
))->setHelp('To');
845

    
846
$section->add($group);
847

    
848
$form->add($section);
849

    
850
if (!is_numeric($pool) && !($act == "newpool")) {
851
	$section = new Form_Section('Additional pools');
852

    
853
	$btnaddpool = new Form_Button(
854
		'btnaddpool',
855
		'Add pool',
856
		'services_dhcp.php?if=' . htmlspecialchars($if) . '&act=newpool'
857
	);
858

    
859
	$section->addInput(new Form_StaticText(
860
		'Add',
861
		$btnaddpool
862
	))->setHelp('If you need additional pools of addresses inside of this subnet outside the above Range, they may be specified here');
863

    
864
	if (is_array($a_pools)) {
865
		$section->addInput(new Form_StaticText(
866
			null,
867
			build_pooltable()
868
		));
869
	}
870

    
871
	$form->add($section);
872
}
873

    
874
$section = new Form_Section('Servers');
875

    
876
$section->addInput(new Form_IpAddress(
877
	'wins1',
878
	'WINS servers',
879
	$pconfig['wins1']
880
))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'WINS Server 1');
881

    
882
$section->addInput(new Form_IpAddress(
883
	'wins2',
884
	null,
885
	$pconfig['wins2']
886
))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'WINS Server 2');
887

    
888
for($idx=1; $idx<=4; $idx++) {
889
	$section->addInput(new Form_IpAddress(
890
		'dns' . $idx,
891
		($idx == 1) ? 'DNS servers':null,
892
		$pconfig['dns' . $idx]
893
	))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'DNS Server ' . $idx)->setHelp(($idx == 4) ? 'Leave blank to use the system default DNS servers, use this interface\'s IP if DNS Forwarder or Resolver is enabled, otherwise use the servers configured on the General page':'');
894
}
895

    
896
$form->add($section);
897

    
898
$section = new Form_Section('Other options');
899

    
900
$section->addInput(new Form_IpAddress(
901
	'gateway',
902
	'Gateway',
903
	$pconfig['gateway']
904
))->setPattern('[.a-zA-Z0-9_]+')
905
  ->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 your network. Type "none" for no gateway assignment');
906

    
907
$section->addInput(new Form_Input(
908
	'domain',
909
	'Domain name',
910
	'text',
911
	$pconfig['domain']
912
))->setHelp('The default is to use the domain name of this system as the default domain name provided by DHCP. You may specify an alternate domain name here');
913

    
914
$section->addInput(new Form_Input(
915
	'domainsearchlist',
916
	'Domain search list',
917
	'text',
918
	$pconfig['domainsearchlist']
919
))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator');
920

    
921
$section->addInput(new Form_Input(
922
	'deftime',
923
	'Default lease time',
924
	'number',
925
	$pconfig['deftime']
926
))->setHelp('This is used for clients that do not ask for a specific expiration time. The default is 7200 seconds');
927

    
928
$section->addInput(new Form_Input(
929
	'maxtime',
930
	'Maximum lease time',
931
	'number',
932
	$pconfig['maxtime']
933
))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds');
934

    
935
if (!is_numeric($pool) && !($act == "newpool")) {
936
	$section->addInput(new Form_IpAddress(
937
		'failover_peerip',
938
		'Failover peer IP',
939
		$pconfig['failover_peerip']
940
	))->setHelp('Leave blank to disable. Enter the interface IP address of the other machine. Machines must be using CARP.' .
941
				'Interface\'s advskew determines whether the DHCPd process is Primary or Secondary. Ensure one machine\'s advskew < 20 (and the other is > 20).');
942
}
943

    
944
if (!is_numeric($pool) && !($act == "newpool")) {
945
	$section->addInput(new Form_Checkbox(
946
		'staticarp',
947
		'Static ARP',
948
		'Enable Static ARP entries',
949
		$pconfig['staticarp']
950
	))->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 NIC.');
951

    
952
	$section->addInput(new Form_Checkbox(
953
		'dhcpleaseinlocaltime',
954
		'Time format change',
955
		'Change DHCP display lease time from UTC to local time',
956
		$pconfig['dhcpleaseinlocaltime']
957
	))->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.' .
958
				' This will be used for all DHCP interfaces lease time');
959
}
960

    
961
// DDNS
962
$btnadv = new Form_Button(
963
	'btnadvdns',
964
	'Advanced'
965
);
966

    
967
$btnadv->removeClass('btn-primary')->addClass('btn-default btn-sm');
968

    
969
$section->addInput(new Form_StaticText(
970
	'Dynamic DNS',
971
	$btnadv
972
));
973

    
974
$section->addInput(new Form_Checkbox(
975
	'ddnsupdate',
976
	null,
977
	'Enable registration of DHCP client names in DNS',
978
	$pconfig['ddnsupdate']
979
));
980

    
981
$section->addInput(new Form_Input(
982
	'ddnsdomain',
983
	'DDNS Domain',
984
	'number',
985
	$pconfig['ddnsdomain']
986
))->setHelp('Leave blank to disable dynamic DNS registration.' . '<br />' .
987
			'Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
988

    
989
$section->addInput(new Form_IpAddress(
990
	'ddnsdomainprimary',
991
	'Primary DDNS address',
992
	$pconfig['ddnsdomainprimary']
993
))->setHelp('Primary domain name server IP address for the dynamic domain name');
994

    
995
$section->addInput(new Form_Input(
996
	'ddnsdomainkeyname',
997
	'DNS Domain key',
998
	'text',
999
	$pconfig['ddnsdomainkeyname']
1000
))->setHelp('Dynamic DNS domain key name which will be used to register client names in the DNS server');
1001

    
1002
$section->addInput(new Form_Input(
1003
	'ddnsdomainkey',
1004
	'DNS Domain key secret',
1005
	'text',
1006
	$pconfig['ddnsdomainkey']
1007
))->setHelp('Dynamic DNS domain key secret which will be used to register client names in the DNS server');
1008

    
1009
// Advanced MAC
1010
$btnadv = new Form_Button(
1011
	'btnadvmac',
1012
	'Advanced'
1013
);
1014

    
1015
$btnadv->removeClass('btn-primary')->addClass('btn-default btn-sm');
1016

    
1017
$section->addInput(new Form_StaticText(
1018
	'MAC address control',
1019
	$btnadv
1020
));
1021

    
1022
$section->addInput(new Form_Input(
1023
	'mac_allow',
1024
	'Allow',
1025
	'text',
1026
	$pconfig['mac_allow']
1027
))->setHelp('List of partial MAC addresses to allow, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1028

    
1029
$section->addInput(new Form_Input(
1030
	'mac_deny',
1031
	'Deny',
1032
	'text',
1033
	$pconfig['mac_deny']
1034
))->setHelp('List of partial MAC addresses to deny access, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1035

    
1036
// Advanced NTP
1037
$btnadv = new Form_Button(
1038
	'btnadvntp',
1039
	'Advanced'
1040
);
1041

    
1042
$btnadv->removeClass('btn-primary')->addClass('btn-default btn-sm');
1043

    
1044
$section->addInput(new Form_StaticText(
1045
	'NTP servers',
1046
	$btnadv
1047
));
1048

    
1049
$section->addInput(new Form_IpAddress(
1050
	'ntp1',
1051
	null,
1052
	$pconfig['ntp1']
1053
))->setAttribute('placeholder', 'NTP Server 1');
1054

    
1055
$section->addInput(new Form_IpAddress(
1056
	'ntp2',
1057
	null,
1058
	$pconfig['ntp2']
1059
))->setAttribute('placeholder', 'NTP Server 2');
1060

    
1061
// Advanced TFTP
1062
$btnadv = new Form_Button(
1063
	'btnadvtftp',
1064
	'Advanced'
1065
);
1066

    
1067
$btnadv->removeClass('btn-primary')->addClass('btn-default btn-sm');
1068

    
1069
$section->addInput(new Form_StaticText(
1070
	'TFTP server',
1071
	$btnadv
1072
));
1073

    
1074
$section->addInput(new Form_IpAddress(
1075
	'tftp',
1076
	null,
1077
	$pconfig['tftp']
1078
))->setHelp('Leave blank to disable.  Enter a full hostname or IP for the TFTP server')->setPattern('[.a-zA-Z0-9_]+');
1079

    
1080
// Advanced LDAP
1081
$btnadv = new Form_Button(
1082
	'btnadvldap',
1083
	'Advanced'
1084
);
1085

    
1086
$btnadv->removeClass('btn-primary')->addClass('btn-default btn-sm');
1087

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

    
1093
$section->addInput(new Form_Input(
1094
	'ldap',
1095
	null,
1096
	'text',
1097
	$pconfig['ldap']
1098
))->setHelp('Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com ');
1099

    
1100
// Advanced NETBOOT
1101
$btnadv = new Form_Button(
1102
	'btnadvboot',
1103
	'Advanced'
1104
);
1105

    
1106
$btnadv->removeClass('btn-primary')->addClass('btn-default btn-sm');
1107

    
1108
$section->addInput(new Form_StaticText(
1109
	'Network booting',
1110
	$btnadv
1111
));
1112

    
1113
$section->addInput(new Form_Checkbox(
1114
	'netboot',
1115
	null,
1116
	'Enables network booting',
1117
	$pconfig['netboot']
1118
));
1119

    
1120
$section->addInput(new Form_IpAddress(
1121
	'nextserver',
1122
	'Next Server',
1123
	$pconfig['nextserver']
1124
))->setHelp('Enter the IP address of the next server');
1125

    
1126
$section->addInput(new Form_Input(
1127
	'filename',
1128
	'Default BIOS file name',
1129
	'text',
1130
	$pconfig['filename']
1131
));
1132

    
1133
$section->addInput(new Form_Input(
1134
	'filename32',
1135
	'UEFI 32 bit file name',
1136
	'text',
1137
	$pconfig['filename32']
1138
));
1139

    
1140
$section->addInput(new Form_Input(
1141
	'filename64',
1142
	'UEFI 64 bit file name',
1143
	'text',
1144
	$pconfig['filename64']
1145
))->setHelp('You need both a filename and a boot server configured for this to work! ' .
1146
			'You will need all three filenames and a boot server configured for UEFI to work! ');
1147

    
1148
$section->addInput(new Form_Input(
1149
	'rootpath',
1150
	'Root path',
1151
	'text',
1152
	$pconfig['rootpath']
1153
))->setHelp('string-format: iscsi:(servername):(protocol):(port):(LUN):targetname ');
1154

    
1155
// Advanced Additional options
1156
$btnadv = new Form_Button(
1157
	'btnadvopts',
1158
	'Advanced'
1159
);
1160

    
1161
$btnadv->removeClass('btn-primary')->addClass('btn-default btn-sm');
1162

    
1163
$section->addInput(new Form_StaticText(
1164
	'Additional BOOTP/DHCP Options',
1165
	$btnadv
1166
));
1167

    
1168
$form->add($section);
1169

    
1170
$section = new Form_Section('Additional BOOTP/DHCP Options');
1171
$section->addClass('adnlopts');
1172

    
1173
$section->addInput(new Form_StaticText(
1174
	null,
1175
	'<div class="alert alert-info"> ' . gettext('Enter the DHCP option number and the value for each item you would like to include in the DHCP lease information. ' .
1176
	'For a list of available options please visit this ') . '<a href="http://www.iana.org/assignments/bootp-dhcp-parameters/" target="_blank">' . gettext("URL") . '</a></div>'
1177
));
1178

    
1179
if(!$pconfig['numberoptions']) {
1180
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1181
}
1182

    
1183
$customitemtypes = array(
1184
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1185
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1186
	'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')
1187
);
1188

    
1189
$numrows = count($item) -1;
1190
$counter = 0;
1191

    
1192
$numrows = count($pconfig['numberoptions']['item']) -1;
1193

    
1194
foreach ($pconfig['numberoptions']['item'] as $item) {
1195
	$number = $item['number'];
1196
	$itemtype = $item['type'];
1197
	$value = $item['value'];
1198

    
1199
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1200
	$group->addClass('repeatable');
1201

    
1202
	$group->add(new Form_Input(
1203
		'number' . $counter,
1204
		null,
1205
		'text',
1206
		$number
1207
	))->setHelp($numrows == $counter ? 'Number':null);
1208

    
1209

    
1210
	$group->add(new Form_Select(
1211
		'itemtype' . $counter,
1212
		null,
1213
		$itemtype,
1214
		$customitemtypes
1215
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1216

    
1217
	$group->add(new Form_Input(
1218
		'value' . $counter,
1219
		null,
1220
		'text',
1221
		$value
1222
	))->setHelp($numrows == $counter ? 'Value':null);
1223

    
1224
	$group->add(new Form_Button(
1225
		'deleterow' . $counter,
1226
		'Delete'
1227
	))->removeClass('btn-primary')->addClass('btn-warning');
1228

    
1229
	$section->add($group);
1230

    
1231
	$counter++;
1232
}
1233

    
1234
$section->addInput(new Form_Button(
1235
	'addrow',
1236
	'Add'
1237
))->removeClass('btn-primary')->addClass('btn-success');
1238

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

    
1241
if ($act == "newpool") {
1242
	$form->addGlobal(new Form_Input(
1243
		'act',
1244
		null,
1245
		'hidden',
1246
		'newpool'
1247
	));
1248
}
1249

    
1250
if (is_numeric($pool)) {
1251
	$form->addGlobal(new Form_Input(
1252
		'pool',
1253
		null,
1254
		'hidden',
1255
		$pool
1256
	));
1257
}
1258

    
1259
$form->addGlobal(new Form_Input(
1260
	'if',
1261
	null,
1262
	'hidden',
1263
	$if
1264
));
1265

    
1266
print($form);
1267

    
1268
// DHCP Static Mappings table
1269

    
1270
if (!is_numeric($pool) && !($act == "newpool")) {
1271
?>
1272

    
1273
<div class="panel panel-default">
1274
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("DHCP Static Mappings for this interface.")?></h2></div>
1275
	<div class="table-responsive">
1276
			<table class="table table-striped table-hover table-condensed">
1277
				<thead>
1278
					<tr>
1279
						<th><?=gettext("Static ARP")?></th>
1280
						<th><?=gettext("MAC address")?></th>
1281
						<th><?=gettext("IP address")?></th>
1282
						<th><?=gettext("Hostname")?></th>
1283
						<th><?=gettext("Description")?></th>
1284
						<th></th>
1285
					</tr>
1286
				</thead>
1287
<?php
1288
	if (is_array($a_maps)) {
1289
		$i = 0;
1290
?>
1291
				<tbody>
1292
<?php
1293
		foreach ($a_maps as $mapent) {
1294
?>
1295
					<tr>
1296
						<td align="center" ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1297
							<?php if (isset($mapent['arp_table_static_entry'])): ?>
1298
								<i class="icon icon-ok"></i>
1299
							<?php endif; ?>
1300
						</td>
1301
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1302
							<?=htmlspecialchars($mapent['mac'])?>
1303
						</td>
1304
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1305
							<?=htmlspecialchars($mapent['ipaddr'])?>
1306
						</td>
1307
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1308
							<?=htmlspecialchars($mapent['hostname'])?>
1309
						</td>
1310
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1311
							<?=htmlspecialchars($mapent['descr'])?>
1312
						</td>
1313
						<td>
1314
							<a class="fa fa-pencil"	title="<?=gettext('Edit static mapping')?>"	href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>"></a>
1315
							<a class="fa fa-trash"	title="<?=gettext('Delete static mapping')?>"	href="services_dhcp.php?if=<?=htmlspecialchars($if)?>&amp;act=del&amp;id=<?=$i?>"></a>
1316
						</td>
1317
					</tr>
1318
<?php
1319
		$i++;
1320
		}
1321
?>
1322
				</tbody>
1323
<?php
1324
	}
1325
?>
1326
		<table>
1327
	</div>
1328
</div>
1329

    
1330
<nav class="action-buttons">
1331
	<a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>" class="btn btn-sm btn-success">
1332
		<i class="fa fa-plus icon-embed-btn"></i>
1333
		<?=gettext("Add")?>
1334
	</a>
1335
</nav>
1336
<?php
1337
}
1338
?>
1339

    
1340
<script>
1341
//<![CDATA[
1342
events.push(function(){
1343

    
1344
	// Show advanced DNS options ======================================================================================
1345
	var showadvdns = false;
1346

    
1347
	function show_advdns() {
1348
<?php
1349
		if (!$pconfig['ddnsupdate'] && empty($pconfig['ddnsdomain']) && empty($pconfig['ddnsdomainprimary']) &&
1350
		    empty($pconfig['ddnsdomainkeyname']) && empty($pconfig['ddnsdomainkey']))
1351
			$hide = false;
1352
		else
1353
			$hide = true;
1354
?>
1355
		var hide = <?php if($hide) {echo 'true';} else {echo 'false';} ?>;
1356

    
1357
		hideCheckbox('ddnsupdate', !showadvdns && !hide);
1358
		hideInput('ddnsdomain', !showadvdns && !hide);
1359
		hideInput('ddnsdomainprimary', !showadvdns && !hide);
1360
		hideInput('ddnsdomainkeyname', !showadvdns && !hide);
1361
		hideInput('ddnsdomainkey', !showadvdns && !hide);
1362
		hideInput('btnadvdns', hide);
1363
		showadvdns = !showadvdns;
1364
	}
1365

    
1366
	$('#btnadvdns').prop('type', 'button');
1367

    
1368
	$('#btnadvdns').click(function(event) {
1369
		show_advdns();
1370
	});
1371

    
1372
 // Show advanced MAC options ======================================================================================
1373
	var showadvmac = false;
1374

    
1375
	function show_advmac() {
1376
<?php
1377
		if (empty($pconfig['mac_allow']) && empty($pconfig['mac_deny']))
1378
			$hide = false;
1379
		else
1380
			$hide = true;
1381
?>
1382
		var hide = <?php if($hide) {echo 'true';} else {echo 'false';} ?>;
1383

    
1384
		hideInput('mac_allow', !showadvmac && !hide);
1385
		hideInput('mac_deny', !showadvmac && !hide);
1386
		hideInput('btnadvmac', hide);
1387

    
1388
		showadvmac = !showadvmac;
1389
	}
1390

    
1391
	$('#btnadvmac').prop('type', 'button');
1392

    
1393
	$('#btnadvmac').click(function(event) {
1394
		show_advmac();
1395
	});
1396

    
1397
  // Show advanced NTP options ======================================================================================
1398
	var showadvntp = false;
1399

    
1400
	function show_advntp() {
1401
<?php
1402
		if (empty($pconfig['ntp1']) && empty($pconfig['ntp2']))
1403
			$hide = false;
1404
		else
1405
			$hide = true;
1406
?>
1407
		var hide = <?php if($hide) {echo 'true';} else {echo 'false';} ?>;
1408

    
1409
		hideInput('ntp1', !showadvntp && !hide);
1410
		hideInput('ntp2', !showadvntp && !hide);
1411
		hideInput('btnadvntp', hide);
1412

    
1413
		showadvntp = !showadvntp;
1414
	}
1415

    
1416
	$('#btnadvntp').prop('type', 'button');
1417

    
1418
	$('#btnadvntp').click(function(event) {
1419
		show_advntp();
1420
	});
1421

    
1422
   // Show advanced TFTP options ======================================================================================
1423
	var showadvtftp = false;
1424

    
1425
	function show_advtftp() {
1426
<?php
1427
		if (empty($pconfig['tftp']))
1428
			$hide = false;
1429
		else
1430
			$hide = true;
1431
?>
1432
		var hide = <?php if($hide) {echo 'true';} else {echo 'false';} ?>;
1433

    
1434
		hideInput('tftp', !showadvtftp && !hide);
1435
		hideInput('btnadvtftp', hide);
1436

    
1437
		showadvtftp = !showadvtftp;
1438
	}
1439

    
1440
	$('#btnadvtftp').prop('type', 'button');
1441

    
1442
	$('#btnadvtftp').click(function(event) {
1443
		show_advtftp();
1444
	});
1445

    
1446
   // Show advanced LDAP options ======================================================================================
1447
	var showadvldap = false;
1448

    
1449
	function show_advldap() {
1450
<?php
1451
		if (empty($pconfig['ldap']))
1452
			$hide = false;
1453
		else
1454
			$hide = true;
1455
?>
1456
		var hide = <?php if($hide) {echo 'true';} else {echo 'false';} ?>;
1457

    
1458
		hideInput('ldap', !showadvldap && !hide);
1459
		hideInput('btnadvldap', hide);
1460

    
1461
		showadvldap = !showadvldap;
1462
	}
1463

    
1464
	$('#btnadvldap').prop('type', 'button');
1465

    
1466
	$('#btnadvldap').click(function(event) {
1467
		show_advldap();
1468
	});
1469

    
1470
   // Show advanced NETBOOT options ===================================================================================
1471
	var showadvboot = false;
1472

    
1473
	function show_advboot() {
1474
<?php
1475
		if (!$pconfig['netboot'] && empty($pconfig['nextserver']) && empty($pconfig['filename']) && empty($pconfig['filename32']) &&
1476
		    empty($pconfig['filename64']) && empty($pconfig['rootpath']))
1477
			$hide = false;
1478
		else
1479
			$hide = true;
1480
?>
1481
		var hide = <?php if($hide) {echo 'true';} else {echo 'false';} ?>;
1482

    
1483
		hideCheckbox('netboot', !showadvboot && !hide);
1484
		hideInput('nextserver', !showadvboot && !hide);
1485
		hideInput('filename', !showadvboot && !hide);
1486
		hideInput('filename32', !showadvboot && !hide);
1487
		hideInput('filename64', !showadvboot && !hide);
1488
		hideInput('rootpath', !showadvboot && !hide);
1489
		hideInput('btnadvboot', hide);
1490

    
1491
		showadvboot = !showadvboot;
1492
	}
1493

    
1494
	$('#btnadvboot').prop('type', 'button');
1495

    
1496
	$('#btnadvboot').click(function(event) {
1497
		show_advboot();
1498
	});
1499

    
1500
	// Show advanced additional opts options ===========================================================================
1501
	var showadvopts = false;
1502

    
1503
	function show_advopts() {
1504
<?php
1505
		if ( empty($pconfig['numberoptions']) ||
1506
		    (empty($pconfig['numberoptions']['item'][0]['number']) && (empty($pconfig['numberoptions']['item'][0]['value']))))
1507
			$hide = false;
1508
		else
1509
			$hide = true;
1510
?>
1511
		var hide = <?php if($hide) {echo 'true';} else {echo 'false';} ?>;
1512

    
1513
		hideClass('adnlopts', !showadvopts && !hide);
1514
		hideInput('btnadvopts', hide);
1515

    
1516
		showadvopts = !showadvopts;
1517
	}
1518

    
1519
	$('#btnadvopts').prop('type', 'button');
1520

    
1521
	$('#btnadvopts').click(function(event) {
1522
		show_advopts();
1523
	});
1524

    
1525
	// ---------- On initial page load ------------------------------------------------------------
1526

    
1527
	show_advdns();
1528
	show_advmac();
1529
	show_advntp();
1530
	show_advtftp();
1531
	show_advldap();
1532
	show_advboot();
1533
	show_advopts();
1534

    
1535
	// Suppress "Delete row" button if there are fewer than two rows
1536
	checkLastRow();
1537
});
1538
//]]>
1539
</script>
1540

    
1541
<?php include("foot.inc");
(132-132/234)