Project

General

Profile

Download (47.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($config['dhcpd'][$if]['staticmap'])) {
141
		$dhcpdconf['staticmap'] = array();
142
	}
143

    
144
	$a_maps = &$config['dhcpd'][$if]['staticmap'];
145
}
146
if (is_array($dhcpdconf)) {
147
	// Global Options
148
	if (!is_numeric($pool) && !($act == "newpool")) {
149
		$pconfig['enable'] = isset($dhcpdconf['enable']);
150
		$pconfig['staticarp'] = isset($dhcpdconf['staticarp']);
151
		// No reason to specify this per-pool, per the dhcpd.conf man page it needs to be in every
152
		//	 pool and should be specified in every pool both nodes share, so we'll treat it as global
153
		$pconfig['failover_peerip'] = $dhcpdconf['failover_peerip'];
154

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

    
164
		$pconfig['dhcpleaseinlocaltime'] = $dhcpleaseinlocaltime;
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['nonak'] = isset($dhcpdconf['nonak']);
185
	$pconfig['ddnsdomain'] = $dhcpdconf['ddnsdomain'];
186
	$pconfig['ddnsdomainprimary'] = $dhcpdconf['ddnsdomainprimary'];
187
	$pconfig['ddnsdomainkeyname'] = $dhcpdconf['ddnsdomainkeyname'];
188
	$pconfig['ddnsdomainkey'] = $dhcpdconf['ddnsdomainkey'];
189
	$pconfig['ddnsupdate'] = isset($dhcpdconf['ddnsupdate']);
190
	$pconfig['mac_allow'] = $dhcpdconf['mac_allow'];
191
	$pconfig['mac_deny'] = $dhcpdconf['mac_deny'];
192
	list($pconfig['ntp1'], $pconfig['ntp2']) = $dhcpdconf['ntpserver'];
193
	$pconfig['tftp'] = $dhcpdconf['tftp'];
194
	$pconfig['ldap'] = $dhcpdconf['ldap'];
195
	$pconfig['netboot'] = isset($dhcpdconf['netboot']);
196
	$pconfig['nextserver'] = $dhcpdconf['nextserver'];
197
	$pconfig['filename'] = $dhcpdconf['filename'];
198
	$pconfig['filename32'] = $dhcpdconf['filename32'];
199
	$pconfig['filename64'] = $dhcpdconf['filename64'];
200
	$pconfig['rootpath'] = $dhcpdconf['rootpath'];
201
	$pconfig['netmask'] = $dhcpdconf['netmask'];
202
	$pconfig['numberoptions'] = $dhcpdconf['numberoptions'];
203
	$pconfig['statsgraph'] = $dhcpdconf['statsgraph'];
204
}
205

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

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

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

    
219
	return true;
220
}
221

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

    
224
	unset($input_errors);
225

    
226
	$pconfig = $_POST;
227

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
416
			if (is_numeric($pool) || ($act == "newpool")) {
417
				if (!((ip2ulong($_POST['range_from']) > ip2ulong($config['dhcpd'][$if]['range']['to'])) ||
418
				      (ip2ulong($_POST['range_to']) < ip2ulong($config['dhcpd'][$if]['range']['from'])))) {
419
					$input_errors[] = gettext("The specified range must not be within the DHCP range for this interface.");
420
				}
421
			}
422

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

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

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

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

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

    
480
		$dhcpd_enable_changed = false;
481

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

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

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

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

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

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

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

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

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

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

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

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

    
587
		write_config();
588
	}
589
}
590

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
695
	return($pooltbl);
696
}
697

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

    
701
include("head.inc");
702

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

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

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

    
717
if (is_subsystem_dirty('staticmaps')) {
718
	print_apply_box(gettext("The static mapping configuration has been changed.") . "<br />" . gettext("You must apply the changes in order for them to take effect."));
719
}
720

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

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

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

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

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

    
749
display_top_tabs($tab_array);
750

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

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

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

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

    
780
$section->addInput(new Form_Checkbox(
781
	'nonak',
782
	'Ignore denied clients',
783
	'Denied clients will be ignored rather than rejected.',
784
	$pconfig['nonak']
785
));
786

    
787

    
788
if (is_numeric($pool) || ($act == "newpool")) {
789
	$section->addInput(new Form_Input(
790
		'descr',
791
		'Pool Description',
792
		'text',
793
		$pconfig['descr']
794
	));
795
}
796

    
797
$section->addInput(new Form_StaticText(
798
	'Subnet',
799
	gen_subnet($ifcfgip, $ifcfgsn)
800
));
801

    
802
$section->addInput(new Form_StaticText(
803
	'Subnet mask',
804
	gen_subnet_mask($ifcfgsn)
805
));
806

    
807
// Compose a string to display the required address ranges
808
$range_from = ip2long(gen_subnetv4($ifcfgip, $ifcfgsn));
809
$range_from++;
810

    
811
$range_to = ip2long(gen_subnetv4_max($ifcfgip, $ifcfgsn));
812
$range_to--;
813

    
814
$rangestr = long2ip32($range_from) . ' - ' . long2ip32($range_to);
815

    
816
if (is_numeric($pool) || ($act == "newpool")) {
817
	$rangestr .= '<br />' . gettext('In-use DHCP Pool Ranges:');
818
	if (is_array($config['dhcpd'][$if]['range'])) {
819
		$rangestr .= '<br />' . $config['dhcpd'][$if]['range']['from'] . ' - ' . $config['dhcpd'][$if]['range']['to'];
820
	}
821

    
822
	foreach ($a_pools as $p) {
823
		if (is_array($p['range'])) {
824
			$rangestr .= '<br />' . $p['range']['from'] . ' - ' . $p['range']['to'];
825
		}
826
	}
827
}
828

    
829
$section->addInput(new Form_StaticText(
830
	'Available range',
831
	$rangestr
832
));
833

    
834
if ($is_olsr_enabled) {
835
	$section->addInput(new Form_Select(
836
		'netmask',
837
		'Subnet mask',
838
		$pconfig['netmask'],
839
		array_combine(range(32, 1, -1), range(32, 1, -1))
840
	));
841
}
842

    
843
$group = new Form_Group('Range');
844

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

    
851
$group->add(new Form_IpAddress(
852
	'range_to',
853
	null,
854
	$pconfig['range_to']
855
))->setHelp('To');
856

    
857
$section->add($group);
858

    
859
$form->add($section);
860

    
861
if (!is_numeric($pool) && !($act == "newpool")) {
862
	$section = new Form_Section('Additional pools');
863

    
864
	$btnaddpool = new Form_Button(
865
		'btnaddpool',
866
		'Add pool',
867
		'services_dhcp.php?if=' . htmlspecialchars($if) . '&act=newpool'
868
	);
869

    
870
	$section->addInput(new Form_StaticText(
871
		'Add',
872
		$btnaddpool
873
	))->setHelp('If you need additional pools of addresses inside of this subnet outside the above Range, they may be specified here');
874

    
875
	if (is_array($a_pools)) {
876
		$section->addInput(new Form_StaticText(
877
			null,
878
			build_pooltable()
879
		));
880
	}
881

    
882
	$form->add($section);
883
}
884

    
885
$section = new Form_Section('Servers');
886

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

    
893
$section->addInput(new Form_IpAddress(
894
	'wins2',
895
	null,
896
	$pconfig['wins2']
897
))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'WINS Server 2');
898

    
899
for ($idx=1; $idx<=4; $idx++) {
900
	$section->addInput(new Form_IpAddress(
901
		'dns' . $idx,
902
		($idx == 1) ? 'DNS servers':null,
903
		$pconfig['dns' . $idx]
904
	))->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':'');
905
}
906

    
907
$form->add($section);
908

    
909
$section = new Form_Section('Other options');
910

    
911
$section->addInput(new Form_IpAddress(
912
	'gateway',
913
	'Gateway',
914
	$pconfig['gateway']
915
))->setPattern('[.a-zA-Z0-9_]+')
916
  ->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');
917

    
918
$section->addInput(new Form_Input(
919
	'domain',
920
	'Domain name',
921
	'text',
922
	$pconfig['domain']
923
))->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');
924

    
925
$section->addInput(new Form_Input(
926
	'domainsearchlist',
927
	'Domain search list',
928
	'text',
929
	$pconfig['domainsearchlist']
930
))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator');
931

    
932
$section->addInput(new Form_Input(
933
	'deftime',
934
	'Default lease time',
935
	'number',
936
	$pconfig['deftime']
937
))->setHelp('This is used for clients that do not ask for a specific expiration time. The default is 7200 seconds');
938

    
939
$section->addInput(new Form_Input(
940
	'maxtime',
941
	'Maximum lease time',
942
	'number',
943
	$pconfig['maxtime']
944
))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds');
945

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

    
955
if (!is_numeric($pool) && !($act == "newpool")) {
956
	$section->addInput(new Form_Checkbox(
957
		'staticarp',
958
		'Static ARP',
959
		'Enable Static ARP entries',
960
		$pconfig['staticarp']
961
	))->setHelp('This option persists even if DHCP server is disabled. Only the machines listed below will be able to communicate with the firewall on this interface.');
962

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

    
978
// DDNS
979
$btnadv = new Form_Button(
980
	'btnadvdns',
981
	'Advanced'
982
);
983

    
984
$btnadv->removeClass('btn-primary')->addClass('btn-info btn-sm');
985

    
986
$section->addInput(new Form_StaticText(
987
	'Dynamic DNS',
988
	$btnadv
989
));
990

    
991
$section->addInput(new Form_Checkbox(
992
	'ddnsupdate',
993
	null,
994
	'Enable registration of DHCP client names in DNS',
995
	$pconfig['ddnsupdate']
996
));
997

    
998
$section->addInput(new Form_Input(
999
	'ddnsdomain',
1000
	'DDNS Domain',
1001
	'text',
1002
	$pconfig['ddnsdomain']
1003
))->setHelp('Leave blank to disable dynamic DNS registration.' . '<br />' .
1004
			'Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
1005

    
1006
$section->addInput(new Form_IpAddress(
1007
	'ddnsdomainprimary',
1008
	'Primary DDNS address',
1009
	$pconfig['ddnsdomainprimary']
1010
))->setHelp('Primary domain name server IP address for the dynamic domain name');
1011

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

    
1019
$section->addInput(new Form_Input(
1020
	'ddnsdomainkey',
1021
	'DNS Domain key secret',
1022
	'text',
1023
	$pconfig['ddnsdomainkey']
1024
))->setHelp('Dynamic DNS domain key secret which will be used to register client names in the DNS server');
1025

    
1026
// Advanced MAC
1027
$btnadv = new Form_Button(
1028
	'btnadvmac',
1029
	'Advanced'
1030
);
1031

    
1032
$btnadv->removeClass('btn-primary')->addClass('btn-info btn-sm');
1033

    
1034
$section->addInput(new Form_StaticText(
1035
	'MAC address control',
1036
	$btnadv
1037
));
1038

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

    
1046
$section->addInput(new Form_Input(
1047
	'mac_deny',
1048
	'MAC Deny',
1049
	'text',
1050
	$pconfig['mac_deny']
1051
))->setHelp('List of partial MAC addresses to deny access, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1052

    
1053
// Advanced NTP
1054
$btnadv = new Form_Button(
1055
	'btnadvntp',
1056
	'Advanced'
1057
);
1058

    
1059
$btnadv->removeClass('btn-primary')->addClass('btn-info btn-sm');
1060

    
1061
$section->addInput(new Form_StaticText(
1062
	'NTP',
1063
	$btnadv
1064
));
1065

    
1066
$section->addInput(new Form_IpAddress(
1067
	'ntp1',
1068
	'NTP Server 1',
1069
	$pconfig['ntp1']
1070
));
1071

    
1072
$section->addInput(new Form_IpAddress(
1073
	'ntp2',
1074
	'NTP Server 2',
1075
	$pconfig['ntp2']
1076
));
1077

    
1078
// Advanced TFTP
1079
$btnadv = new Form_Button(
1080
	'btnadvtftp',
1081
	'Advanced'
1082
);
1083

    
1084
$btnadv->removeClass('btn-primary')->addClass('btn-info btn-sm');
1085

    
1086
$section->addInput(new Form_StaticText(
1087
	'TFTP',
1088
	$btnadv
1089
));
1090

    
1091
$section->addInput(new Form_IpAddress(
1092
	'tftp',
1093
	'TFTP Server',
1094
	$pconfig['tftp']
1095
))->setHelp('Leave blank to disable.  Enter a full hostname or IP for the TFTP server')->setPattern('[.a-zA-Z0-9_]+');
1096

    
1097
// Advanced LDAP
1098
$btnadv = new Form_Button(
1099
	'btnadvldap',
1100
	'Advanced'
1101
);
1102

    
1103
$btnadv->removeClass('btn-primary')->addClass('btn-info btn-sm');
1104

    
1105
$section->addInput(new Form_StaticText(
1106
	'LDAP',
1107
	$btnadv
1108
));
1109

    
1110
$section->addInput(new Form_Input(
1111
	'ldap',
1112
	'LDAP Server URI',
1113
	'text',
1114
	$pconfig['ldap']
1115
))->setHelp('Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com ');
1116

    
1117
$form->add($section);
1118

    
1119
// Advanced Additional options
1120
$btnadv = new Form_Button(
1121
	'btnadvopts',
1122
	'Advanced'
1123
);
1124

    
1125
$btnadv->removeClass('btn-primary')->addClass('btn-info btn-sm');
1126

    
1127
$section->addInput(new Form_StaticText(
1128
	'Additional BOOTP/DHCP Options',
1129
	$btnadv
1130
));
1131

    
1132
$section = new Form_Section('Additional BOOTP/DHCP Options');
1133
$section->addClass('adnlopts');
1134

    
1135
$section->addInput(new Form_StaticText(
1136
	null,
1137
	'<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.') . ' ' .
1138
	sprintf(gettext('For a list of available options please visit this %1$s URL%2$s'), '<a href="http://www.iana.org/assignments/bootp-dhcp-parameters/" target="_blank">', '</a></div>')
1139
));
1140

    
1141
if (!$pconfig['numberoptions']) {
1142
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1143
}
1144

    
1145
$customitemtypes = array(
1146
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1147
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1148
	'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')
1149
);
1150

    
1151
$numrows = count($item) -1;
1152
$counter = 0;
1153

    
1154
$numrows = count($pconfig['numberoptions']['item']) -1;
1155

    
1156
foreach ($pconfig['numberoptions']['item'] as $item) {
1157
	$number = $item['number'];
1158
	$itemtype = $item['type'];
1159
	$value = base64_decode($item['value']);
1160

    
1161
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1162
	$group->addClass('repeatable');
1163

    
1164
	$group->add(new Form_Input(
1165
		'number' . $counter,
1166
		null,
1167
		'text',
1168
		$number
1169
	))->setHelp($numrows == $counter ? 'Number':null);
1170

    
1171

    
1172
	$group->add(new Form_Select(
1173
		'itemtype' . $counter,
1174
		null,
1175
		$itemtype,
1176
		$customitemtypes
1177
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1178

    
1179
	$group->add(new Form_Input(
1180
		'value' . $counter,
1181
		null,
1182
		'text',
1183
		$value
1184
	))->setHelp($numrows == $counter ? 'Value':null);
1185

    
1186
	$group->add(new Form_Button(
1187
		'deleterow' . $counter,
1188
		'Delete'
1189
	))->removeClass('btn-primary')->addClass('btn-warning');
1190

    
1191
	$section->add($group);
1192

    
1193
	$counter++;
1194
}
1195

    
1196
$section->addInput(new Form_Button(
1197
	'addrow',
1198
	'Add'
1199
))->removeClass('btn-primary')->addClass('btn-success');
1200

    
1201
$form->add($section);
1202

    
1203
if ($pconfig['netboot']) {
1204
	$sectate = COLLAPSIBLE|SEC_OPEN;
1205
} else {
1206
	$sectate = COLLAPSIBLE|SEC_CLOSED;
1207
}
1208
$section = new Form_Section("Network booting", nwkbootsec, $sectate);
1209

    
1210
$section->addInput(new Form_Checkbox(
1211
	'netboot',
1212
	'Enable',
1213
	'Enables network booting',
1214
	$pconfig['netboot']
1215
));
1216

    
1217
$section->addInput(new Form_IpAddress(
1218
	'nextserver',
1219
	'Next Server',
1220
	$pconfig['nextserver']
1221
))->setHelp('Enter the IP address of the next server');
1222

    
1223
$section->addInput(new Form_Input(
1224
	'filename',
1225
	'Default BIOS file name',
1226
	'text',
1227
	$pconfig['filename']
1228
));
1229

    
1230
$section->addInput(new Form_Input(
1231
	'filename32',
1232
	'UEFI 32 bit file name',
1233
	'text',
1234
	$pconfig['filename32']
1235
));
1236

    
1237
$section->addInput(new Form_Input(
1238
	'filename64',
1239
	'UEFI 64 bit file name',
1240
	'text',
1241
	$pconfig['filename64']
1242
))->setHelp('You need both a filename and a boot server configured for this to work! ' .
1243
			'You will need all three filenames and a boot server configured for UEFI to work! ');
1244

    
1245
$section->addInput(new Form_Input(
1246
	'rootpath',
1247
	'Root path',
1248
	'text',
1249
	$pconfig['rootpath']
1250
))->setHelp('string-format: iscsi:(servername):(protocol):(port):(LUN):targetname ');
1251

    
1252
$form->add($section);
1253

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

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

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

    
1279
print($form);
1280

    
1281
// DHCP Static Mappings table
1282

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

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

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

    
1353
<script type="text/javascript">
1354
//<![CDATA[
1355
events.push(function() {
1356

    
1357
	// Show advanced DNS options ======================================================================================
1358
	var showadvdns = false;
1359

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

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

    
1380
	$('#btnadvdns').prop('type', 'button');
1381

    
1382
	$('#btnadvdns').click(function(event) {
1383
		show_advdns();
1384
	});
1385

    
1386
 // Show advanced MAC options ======================================================================================
1387
	var showadvmac = false;
1388

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

    
1399
		hideInput('mac_allow', !showadvmac && !hide);
1400
		hideInput('mac_deny', !showadvmac && !hide);
1401

    
1402
		showadvmac = !showadvmac;
1403
	}
1404

    
1405
	$('#btnadvmac').prop('type', 'button');
1406

    
1407
	$('#btnadvmac').click(function(event) {
1408
		show_advmac(true);
1409
	});
1410

    
1411
  // Show advanced NTP options ======================================================================================
1412
	var showadvntp = false;
1413

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

    
1424
		hideInput('ntp1', !showadvntp && !hide);
1425
		hideInput('ntp2', !showadvntp && !hide);
1426
		hideInput('btnadvntp', hide);
1427

    
1428
		showadvntp = !showadvntp;
1429
	}
1430

    
1431
	$('#btnadvntp').prop('type', 'button');
1432

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

    
1437
   // Show advanced TFTP options ======================================================================================
1438
	var showtftp = false;
1439

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

    
1450
		hideInput('tftp', !showtftp & !hide);
1451

    
1452
		showtftp = !showtftp;
1453
	}
1454

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

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

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

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

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

    
1477
		showadvldap = !showadvldap;
1478
	}
1479

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

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

    
1486
	// Show advanced additional opts options ===========================================================================
1487
	var showadvopts = false;
1488

    
1489
	function show_advopts() {
1490
<?php
1491
		if (empty($pconfig['numberoptions']) ||
1492
		    (empty($pconfig['numberoptions']['item'][0]['number']) && (empty($pconfig['numberoptions']['item'][0]['value'])))) {
1493
			$hide = false;
1494
		} else {
1495
			$hide = true;
1496
		}
1497
?>
1498
		var hide = <?php if ($hide) {echo 'true';} else {echo 'false';} ?>;
1499

    
1500
		hideClass('adnlopts', !showadvopts && !hide);
1501
		hideInput('btnadvopts', hide);
1502

    
1503
		showadvopts = !showadvopts;
1504
	}
1505

    
1506
	$('#btnadvopts').prop('type', 'button');
1507

    
1508
	$('#btnadvopts').click(function(event) {
1509
		show_advopts();
1510
	});
1511

    
1512
	// ---------- On initial page load ------------------------------------------------------------
1513

    
1514
	show_advdns();
1515
	show_advmac();
1516
	show_advntp();
1517
	show_advtftp();
1518
	show_advldap();
1519
	show_advopts();
1520

    
1521
	// Suppress "Delete row" button if there are fewer than two rows
1522
	checkLastRow();
1523
});
1524
//]]>
1525
</script>
1526

    
1527
<?php include("foot.inc");
(118-118/229)