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
$closehead = false;
699
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
700
$shortcut_section = "dhcp";
701

    
702
include("head.inc");
703

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

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

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

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

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

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

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

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

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

    
750
display_top_tabs($tab_array);
751

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

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

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

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

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

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

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

    
800
// Compose a string to display the required address ranges
801
$range_from = ip2long(long2ip32(ip2long($ifcfgip) & gen_subnet_mask_long($ifcfgsn)));
802
$range_from++;
803

    
804
$range_to = ip2long(long2ip32(ip2long($ifcfgip) | (~gen_subnet_mask_long($ifcfgsn))));
805
$range_to--;
806

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1219

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

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

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

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

    
1241
	$counter++;
1242
}
1243

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

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

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

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

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

    
1276
print($form);
1277

    
1278
// DHCP Static Mappings table
1279

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

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

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

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

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

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

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

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

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

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

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

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

    
1400
		showadvmac = !showadvmac;
1401
	}
1402

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

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

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

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

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

    
1426
		showadvntp = !showadvntp;
1427
	}
1428

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

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

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

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

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

    
1451
		showadvtftp = !showadvtftp;
1452
	}
1453

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

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

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

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

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

    
1476
		showadvldap = !showadvldap;
1477
	}
1478

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

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

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

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

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

    
1507
		showadvboot = !showadvboot;
1508
	}
1509

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

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

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

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

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

    
1533
		showadvopts = !showadvopts;
1534
	}
1535

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

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

    
1542
	// ---------- On initial page load ------------------------------------------------------------
1543

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

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

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