Project

General

Profile

Download (48.8 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
	foreach ($iflist as $ifent => $ifname) {
96
		$oc = $config['interfaces'][$ifent];
97
		if ((is_array($config['dhcpd'][$ifent]) && !isset($config['dhcpd'][$ifent]['enable']) && (!is_ipaddrv4($oc['ipaddr']))) ||
98
		    (!is_array($config['dhcpd'][$ifent]) && (!is_ipaddrv4($oc['ipaddr'])))) {
99
			continue;
100
		}
101

    
102
		$if = $ifent;
103
		break;
104
	}
105
}
106

    
107
$act = $_GET['act'];
108
if (!empty($_POST['act'])) {
109
	$act = $_POST['act'];
110
}
111

    
112
$a_pools = array();
113

    
114
if (is_array($config['dhcpd'][$if])) {
115
	$pool = $_GET['pool'];
116
	if (is_numeric($_POST['pool'])) {
117
		$pool = $_POST['pool'];
118
	}
119

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

    
126
	if (!is_array($config['dhcpd'][$if]['pool'])) {
127
		$config['dhcpd'][$if]['pool'] = array();
128
	}
129

    
130
	$a_pools = &$config['dhcpd'][$if]['pool'];
131

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

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

    
158
		$pconfig['dhcpleaseinlocaltime'] = $dhcpleaseinlocaltime;
159

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

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

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

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

    
205
$ifcfgip = $config['interfaces'][$if]['ipaddr'];
206
$ifcfgsn = $config['interfaces'][$if]['subnet'];
207

    
208
function validate_partial_mac_list($maclist) {
209
	$macs = explode(',', $maclist);
210

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

    
218
	return true;
219
}
220

    
221
if (isset($_POST['submit'])) {
222

    
223
	unset($input_errors);
224

    
225
	$pconfig = $_POST;
226

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

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

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

    
246
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
247

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

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

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

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

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

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

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

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

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

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

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

    
371
		if ($_POST['staticarp'] && $noip) {
372
			$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.";
373
		}
374

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

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

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

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

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

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

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

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

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

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

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

    
481
		$dhcpd_enable_changed = false;
482

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

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

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

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

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

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

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

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

    
558
		$dhcpdconf['tftp'] = $_POST['tftp'];
559
		$dhcpdconf['ldap'] = $_POST['ldap'];
560
		$dhcpdconf['netboot'] = ($_POST['netboot']) ? true : false;
561
		$dhcpdconf['nextserver'] = $_POST['nextserver'];
562
		$dhcpdconf['filename'] = $_POST['filename'];
563
		$dhcpdconf['filename32'] = $_POST['filename32'];
564
		$dhcpdconf['filename64'] = $_POST['filename64'];
565
		$dhcpdconf['rootpath'] = $_POST['rootpath'];
566
		unset($dhcpdconf['statsgraph']);
567
		if ($_POST['statsgraph']) {
568
			$dhcpdconf['statsgraph'] = $_POST['statsgraph'];
569
			enable_rrd_graphing();
570
		}
571

    
572
		// Handle the custom options rowhelper
573
		if (isset($dhcpdconf['numberoptions']['item'])) {
574
			unset($dhcpdconf['numberoptions']['item']);
575
		}
576

    
577
		$dhcpdconf['numberoptions'] = $numberoptions;
578

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

    
587
		write_config();
588
	}
589
}
590

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

    
620
	if ($retvaldhcp == 1 || $retvaldns == 1 || $retvalfc == 1) {
621
		$retval = 1;
622
	}
623

    
624
	$savemsg = get_std_save_message($retval);
625
}
626

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

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

    
647
		header("Location: services_dhcp.php?if={$if}");
648
		exit;
649
	}
650
}
651

    
652
// Build an HTML table that can be inserted into a Form_StaticText element
653
function build_pooltable() {
654
	global $a_pools;
655

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

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

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

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

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

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

    
691
	$pooltbl .=			'</tbody>';
692
	$pooltbl .=		'</table>';
693
	$pooltbl .= '</div>';
694

    
695
	return($pooltbl);
696
}
697

    
698
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
699
$shortcut_section = "dhcp";
700

    
701
include("head.inc");
702

    
703
if ($input_errors) {
704
	print_input_errors($input_errors);
705
}
706

    
707
if ($savemsg) {
708
	print_info_box($savemsg, 'success');
709
}
710

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

    
717
if (is_subsystem_dirty('staticmaps')) {
718
	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."));
719
}
720

    
721
/* active tabs */
722
$tab_array = array();
723
$tabscounter = 0;
724
$i = 0;
725

    
726
foreach ($iflist as $ifent => $ifname) {
727
	$oc = $config['interfaces'][$ifent];
728
	if ((is_array($config['dhcpd'][$ifent]) && !isset($config['dhcpd'][$ifent]['enable']) && (!is_ipaddrv4($oc['ipaddr']))) ||
729
	    (!is_array($config['dhcpd'][$ifent]) && (!is_ipaddrv4($oc['ipaddr'])))) {
730
		continue;
731
	}
732

    
733
	if ($ifent == $if) {
734
		$active = true;
735
	} else {
736
		$active = false;
737
	}
738

    
739
	$tab_array[] = array($ifname, $active, "services_dhcp.php?if={$ifent}");
740
	$tabscounter++;
741
}
742

    
743
if ($tabscounter == 0) {
744
	print_info_box(gettext("The DHCP Server can only be enabled on interfaces configured with a static IPv4 address. This system has none."));
745
	include("foot.inc");
746
	exit;
747
}
748

    
749
display_top_tabs($tab_array);
750

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

    
757
$section = new Form_Section('General Options');
758

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

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

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

    
789
$section->addInput(new Form_StaticText(
790
	'Subnet',
791
	gen_subnet($ifcfgip, $ifcfgsn)
792
));
793

    
794
$section->addInput(new Form_StaticText(
795
	'Subnet mask',
796
	gen_subnet_mask($ifcfgsn)
797
));
798

    
799
// Compose a string to display the required address ranges
800
$range_from = ip2long(gen_subnetv4($ifcfgip, $ifcfgsn));
801
$range_from++;
802

    
803
$range_to = ip2long(gen_subnetv4_max($ifcfgip, $ifcfgsn));
804
$range_to--;
805

    
806
$rangestr = long2ip32($range_from) . ' - ' . long2ip32($range_to);
807

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

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

    
821
$section->addInput(new Form_StaticText(
822
	'Available range',
823
	$rangestr
824
));
825

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

    
835
$group = new Form_Group('Range');
836

    
837
$group->add(new Form_IpAddress(
838
	'range_from',
839
	null,
840
	$pconfig['range_from']
841
))->setHelp('From');
842

    
843
$group->add(new Form_IpAddress(
844
	'range_to',
845
	null,
846
	$pconfig['range_to']
847
))->setHelp('To');
848

    
849
$section->add($group);
850

    
851
$form->add($section);
852

    
853
if (!is_numeric($pool) && !($act == "newpool")) {
854
	$section = new Form_Section('Additional pools');
855

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

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

    
867
	if (is_array($a_pools)) {
868
		$section->addInput(new Form_StaticText(
869
			null,
870
			build_pooltable()
871
		));
872
	}
873

    
874
	$form->add($section);
875
}
876

    
877
$section = new Form_Section('Servers');
878

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

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

    
891
for ($idx=1; $idx<=4; $idx++) {
892
	$section->addInput(new Form_IpAddress(
893
		'dns' . $idx,
894
		($idx == 1) ? 'DNS servers':null,
895
		$pconfig['dns' . $idx]
896
	))->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':'');
897
}
898

    
899
$form->add($section);
900

    
901
$section = new Form_Section('Other options');
902

    
903
$section->addInput(new Form_IpAddress(
904
	'gateway',
905
	'Gateway',
906
	$pconfig['gateway']
907
))->setPattern('[.a-zA-Z0-9_]+')
908
  ->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');
909

    
910
$section->addInput(new Form_Input(
911
	'domain',
912
	'Domain name',
913
	'text',
914
	$pconfig['domain']
915
))->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');
916

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

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

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

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

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

    
955
	$section->addInput(new Form_Checkbox(
956
		'dhcpleaseinlocaltime',
957
		'Time format change',
958
		'Change DHCP display lease time from UTC to local time',
959
		$pconfig['dhcpleaseinlocaltime']
960
	))->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.' .
961
				' This will be used for all DHCP interfaces lease time');
962
	$section->addInput(new Form_Checkbox(
963
		'statsgraph',
964
		'Statistics graphs',
965
		'Enable RRD statistics graphs',
966
		$pconfig['statsgraph']
967
	))->setHelp('Enable this to add DHCP leases statistics to the RRD graphs. Disabled by default.');
968
}
969

    
970
// DDNS
971
$btnadv = new Form_Button(
972
	'btnadvdns',
973
	'Advanced'
974
);
975

    
976
$btnadv->removeClass('btn-primary')->addClass('btn-default btn-sm');
977

    
978
$section->addInput(new Form_StaticText(
979
	'Dynamic DNS',
980
	$btnadv
981
));
982

    
983
$section->addInput(new Form_Checkbox(
984
	'ddnsupdate',
985
	null,
986
	'Enable registration of DHCP client names in DNS',
987
	$pconfig['ddnsupdate']
988
));
989

    
990
$section->addInput(new Form_Input(
991
	'ddnsdomain',
992
	'DDNS Domain',
993
	'text',
994
	$pconfig['ddnsdomain']
995
))->setHelp('Leave blank to disable dynamic DNS registration.' . '<br />' .
996
			'Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
997

    
998
$section->addInput(new Form_IpAddress(
999
	'ddnsdomainprimary',
1000
	'Primary DDNS address',
1001
	$pconfig['ddnsdomainprimary']
1002
))->setHelp('Primary domain name server IP address for the dynamic domain name');
1003

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

    
1011
$section->addInput(new Form_Input(
1012
	'ddnsdomainkey',
1013
	'DNS Domain key secret',
1014
	'text',
1015
	$pconfig['ddnsdomainkey']
1016
))->setHelp('Dynamic DNS domain key secret which will be used to register client names in the DNS server');
1017

    
1018
// Advanced MAC
1019
$btnadv = new Form_Button(
1020
	'btnadvmac',
1021
	'Advanced'
1022
);
1023

    
1024
$btnadv->removeClass('btn-primary')->addClass('btn-default btn-sm');
1025

    
1026
$section->addInput(new Form_StaticText(
1027
	'MAC address control',
1028
	$btnadv
1029
));
1030

    
1031
$section->addInput(new Form_Input(
1032
	'mac_allow',
1033
	'Allow',
1034
	'text',
1035
	$pconfig['mac_allow']
1036
))->setHelp('List of partial MAC addresses to allow, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1037

    
1038
$section->addInput(new Form_Input(
1039
	'mac_deny',
1040
	'Deny',
1041
	'text',
1042
	$pconfig['mac_deny']
1043
))->setHelp('List of partial MAC addresses to deny access, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1044

    
1045
// Advanced NTP
1046
$btnadv = new Form_Button(
1047
	'btnadvntp',
1048
	'Advanced'
1049
);
1050

    
1051
$btnadv->removeClass('btn-primary')->addClass('btn-default btn-sm');
1052

    
1053
$section->addInput(new Form_StaticText(
1054
	'NTP servers',
1055
	$btnadv
1056
));
1057

    
1058
$section->addInput(new Form_IpAddress(
1059
	'ntp1',
1060
	null,
1061
	$pconfig['ntp1']
1062
))->setAttribute('placeholder', 'NTP Server 1');
1063

    
1064
$section->addInput(new Form_IpAddress(
1065
	'ntp2',
1066
	null,
1067
	$pconfig['ntp2']
1068
))->setAttribute('placeholder', 'NTP Server 2');
1069

    
1070
// Advanced TFTP
1071
$btnadv = new Form_Button(
1072
	'btnadvtftp',
1073
	'Advanced'
1074
);
1075

    
1076
$btnadv->removeClass('btn-primary')->addClass('btn-default btn-sm');
1077

    
1078
$section->addInput(new Form_StaticText(
1079
	'TFTP server',
1080
	$btnadv
1081
));
1082

    
1083
$section->addInput(new Form_IpAddress(
1084
	'tftp',
1085
	null,
1086
	$pconfig['tftp']
1087
))->setHelp('Leave blank to disable.  Enter a full hostname or IP for the TFTP server')->setPattern('[.a-zA-Z0-9_]+');
1088

    
1089
// Advanced LDAP
1090
$btnadv = new Form_Button(
1091
	'btnadvldap',
1092
	'Advanced'
1093
);
1094

    
1095
$btnadv->removeClass('btn-primary')->addClass('btn-default btn-sm');
1096

    
1097
$section->addInput(new Form_StaticText(
1098
	'LDAP URI',
1099
	$btnadv
1100
));
1101

    
1102
$section->addInput(new Form_Input(
1103
	'ldap',
1104
	null,
1105
	'text',
1106
	$pconfig['ldap']
1107
))->setHelp('Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com ');
1108

    
1109
// Advanced NETBOOT
1110
$btnadv = new Form_Button(
1111
	'btnadvboot',
1112
	'Advanced'
1113
);
1114

    
1115
$btnadv->removeClass('btn-primary')->addClass('btn-default btn-sm');
1116

    
1117
$section->addInput(new Form_StaticText(
1118
	'Network booting',
1119
	$btnadv
1120
));
1121

    
1122
$section->addInput(new Form_Checkbox(
1123
	'netboot',
1124
	null,
1125
	'Enables network booting',
1126
	$pconfig['netboot']
1127
));
1128

    
1129
$section->addInput(new Form_IpAddress(
1130
	'nextserver',
1131
	'Next Server',
1132
	$pconfig['nextserver']
1133
))->setHelp('Enter the IP address of the next server');
1134

    
1135
$section->addInput(new Form_Input(
1136
	'filename',
1137
	'Default BIOS file name',
1138
	'text',
1139
	$pconfig['filename']
1140
));
1141

    
1142
$section->addInput(new Form_Input(
1143
	'filename32',
1144
	'UEFI 32 bit file name',
1145
	'text',
1146
	$pconfig['filename32']
1147
));
1148

    
1149
$section->addInput(new Form_Input(
1150
	'filename64',
1151
	'UEFI 64 bit file name',
1152
	'text',
1153
	$pconfig['filename64']
1154
))->setHelp('You need both a filename and a boot server configured for this to work! ' .
1155
			'You will need all three filenames and a boot server configured for UEFI to work! ');
1156

    
1157
$section->addInput(new Form_Input(
1158
	'rootpath',
1159
	'Root path',
1160
	'text',
1161
	$pconfig['rootpath']
1162
))->setHelp('string-format: iscsi:(servername):(protocol):(port):(LUN):targetname ');
1163

    
1164
// Advanced Additional options
1165
$btnadv = new Form_Button(
1166
	'btnadvopts',
1167
	'Advanced'
1168
);
1169

    
1170
$btnadv->removeClass('btn-primary')->addClass('btn-default btn-sm');
1171

    
1172
$section->addInput(new Form_StaticText(
1173
	'Additional BOOTP/DHCP Options',
1174
	$btnadv
1175
));
1176

    
1177
$form->add($section);
1178

    
1179
$section = new Form_Section('Additional BOOTP/DHCP Options');
1180
$section->addClass('adnlopts');
1181

    
1182
$section->addInput(new Form_StaticText(
1183
	null,
1184
	'<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. ' .
1185
	'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>'
1186
));
1187

    
1188
if (!$pconfig['numberoptions']) {
1189
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1190
}
1191

    
1192
$customitemtypes = array(
1193
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1194
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1195
	'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')
1196
);
1197

    
1198
$numrows = count($item) -1;
1199
$counter = 0;
1200

    
1201
$numrows = count($pconfig['numberoptions']['item']) -1;
1202

    
1203
foreach ($pconfig['numberoptions']['item'] as $item) {
1204
	$number = $item['number'];
1205
	$itemtype = $item['type'];
1206
	$value = $item['value'];
1207

    
1208
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1209
	$group->addClass('repeatable');
1210

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

    
1218

    
1219
	$group->add(new Form_Select(
1220
		'itemtype' . $counter,
1221
		null,
1222
		$itemtype,
1223
		$customitemtypes
1224
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1225

    
1226
	$group->add(new Form_Input(
1227
		'value' . $counter,
1228
		null,
1229
		'text',
1230
		$value
1231
	))->setHelp($numrows == $counter ? 'Value':null);
1232

    
1233
	$group->add(new Form_Button(
1234
		'deleterow' . $counter,
1235
		'Delete'
1236
	))->removeClass('btn-primary')->addClass('btn-warning');
1237

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

    
1240
	$counter++;
1241
}
1242

    
1243
$section->addInput(new Form_Button(
1244
	'addrow',
1245
	'Add'
1246
))->removeClass('btn-primary')->addClass('btn-success');
1247

    
1248
$form->add($section);
1249

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

    
1259
if (is_numeric($pool)) {
1260
	$form->addGlobal(new Form_Input(
1261
		'pool',
1262
		null,
1263
		'hidden',
1264
		$pool
1265
	));
1266
}
1267

    
1268
$form->addGlobal(new Form_Input(
1269
	'if',
1270
	null,
1271
	'hidden',
1272
	$if
1273
));
1274

    
1275
print($form);
1276

    
1277
// DHCP Static Mappings table
1278

    
1279
if (!is_numeric($pool) && !($act == "newpool")) {
1280
?>
1281

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

    
1339
<nav class="action-buttons">
1340
	<a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>" class="btn btn-sm btn-success">
1341
		<i class="fa fa-plus icon-embed-btn"></i>
1342
		<?=gettext("Add")?>
1343
	</a>
1344
</nav>
1345
<?php
1346
}
1347
?>
1348

    
1349
<script type="text/javascript">
1350
//<![CDATA[
1351
events.push(function() {
1352

    
1353
	// Show advanced DNS options ======================================================================================
1354
	var showadvdns = false;
1355

    
1356
	function show_advdns() {
1357
<?php
1358
		if (!$pconfig['ddnsupdate'] && empty($pconfig['ddnsdomain']) && empty($pconfig['ddnsdomainprimary']) &&
1359
		    empty($pconfig['ddnsdomainkeyname']) && empty($pconfig['ddnsdomainkey'])) {
1360
			$hide = false;
1361
		} else {
1362
			$hide = true;
1363
		}
1364
?>
1365
		var hide = <?php if ($hide) {echo 'true';} else {echo 'false';} ?>;
1366

    
1367
		hideCheckbox('ddnsupdate', !showadvdns && !hide);
1368
		hideInput('ddnsdomain', !showadvdns && !hide);
1369
		hideInput('ddnsdomainprimary', !showadvdns && !hide);
1370
		hideInput('ddnsdomainkeyname', !showadvdns && !hide);
1371
		hideInput('ddnsdomainkey', !showadvdns && !hide);
1372
		hideInput('btnadvdns', hide);
1373
		showadvdns = !showadvdns;
1374
	}
1375

    
1376
	$('#btnadvdns').prop('type', 'button');
1377

    
1378
	$('#btnadvdns').click(function(event) {
1379
		show_advdns();
1380
	});
1381

    
1382
 // Show advanced MAC options ======================================================================================
1383
	var showadvmac = false;
1384

    
1385
	function show_advmac() {
1386
<?php
1387
		if (empty($pconfig['mac_allow']) && empty($pconfig['mac_deny'])) {
1388
			$hide = false;
1389
		} else {
1390
			$hide = true;
1391
		}
1392
?>
1393
		var hide = <?php if ($hide) {echo 'true';} else {echo 'false';} ?>;
1394

    
1395
		hideInput('mac_allow', !showadvmac && !hide);
1396
		hideInput('mac_deny', !showadvmac && !hide);
1397
		hideInput('btnadvmac', hide);
1398

    
1399
		showadvmac = !showadvmac;
1400
	}
1401

    
1402
	$('#btnadvmac').prop('type', 'button');
1403

    
1404
	$('#btnadvmac').click(function(event) {
1405
		show_advmac();
1406
	});
1407

    
1408
  // Show advanced NTP options ======================================================================================
1409
	var showadvntp = false;
1410

    
1411
	function show_advntp() {
1412
<?php
1413
		if (empty($pconfig['ntp1']) && empty($pconfig['ntp2'])) {
1414
			$hide = false;
1415
		} else {
1416
			$hide = true;
1417
		}
1418
?>
1419
		var hide = <?php if ($hide) {echo 'true';} else {echo 'false';} ?>;
1420

    
1421
		hideInput('ntp1', !showadvntp && !hide);
1422
		hideInput('ntp2', !showadvntp && !hide);
1423
		hideInput('btnadvntp', hide);
1424

    
1425
		showadvntp = !showadvntp;
1426
	}
1427

    
1428
	$('#btnadvntp').prop('type', 'button');
1429

    
1430
	$('#btnadvntp').click(function(event) {
1431
		show_advntp();
1432
	});
1433

    
1434
   // Show advanced TFTP options ======================================================================================
1435
	var showadvtftp = false;
1436

    
1437
	function show_advtftp() {
1438
<?php
1439
		if (empty($pconfig['tftp'])) {
1440
			$hide = false;
1441
		} else {
1442
			$hide = true;
1443
		}
1444
?>
1445
		var hide = <?php if ($hide) {echo 'true';} else {echo 'false';} ?>;
1446

    
1447
		hideInput('tftp', !showadvtftp && !hide);
1448
		hideInput('btnadvtftp', hide);
1449

    
1450
		showadvtftp = !showadvtftp;
1451
	}
1452

    
1453
	$('#btnadvtftp').prop('type', 'button');
1454

    
1455
	$('#btnadvtftp').click(function(event) {
1456
		show_advtftp();
1457
	});
1458

    
1459
   // Show advanced LDAP options ======================================================================================
1460
	var showadvldap = false;
1461

    
1462
	function show_advldap() {
1463
<?php
1464
		if (empty($pconfig['ldap'])) {
1465
			$hide = false;
1466
		} else {
1467
			$hide = true;
1468
		}
1469
?>
1470
		var hide = <?php if ($hide) {echo 'true';} else {echo 'false';} ?>;
1471

    
1472
		hideInput('ldap', !showadvldap && !hide);
1473
		hideInput('btnadvldap', hide);
1474

    
1475
		showadvldap = !showadvldap;
1476
	}
1477

    
1478
	$('#btnadvldap').prop('type', 'button');
1479

    
1480
	$('#btnadvldap').click(function(event) {
1481
		show_advldap();
1482
	});
1483

    
1484
   // Show advanced NETBOOT options ===================================================================================
1485
	var showadvboot = false;
1486

    
1487
	function show_advboot() {
1488
<?php
1489
		if (!$pconfig['netboot'] && empty($pconfig['nextserver']) && empty($pconfig['filename']) && empty($pconfig['filename32']) &&
1490
		    empty($pconfig['filename64']) && empty($pconfig['rootpath'])) {
1491
			$hide = false;
1492
		} else {
1493
			$hide = true;
1494
		}
1495
?>
1496
		var hide = <?php if ($hide) {echo 'true';} else {echo 'false';} ?>;
1497

    
1498
		hideCheckbox('netboot', !showadvboot && !hide);
1499
		hideInput('nextserver', !showadvboot && !hide);
1500
		hideInput('filename', !showadvboot && !hide);
1501
		hideInput('filename32', !showadvboot && !hide);
1502
		hideInput('filename64', !showadvboot && !hide);
1503
		hideInput('rootpath', !showadvboot && !hide);
1504
		hideInput('btnadvboot', hide);
1505

    
1506
		showadvboot = !showadvboot;
1507
	}
1508

    
1509
	$('#btnadvboot').prop('type', 'button');
1510

    
1511
	$('#btnadvboot').click(function(event) {
1512
		show_advboot();
1513
	});
1514

    
1515
	// Show advanced additional opts options ===========================================================================
1516
	var showadvopts = false;
1517

    
1518
	function show_advopts() {
1519
<?php
1520
		if (empty($pconfig['numberoptions']) ||
1521
		    (empty($pconfig['numberoptions']['item'][0]['number']) && (empty($pconfig['numberoptions']['item'][0]['value'])))) {
1522
			$hide = false;
1523
		} else {
1524
			$hide = true;
1525
		}
1526
?>
1527
		var hide = <?php if ($hide) {echo 'true';} else {echo 'false';} ?>;
1528

    
1529
		hideClass('adnlopts', !showadvopts && !hide);
1530
		hideInput('btnadvopts', hide);
1531

    
1532
		showadvopts = !showadvopts;
1533
	}
1534

    
1535
	$('#btnadvopts').prop('type', 'button');
1536

    
1537
	$('#btnadvopts').click(function(event) {
1538
		show_advopts();
1539
	});
1540

    
1541
	// ---------- On initial page load ------------------------------------------------------------
1542

    
1543
	show_advdns();
1544
	show_advmac();
1545
	show_advntp();
1546
	show_advtftp();
1547
	show_advldap();
1548
	show_advboot();
1549
	show_advopts();
1550

    
1551
	// Suppress "Delete row" button if there are fewer than two rows
1552
	checkLastRow();
1553
});
1554
//]]>
1555
</script>
1556

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