Project

General

Profile

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

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

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

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

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

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

    
94
$iflist = get_configured_interface_with_descr();
95

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

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

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

    
115
$a_pools = array();
116

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
221
	return true;
222
}
223

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

    
226
	unset($input_errors);
227

    
228
	$pconfig = $_POST;
229

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
484
		$dhcpd_enable_changed = false;
485

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

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

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

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

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

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

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

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

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

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

    
579
		$dhcpdconf['numberoptions'] = $numberoptions;
580

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

    
589
		write_config();
590
	}
591
}
592

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

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

    
626
	$savemsg = get_std_save_message($retval);
627
}
628

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

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

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

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

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

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

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

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

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

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

    
693
	$pooltbl .=			'</tbody>';
694
	$pooltbl .=		'</table>';
695
	$pooltbl .= '</div>';
696

    
697
	return($pooltbl);
698
}
699

    
700
$closehead = false;
701
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
702
$shortcut_section = "dhcp";
703

    
704
include("head.inc");
705

    
706
if ($input_errors)
707
	print_input_errors($input_errors);
708

    
709
if ($savemsg)
710
	print_info_box($savemsg, 'success');
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
/* 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
require_once('classes/Form.class.php');
752

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1220

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

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

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

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

    
1242
	$counter++;
1243
}
1244

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

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

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

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

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

    
1277
print($form);
1278

    
1279
// DHCP Static Mappings table
1280

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

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

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

    
1351
<script>
1352
//<![CDATA[
1353
events.push(function(){
1354

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

    
1358
	function show_advdns() {
1359
<?php
1360
		if (!$pconfig['ddnsupdate'] && empty($pconfig['ddnsdomain']) && empty($pconfig['ddnsdomainprimary']) &&
1361
		    empty($pconfig['ddnsdomainkeyname']) && empty($pconfig['ddnsdomainkey']))
1362
			$hide = false;
1363
		else
1364
			$hide = true;
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
		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
		var hide = <?php if($hide) {echo 'true';} else {echo 'false';} ?>;
1419

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

    
1424
		showadvntp = !showadvntp;
1425
	}
1426

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

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

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

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

    
1445
		hideInput('tftp', !showadvtftp && !hide);
1446
		hideInput('btnadvtftp', hide);
1447

    
1448
		showadvtftp = !showadvtftp;
1449
	}
1450

    
1451
	$('#btnadvtftp').prop('type', 'button');
1452

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

    
1457
   // Show advanced LDAP options ======================================================================================
1458
	var showadvldap = false;
1459

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

    
1469
		hideInput('ldap', !showadvldap && !hide);
1470
		hideInput('btnadvldap', hide);
1471

    
1472
		showadvldap = !showadvldap;
1473
	}
1474

    
1475
	$('#btnadvldap').prop('type', 'button');
1476

    
1477
	$('#btnadvldap').click(function(event) {
1478
		show_advldap();
1479
	});
1480

    
1481
   // Show advanced NETBOOT options ===================================================================================
1482
	var showadvboot = false;
1483

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

    
1494
		hideCheckbox('netboot', !showadvboot && !hide);
1495
		hideInput('nextserver', !showadvboot && !hide);
1496
		hideInput('filename', !showadvboot && !hide);
1497
		hideInput('filename32', !showadvboot && !hide);
1498
		hideInput('filename64', !showadvboot && !hide);
1499
		hideInput('rootpath', !showadvboot && !hide);
1500
		hideInput('btnadvboot', hide);
1501

    
1502
		showadvboot = !showadvboot;
1503
	}
1504

    
1505
	$('#btnadvboot').prop('type', 'button');
1506

    
1507
	$('#btnadvboot').click(function(event) {
1508
		show_advboot();
1509
	});
1510

    
1511
	// Show advanced additional opts options ===========================================================================
1512
	var showadvopts = false;
1513

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

    
1524
		hideClass('adnlopts', !showadvopts && !hide);
1525
		hideInput('btnadvopts', hide);
1526

    
1527
		showadvopts = !showadvopts;
1528
	}
1529

    
1530
	$('#btnadvopts').prop('type', 'button');
1531

    
1532
	$('#btnadvopts').click(function(event) {
1533
		show_advopts();
1534
	});
1535

    
1536
	// ---------- On initial page load ------------------------------------------------------------
1537

    
1538
	show_advdns();
1539
	show_advmac();
1540
	show_advntp();
1541
	show_advtftp();
1542
	show_advldap();
1543
	show_advboot();
1544
	show_advopts();
1545

    
1546
	// Suppress "Delete row" button if there are fewer than two rows
1547
	checkLastRow();
1548
});
1549
//]]>
1550
</script>
1551

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