Project

General

Profile

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

    
58
##|+PRIV
59
##|*IDENT=page-services-dhcpserver
60
##|*NAME=Services: DHCP Server
61
##|*DESCR=Allow access to the 'Services: DHCP Server' page.
62
##|*MATCH=services_dhcp.php*
63
##|-PRIV
64

    
65
require_once("guiconfig.inc");
66
require_once("filter.inc");
67
require_once('rrd.inc');
68
require_once("shaper.inc");
69

    
70
if (!$g['services_dhcp_server_enable']) {
71
	header("Location: /");
72
	exit;
73
}
74

    
75
$if = $_GET['if'];
76
if (!empty($_POST['if'])) {
77
	$if = $_POST['if'];
78
}
79

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

    
90
$iflist = get_configured_interface_with_descr();
91

    
92
/* set the starting interface */
93
if (!$if || !isset($iflist[$if])) {
94
	$found_starting_if = false;
95
	// First look for an interface with DHCP already enabled.
96
	foreach ($iflist as $ifent => $ifname) {
97
		$oc = $config['interfaces'][$ifent];
98
		if (is_array($config['dhcpd'][$ifent]) && isset($config['dhcpd'][$ifent]['enable']) && (is_ipaddrv4($oc['ipaddr']))) {
99
			$if = $ifent;
100
			$found_starting_if = true;
101
			break;
102
		}
103
	}
104

    
105
	// If there is no DHCP-enabled interface and LAN is a candidate, then choose LAN.
106
	if (!$found_starting_if && isset($iflist['lan']) && is_ipaddrv4($config['interfaces']['lan']['ipaddr'])) {
107
		$if = 'lan';
108
		$found_starting_if = true;
109
	}
110

    
111
	// At the last select whatever can be found.
112
	if (!$found_starting_if) {
113
		foreach ($iflist as $ifent => $ifname) {
114
			$oc = $config['interfaces'][$ifent];
115
			if ((is_array($config['dhcpd'][$ifent]) && !isset($config['dhcpd'][$ifent]['enable']) && (!is_ipaddrv4($oc['ipaddr']))) ||
116
				(!is_array($config['dhcpd'][$ifent]) && (!is_ipaddrv4($oc['ipaddr'])))) {
117
				continue;
118
			}
119

    
120
			$if = $ifent;
121
			break;
122
		}
123
	}
124
}
125

    
126
$act = $_GET['act'];
127
if (!empty($_POST['act'])) {
128
	$act = $_POST['act'];
129
}
130

    
131
$a_pools = array();
132

    
133
if (is_array($config['dhcpd'][$if])) {
134
	$pool = $_GET['pool'];
135
	if (is_numeric($_POST['pool'])) {
136
		$pool = $_POST['pool'];
137
	}
138

    
139
	// If we have a pool but no interface name, that's not valid. Redirect away.
140
	if (is_numeric($pool) && empty($if)) {
141
		header("Location: services_dhcp.php");
142
		exit;
143
	}
144

    
145
	if (!is_array($config['dhcpd'][$if]['pool'])) {
146
		$config['dhcpd'][$if]['pool'] = array();
147
	}
148

    
149
	$a_pools = &$config['dhcpd'][$if]['pool'];
150

    
151
	if (is_numeric($pool) && $a_pools[$pool]) {
152
		$dhcpdconf = &$a_pools[$pool];
153
	} elseif ($act == "newpool") {
154
		$dhcpdconf = array();
155
	} else {
156
		$dhcpdconf = &$config['dhcpd'][$if];
157
	}
158

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

    
163
	$a_maps = &$config['dhcpd'][$if]['staticmap'];
164
}
165
if (is_array($dhcpdconf)) {
166
	// Global Options
167
	if (!is_numeric($pool) && !($act == "newpool")) {
168
		$pconfig['enable'] = isset($dhcpdconf['enable']);
169
		$pconfig['staticarp'] = isset($dhcpdconf['staticarp']);
170
		// No reason to specify this per-pool, per the dhcpd.conf man page it needs to be in every
171
		//	 pool and should be specified in every pool both nodes share, so we'll treat it as global
172
		$pconfig['failover_peerip'] = $dhcpdconf['failover_peerip'];
173

    
174
		// dhcpleaseinlocaltime is global to all interfaces. So if it is selected on any interface,
175
		// then show it true/checked.
176
		foreach ($config['dhcpd'] as $dhcpdifitem) {
177
			$dhcpleaseinlocaltime = $dhcpdifitem['dhcpleaseinlocaltime'];
178
			if ($dhcpleaseinlocaltime) {
179
				break;
180
			}
181
		}
182

    
183
		$pconfig['dhcpleaseinlocaltime'] = $dhcpleaseinlocaltime;
184
	} else {
185
		// Options that exist only in pools
186
		$pconfig['descr'] = $dhcpdconf['descr'];
187
	}
188

    
189
	// Options that can be global or per-pool.
190
	if (is_array($dhcpdconf['range'])) {
191
		$pconfig['range_from'] = $dhcpdconf['range']['from'];
192
		$pconfig['range_to'] = $dhcpdconf['range']['to'];
193
	}
194

    
195
	$pconfig['deftime'] = $dhcpdconf['defaultleasetime'];
196
	$pconfig['maxtime'] = $dhcpdconf['maxleasetime'];
197
	$pconfig['gateway'] = $dhcpdconf['gateway'];
198
	$pconfig['domain'] = $dhcpdconf['domain'];
199
	$pconfig['domainsearchlist'] = $dhcpdconf['domainsearchlist'];
200
	list($pconfig['wins1'], $pconfig['wins2']) = $dhcpdconf['winsserver'];
201
	list($pconfig['dns1'], $pconfig['dns2'], $pconfig['dns3'], $pconfig['dns4']) = $dhcpdconf['dnsserver'];
202
	$pconfig['ignorebootp'] = isset($dhcpdconf['ignorebootp']);
203
	$pconfig['denyunknown'] = isset($dhcpdconf['denyunknown']);
204
	$pconfig['nonak'] = isset($dhcpdconf['nonak']);
205
	$pconfig['ddnsdomain'] = $dhcpdconf['ddnsdomain'];
206
	$pconfig['ddnsdomainprimary'] = $dhcpdconf['ddnsdomainprimary'];
207
	$pconfig['ddnsdomainkeyname'] = $dhcpdconf['ddnsdomainkeyname'];
208
	$pconfig['ddnsdomainkey'] = $dhcpdconf['ddnsdomainkey'];
209
	$pconfig['ddnsupdate'] = isset($dhcpdconf['ddnsupdate']);
210
	$pconfig['mac_allow'] = $dhcpdconf['mac_allow'];
211
	$pconfig['mac_deny'] = $dhcpdconf['mac_deny'];
212
	list($pconfig['ntp1'], $pconfig['ntp2']) = $dhcpdconf['ntpserver'];
213
	$pconfig['tftp'] = $dhcpdconf['tftp'];
214
	$pconfig['ldap'] = $dhcpdconf['ldap'];
215
	$pconfig['netboot'] = isset($dhcpdconf['netboot']);
216
	$pconfig['nextserver'] = $dhcpdconf['nextserver'];
217
	$pconfig['filename'] = $dhcpdconf['filename'];
218
	$pconfig['filename32'] = $dhcpdconf['filename32'];
219
	$pconfig['filename64'] = $dhcpdconf['filename64'];
220
	$pconfig['rootpath'] = $dhcpdconf['rootpath'];
221
	$pconfig['netmask'] = $dhcpdconf['netmask'];
222
	$pconfig['numberoptions'] = $dhcpdconf['numberoptions'];
223
	$pconfig['statsgraph'] = $dhcpdconf['statsgraph'];
224
}
225

    
226
$ifcfgip = $config['interfaces'][$if]['ipaddr'];
227
$ifcfgsn = $config['interfaces'][$if]['subnet'];
228

    
229
$subnet_start = gen_subnetv4($ifcfgip, $ifcfgsn);
230
$subnet_end = gen_subnetv4_max($ifcfgip, $ifcfgsn);
231

    
232
function validate_partial_mac_list($maclist) {
233
	$macs = explode(',', $maclist);
234

    
235
	// Loop through and look for invalid MACs.
236
	foreach ($macs as $mac) {
237
		if (!is_macaddr($mac, true)) {
238
			return false;
239
		}
240
	}
241

    
242
	return true;
243
}
244

    
245
if (isset($_POST['save'])) {
246

    
247
	unset($input_errors);
248

    
249
	$pconfig = $_POST;
250

    
251
	$numberoptions = array();
252
	for ($x = 0; $x < 99; $x++) {
253
		if (isset($_POST["number{$x}"]) && ctype_digit($_POST["number{$x}"])) {
254
			$numbervalue = array();
255
			$numbervalue['number'] = htmlspecialchars($_POST["number{$x}"]);
256
			$numbervalue['type'] = htmlspecialchars($_POST["itemtype{$x}"]);
257
			$numbervalue['value'] = base64_encode($_POST["value{$x}"]);
258
			$numberoptions['item'][] = $numbervalue;
259
		}
260
	}
261

    
262
	// Reload the new pconfig variable that the form uses.
263
	$pconfig['numberoptions'] = $numberoptions;
264

    
265
	/* input validation */
266
	if ($_POST['enable'] || is_numeric($pool) || $act == "newpool") {
267
		$reqdfields = explode(" ", "range_from range_to");
268
		$reqdfieldsn = array(gettext("Range begin"), gettext("Range end"));
269

    
270
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
271

    
272
		if (($_POST['nonak']) && !empty($_POST['failover_peerip'])) {
273
			$input_errors[] = gettext("Ignore Denied Clients may not be used when a Failover Peer IP is defined.");
274
		}
275

    
276
		if (($_POST['range_from'] && !is_ipaddrv4($_POST['range_from']))) {
277
			$input_errors[] = gettext("A valid range must be specified.");
278
		}
279
		if (($_POST['range_to'] && !is_ipaddrv4($_POST['range_to']))) {
280
			$input_errors[] = gettext("A valid range must be specified.");
281
		}
282
		if (($_POST['gateway'] && $_POST['gateway'] != "none" && !is_ipaddrv4($_POST['gateway']))) {
283
			$input_errors[] = gettext("A valid IP address must be specified for the gateway.");
284
		}
285
		if (($_POST['wins1'] && !is_ipaddrv4($_POST['wins1'])) || ($_POST['wins2'] && !is_ipaddrv4($_POST['wins2']))) {
286
			$input_errors[] = gettext("A valid IP address must be specified for the primary/secondary WINS servers.");
287
		}
288
		$parent_ip = get_interface_ip($_POST['if']);
289
		if (is_ipaddrv4($parent_ip) && $_POST['gateway'] && $_POST['gateway'] != "none") {
290
			$parent_sn = get_interface_subnet($_POST['if']);
291
			if (!ip_in_subnet($_POST['gateway'], gen_subnet($parent_ip, $parent_sn) . "/" . $parent_sn) && !ip_in_interface_alias_subnet($_POST['if'], $_POST['gateway'])) {
292
				$input_errors[] = sprintf(gettext("The gateway address %s does not lie within the chosen interface's subnet."), $_POST['gateway']);
293
			}
294
		}
295

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

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

    
304
		if (isset($config['captiveportal']) && is_array($config['captiveportal'])) {
305
			$deftime = 7200; // Default value if it's empty
306
			if (is_numeric($_POST['deftime'])) {
307
				$deftime = $_POST['deftime'];
308
			}
309

    
310
			foreach ($config['captiveportal'] as $cpZone => $cpdata) {
311
				if (!isset($cpdata['enable'])) {
312
					continue;
313
				}
314
				if (!isset($cpdata['timeout']) || !is_numeric($cpdata['timeout'])) {
315
					continue;
316
				}
317
				$cp_ifs = explode(',', $cpdata['interface']);
318
				if (!in_array($if, $cp_ifs)) {
319
					continue;
320
				}
321
				if ($cpdata['timeout'] > $deftime) {
322
					$input_errors[] = sprintf(gettext(
323
						'The Captive Portal zone (%1$s) has Hard Timeout parameter set to a value bigger than Default lease time (%2$s).'), $cpZone, $deftime);
324
				}
325
			}
326
		}
327

    
328
		if ($_POST['maxtime'] && (!is_numeric($_POST['maxtime']) || ($_POST['maxtime'] < 60) || ($_POST['maxtime'] <= $_POST['deftime']))) {
329
			$input_errors[] = gettext("The maximum lease time must be at least 60 seconds and higher than the default lease time.");
330
		}
331
		if (($_POST['ddnsdomain'] && !is_domain($_POST['ddnsdomain']))) {
332
			$input_errors[] = gettext("A valid domain name must be specified for the dynamic DNS registration.");
333
		}
334
		if (($_POST['ddnsdomain'] && !is_ipaddrv4($_POST['ddnsdomainprimary']))) {
335
			$input_errors[] = gettext("A valid primary domain name server IP address must be specified for the dynamic domain name.");
336
		}
337
		if (($_POST['ddnsdomainkey'] && !$_POST['ddnsdomainkeyname']) ||
338
		    ($_POST['ddnsdomainkeyname'] && !$_POST['ddnsdomainkey'])) {
339
			$input_errors[] = gettext("Both a valid domain key and key name must be specified.");
340
		}
341
		if ($_POST['domainsearchlist']) {
342
			$domain_array = preg_split("/[ ;]+/", $_POST['domainsearchlist']);
343
			foreach ($domain_array as $curdomain) {
344
				if (!is_domain($curdomain)) {
345
					$input_errors[] = gettext("A valid domain search list must be specified.");
346
					break;
347
				}
348
			}
349
		}
350

    
351
		// Validate MACs
352
		if (!empty($_POST['mac_allow']) && !validate_partial_mac_list($_POST['mac_allow'])) {
353
			$input_errors[] = gettext("If a mac allow list is specified, it must contain only valid partial MAC addresses.");
354
		}
355
		if (!empty($_POST['mac_deny']) && !validate_partial_mac_list($_POST['mac_deny'])) {
356
			$input_errors[] = gettext("If a mac deny list is specified, it must contain only valid partial MAC addresses.");
357
		}
358

    
359
		if (($_POST['ntp1'] && (!is_ipaddrv4($_POST['ntp1']) && !is_hostname($_POST['ntp1']))) || ($_POST['ntp2'] && (!is_ipaddrv4($_POST['ntp2']) && !is_hostname($_POST['ntp2'])))) {
360
			$input_errors[] = gettext("A valid IP address or hostname must be specified for the primary/secondary NTP servers.");
361
		}
362
		if (($_POST['domain'] && !is_domain($_POST['domain']))) {
363
			$input_errors[] = gettext("A valid domain name must be specified for the DNS domain.");
364
		}
365
		if ($_POST['tftp'] && !is_ipaddrv4($_POST['tftp']) && !is_domain($_POST['tftp']) && !filter_var($_POST['tftp'], FILTER_VALIDATE_URL)) {
366
			$input_errors[] = gettext("A valid IP address, hostname or URL must be specified for the TFTP server.");
367
		}
368
		if (($_POST['nextserver'] && !is_ipaddrv4($_POST['nextserver']))) {
369
			$input_errors[] = gettext("A valid IP address must be specified for the network boot server.");
370
		}
371

    
372
		if (gen_subnet($ifcfgip, $ifcfgsn) == $_POST['range_from']) {
373
			$input_errors[] = gettext("The network address cannot be used in the starting subnet range.");
374
		}
375
		if (gen_subnet_max($ifcfgip, $ifcfgsn) == $_POST['range_to']) {
376
			$input_errors[] = gettext("The broadcast address cannot be used in the ending subnet range.");
377
		}
378

    
379
		// Disallow a range that includes the virtualip
380
		if (is_array($config['virtualip']['vip'])) {
381
			foreach ($config['virtualip']['vip'] as $vip) {
382
				if ($vip['interface'] == $if) {
383
					if ($vip['subnet'] && is_inrange_v4($vip['subnet'], $_POST['range_from'], $_POST['range_to'])) {
384
						$input_errors[] = sprintf(gettext("The subnet range cannot overlap with virtual IP address %s."), $vip['subnet']);
385
					}
386
				}
387
			}
388
		}
389

    
390
		$noip = false;
391
		if (is_array($a_maps)) {
392
			foreach ($a_maps as $map) {
393
				if (empty($map['ipaddr'])) {
394
					$noip = true;
395
				}
396
			}
397
		}
398

    
399
		if ($_POST['staticarp'] && $noip) {
400
			$input_errors[] = gettext("Cannot enable static ARP when there are static map entries without IP addresses. Ensure all static maps have IP addresses and try again.");
401
		}
402

    
403
		if (is_array($pconfig['numberoptions']['item'])) {
404
			foreach ($pconfig['numberoptions']['item'] as $numberoption) {
405
				$numberoption_value = base64_decode($numberoption['value']);
406
				if ($numberoption['type'] == 'text' && strstr($numberoption_value, '"')) {
407
					$input_errors[] = gettext("Text type cannot include quotation marks.");
408
				} else if ($numberoption['type'] == 'string' && !preg_match('/^"[^"]*"$/', $numberoption_value) && !preg_match('/^[0-9a-f]{2}(?:\:[0-9a-f]{2})*$/i', $numberoption_value)) {
409
					$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");
410
				} else if ($numberoption['type'] == 'boolean' && $numberoption_value != 'true' && $numberoption_value != 'false' && $numberoption_value != 'on' && $numberoption_value != 'off') {
411
					$input_errors[] = gettext("Boolean type must be true, false, on, or off.");
412
				} else if ($numberoption['type'] == 'unsigned integer 8' && (!is_numeric($numberoption_value) || $numberoption_value < 0 || $numberoption_value > 255)) {
413
					$input_errors[] = gettext("Unsigned 8-bit integer type must be a number in the range 0 to 255.");
414
				} else if ($numberoption['type'] == 'unsigned integer 16' && (!is_numeric($numberoption_value) || $numberoption_value < 0 || $numberoption_value > 65535)) {
415
					$input_errors[] = gettext("Unsigned 16-bit integer type must be a number in the range 0 to 65535.");
416
				} else if ($numberoption['type'] == 'unsigned integer 32' && (!is_numeric($numberoption_value) || $numberoption_value < 0 || $numberoption_value > 4294967295)) {
417
					$input_errors[] = gettext("Unsigned 32-bit integer type must be a number in the range 0 to 4294967295.");
418
				} else if ($numberoption['type'] == 'signed integer 8' && (!is_numeric($numberoption_value) || $numberoption_value < -128 || $numberoption_value > 127)) {
419
					$input_errors[] = gettext("Signed 8-bit integer type must be a number in the range -128 to 127.");
420
				} else if ($numberoption['type'] == 'signed integer 16' && (!is_numeric($numberoption_value) || $numberoption_value < -32768 || $numberoption_value > 32767)) {
421
					$input_errors[] = gettext("Signed 16-bit integer type must be a number in the range -32768 to 32767.");
422
				} else if ($numberoption['type'] == 'signed integer 32' && (!is_numeric($numberoption_value) || $numberoption_value < -2147483648 || $numberoption_value > 2147483647)) {
423
					$input_errors[] = gettext("Signed 32-bit integer type must be a number in the range -2147483648 to 2147483647.");
424
				} else if ($numberoption['type'] == 'ip-address' && !is_ipaddrv4($numberoption_value) && !is_hostname($numberoption_value)) {
425
					$input_errors[] = gettext("IP address or host type must be an IP address or host name.");
426
				}
427
			}
428
		}
429

    
430
		if (!$input_errors) {
431
			/* make sure the range lies within the current subnet */
432
			if (ip_greater_than($_POST['range_from'], $_POST['range_to'])) {
433
				$input_errors[] = gettext("The range is invalid (first element higher than second element).");
434
			}
435

    
436
			if (!is_inrange_v4($_POST['range_from'], $subnet_start, $subnet_end) ||
437
			    !is_inrange_v4($_POST['range_to'], $subnet_start, $subnet_end)) {
438
				$input_errors[] = gettext("The specified range lies outside of the current subnet.");
439
			}
440

    
441
			if (is_numeric($pool) || ($act == "newpool")) {
442
				if (is_inrange_v4($_POST['range_from'],
443
				    $config['dhcpd'][$if]['range']['from'],
444
				    $config['dhcpd'][$if]['range']['to']) ||
445
				    is_inrange_v4($_POST['range_to'],
446
				    $config['dhcpd'][$if]['range']['from'],
447
				    $config['dhcpd'][$if]['range']['to'])) {
448
					$input_errors[] = gettext("The specified range must not be within the DHCP range for this interface.");
449
				}
450
			}
451

    
452
			foreach ($a_pools as $id => $p) {
453
				if (is_numeric($pool) && ($id == $pool)) {
454
					continue;
455
				}
456

    
457
				if (is_inrange_v4($_POST['range_from'],
458
				    $p['range']['from'], $p['range']['to']) ||
459
				    is_inrange_v4($_POST['range_to'],
460
				    $p['range']['from'], $p['range']['to'])) {
461
					$input_errors[] = gettext("The specified range must not be within the range configured on a DHCP pool for this interface.");
462
					break;
463
				}
464
			}
465

    
466
			/* make sure that the DHCP Relay isn't enabled on this interface */
467
			if (isset($config['dhcrelay']['enable']) && (stristr($config['dhcrelay']['interface'], $if) !== false)) {
468
				$input_errors[] = sprintf(gettext("The DHCP relay on the %s interface must be disabled before enabling the DHCP server."), $iflist[$if]);
469
			}
470

    
471
			if (is_array($a_maps)) {
472
				foreach ($a_maps as $map) {
473
					if (empty($map['ipaddr'])) {
474
						continue;
475
					}
476
					if (is_inrange_v4($map['ipaddr'], $_POST['range_from'], $_POST['range_to'])) {
477
						$input_errors[] = sprintf(gettext("The DHCP range cannot overlap any static DHCP mappings."));
478
						break;
479
					}
480
				}
481
			}
482
		}
483
	}
484

    
485
	if (!$input_errors) {
486
		if (!is_numeric($pool)) {
487
			if ($act == "newpool") {
488
				$dhcpdconf = array();
489
			} else {
490
				if (!is_array($config['dhcpd'][$if])) {
491
					$config['dhcpd'][$if] = array();
492
				}
493
				$dhcpdconf = $config['dhcpd'][$if];
494
			}
495
		} else {
496
			if (is_array($a_pools[$pool])) {
497
				$dhcpdconf = $a_pools[$pool];
498
			} else {
499
				// Someone specified a pool but it doesn't exist. Punt.
500
				header("Location: services_dhcp.php");
501
				exit;
502
			}
503
		}
504
		if (!is_array($dhcpdconf['range'])) {
505
			$dhcpdconf['range'] = array();
506
		}
507

    
508
		$dhcpd_enable_changed = false;
509

    
510
		// Global Options
511
		if (!is_numeric($pool) && !($act == "newpool")) {
512
			$old_dhcpd_enable = isset($dhcpdconf['enable']);
513
			$new_dhcpd_enable = ($_POST['enable']) ? true : false;
514
			if ($old_dhcpd_enable != $new_dhcpd_enable) {
515
				/* DHCP has been enabled or disabled. The pf ruleset will need to be rebuilt to allow or disallow DHCP. */
516
				$dhcpd_enable_changed = true;
517
			}
518

    
519
			$dhcpdconf['enable'] = $new_dhcpd_enable;
520
			$dhcpdconf['staticarp'] = ($_POST['staticarp']) ? true : false;
521
			$previous = $dhcpdconf['failover_peerip'];
522
			if ($previous != $_POST['failover_peerip']) {
523
				mwexec("/bin/rm -rf /var/dhcpd/var/db/*");
524
			}
525

    
526
			$dhcpdconf['failover_peerip'] = $_POST['failover_peerip'];
527
			// dhcpleaseinlocaltime is global to all interfaces. So update the setting on all interfaces.
528
			foreach ($config['dhcpd'] as &$dhcpdifitem) {
529
				$dhcpdifitem['dhcpleaseinlocaltime'] = $_POST['dhcpleaseinlocaltime'];
530
			}
531
		} else {
532
			// Options that exist only in pools
533
			$dhcpdconf['descr'] = $_POST['descr'];
534
		}
535

    
536
		// Options that can be global or per-pool.
537
		$dhcpdconf['range']['from'] = $_POST['range_from'];
538
		$dhcpdconf['range']['to'] = $_POST['range_to'];
539
		$dhcpdconf['defaultleasetime'] = $_POST['deftime'];
540
		$dhcpdconf['maxleasetime'] = $_POST['maxtime'];
541
		$dhcpdconf['netmask'] = $_POST['netmask'];
542

    
543
		unset($dhcpdconf['winsserver']);
544
		if ($_POST['wins1']) {
545
			$dhcpdconf['winsserver'][] = $_POST['wins1'];
546
		}
547
		if ($_POST['wins2']) {
548
			$dhcpdconf['winsserver'][] = $_POST['wins2'];
549
		}
550

    
551
		unset($dhcpdconf['dnsserver']);
552
		if ($_POST['dns1']) {
553
			$dhcpdconf['dnsserver'][] = $_POST['dns1'];
554
		}
555
		if ($_POST['dns2']) {
556
			$dhcpdconf['dnsserver'][] = $_POST['dns2'];
557
		}
558
		if ($_POST['dns3']) {
559
			$dhcpdconf['dnsserver'][] = $_POST['dns3'];
560
		}
561
		if ($_POST['dns4']) {
562
			$dhcpdconf['dnsserver'][] = $_POST['dns4'];
563
		}
564

    
565
		$dhcpdconf['gateway'] = $_POST['gateway'];
566
		$dhcpdconf['domain'] = $_POST['domain'];
567
		$dhcpdconf['domainsearchlist'] = $_POST['domainsearchlist'];
568
		$dhcpdconf['ignorebootp'] = ($_POST['ignorebootp']) ? true : false;
569
		$dhcpdconf['denyunknown'] = ($_POST['denyunknown']) ? true : false;
570
		$dhcpdconf['nonak'] = ($_POST['nonak']) ? true : false;
571
		$dhcpdconf['ddnsdomain'] = $_POST['ddnsdomain'];
572
		$dhcpdconf['ddnsdomainprimary'] = $_POST['ddnsdomainprimary'];
573
		$dhcpdconf['ddnsdomainkeyname'] = $_POST['ddnsdomainkeyname'];
574
		$dhcpdconf['ddnsdomainkey'] = $_POST['ddnsdomainkey'];
575
		$dhcpdconf['ddnsupdate'] = ($_POST['ddnsupdate']) ? true : false;
576
		$dhcpdconf['mac_allow'] = $_POST['mac_allow'];
577
		$dhcpdconf['mac_deny'] = $_POST['mac_deny'];
578

    
579
		unset($dhcpdconf['ntpserver']);
580
		if ($_POST['ntp1']) {
581
			$dhcpdconf['ntpserver'][] = $_POST['ntp1'];
582
		}
583
		if ($_POST['ntp2']) {
584
			$dhcpdconf['ntpserver'][] = $_POST['ntp2'];
585
		}
586

    
587
		$dhcpdconf['tftp'] = $_POST['tftp'];
588
		$dhcpdconf['ldap'] = $_POST['ldap'];
589
		$dhcpdconf['netboot'] = ($_POST['netboot']) ? true : false;
590
		$dhcpdconf['nextserver'] = $_POST['nextserver'];
591
		$dhcpdconf['filename'] = $_POST['filename'];
592
		$dhcpdconf['filename32'] = $_POST['filename32'];
593
		$dhcpdconf['filename64'] = $_POST['filename64'];
594
		$dhcpdconf['rootpath'] = $_POST['rootpath'];
595
		unset($dhcpdconf['statsgraph']);
596
		if ($_POST['statsgraph']) {
597
			$dhcpdconf['statsgraph'] = $_POST['statsgraph'];
598
			enable_rrd_graphing();
599
		}
600

    
601
		// Handle the custom options rowhelper
602
		if (isset($dhcpdconf['numberoptions']['item'])) {
603
			unset($dhcpdconf['numberoptions']['item']);
604
		}
605

    
606
		$dhcpdconf['numberoptions'] = $numberoptions;
607

    
608
		if (is_numeric($pool) && is_array($a_pools[$pool])) {
609
			$a_pools[$pool] = $dhcpdconf;
610
		} elseif ($act == "newpool") {
611
			$a_pools[] = $dhcpdconf;
612
		} else {
613
			$config['dhcpd'][$if] = $dhcpdconf;
614
		}
615

    
616
		write_config();
617
	}
618
}
619

    
620
if ((isset($_POST['save']) || isset($_POST['apply'])) && (!$input_errors)) {
621
	$retval = 0;
622
	$retvaldhcp = 0;
623
	$retvaldns = 0;
624
	/* dnsmasq_configure calls dhcpd_configure */
625
	/* no need to restart dhcpd twice */
626
	if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic']))	{
627
		$retvaldns = services_dnsmasq_configure();
628
		if ($retvaldns == 0) {
629
			clear_subsystem_dirty('hosts');
630
			clear_subsystem_dirty('staticmaps');
631
		}
632
	} else if (isset($config['unbound']['enable']) && isset($config['unbound']['regdhcpstatic'])) {
633
		$retvaldns = services_unbound_configure();
634
		if ($retvaldns == 0) {
635
			clear_subsystem_dirty('unbound');
636
			clear_subsystem_dirty('hosts');
637
			clear_subsystem_dirty('staticmaps');
638
		}
639
	} else {
640
		$retvaldhcp = services_dhcpd_configure();
641
		if ($retvaldhcp == 0) {
642
			clear_subsystem_dirty('staticmaps');
643
		}
644
	}
645
	if ($dhcpd_enable_changed) {
646
		$retvalfc = filter_configure();
647
	}
648

    
649
	if ($retvaldhcp == 1 || $retvaldns == 1 || $retvalfc == 1) {
650
		$retval = 1;
651
	}
652

    
653
	$savemsg = get_std_save_message($retval);
654
}
655

    
656
if ($act == "delpool") {
657
	if ($a_pools[$_GET['id']]) {
658
		unset($a_pools[$_GET['id']]);
659
		write_config();
660
		header("Location: services_dhcp.php?if={$if}");
661
		exit;
662
	}
663
}
664

    
665
if ($act == "del") {
666
	if ($a_maps[$_GET['id']]) {
667
		unset($a_maps[$_GET['id']]);
668
		write_config();
669
		if (isset($config['dhcpd'][$if]['enable'])) {
670
			mark_subsystem_dirty('staticmaps');
671
			if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic'])) {
672
				mark_subsystem_dirty('hosts');
673
			}
674
		}
675

    
676
		header("Location: services_dhcp.php?if={$if}");
677
		exit;
678
	}
679
}
680

    
681
// Build an HTML table that can be inserted into a Form_StaticText element
682
function build_pooltable() {
683
	global $a_pools, $if;
684

    
685
	$pooltbl =	'<div class="table-responsive">';
686
	$pooltbl .=		'<table class="table table-striped table-hover table-condensed">';
687
	$pooltbl .=			'<thead>';
688
	$pooltbl .=				'<tr>';
689
	$pooltbl .=					'<th>' . gettext("Pool Start") . '</th>';
690
	$pooltbl .=					'<th>' . gettext("Pool End") . '</th>';
691
	$pooltbl .=					'<th>' . gettext("Description") . '</th>';
692
	$pooltbl .=					'<th>' . gettext("Actions") . '</th>';
693
	$pooltbl .=				'</tr>';
694
	$pooltbl .=			'</thead>';
695
	$pooltbl .=			'<tbody>';
696

    
697
	if (is_array($a_pools)) {
698
		$i = 0;
699
		foreach ($a_pools as $poolent) {
700
			if (!empty($poolent['range']['from']) && !empty($poolent['range']['to'])) {
701
				$pooltbl .= '<tr>';
702
				$pooltbl .= '<td ondblclick="document.location=\'services_dhcp.php?if=' . htmlspecialchars($if) . '&pool=' . $i . '\';">' .
703
							htmlspecialchars($poolent['range']['from']) . '</td>';
704

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

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

    
711
				$pooltbl .= '<td><a class="fa fa-pencil" title="'. gettext("Edit pool") . '" href="services_dhcp.php?if=' . htmlspecialchars($if) . '&pool=' . $i . '"></a>';
712

    
713
				$pooltbl .= ' <a class="fa fa-trash" title="'. gettext("Delete pool") . '" href="services_dhcp.php?if=' . htmlspecialchars($if) . '&act=delpool&id=' . $i . '"></a></td>';
714
				$pooltbl .= '</tr>';
715
			}
716
		$i++;
717
		}
718
	}
719

    
720
	$pooltbl .=			'</tbody>';
721
	$pooltbl .=		'</table>';
722
	$pooltbl .= '</div>';
723

    
724
	return($pooltbl);
725
}
726

    
727
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
728

    
729
if (!empty($if) && !isset($config['dhcrelay']['enable']) && isset($iflist[$if])) {
730
	$pgtitle[] = $iflist[$if];
731
}
732
$shortcut_section = "dhcp";
733

    
734
include("head.inc");
735

    
736
if ($input_errors) {
737
	print_input_errors($input_errors);
738
}
739

    
740
if ($savemsg) {
741
	print_info_box($savemsg, 'success');
742
}
743

    
744
if (isset($config['dhcrelay']['enable'])) {
745
	print_info_box(gettext("DHCP Relay is currently enabled. Cannot enable the DHCP Server service while the DHCP Relay is enabled on any interface."));
746
	include("foot.inc");
747
	exit;
748
}
749

    
750
if (is_subsystem_dirty('staticmaps')) {
751
	print_apply_box(gettext("The static mapping configuration has been changed.") . "<br />" . gettext("The changes must be applied for them to take effect."));
752
}
753

    
754
/* active tabs */
755
$tab_array = array();
756
$tabscounter = 0;
757
$i = 0;
758

    
759
foreach ($iflist as $ifent => $ifname) {
760
	$oc = $config['interfaces'][$ifent];
761
	if ((is_array($config['dhcpd'][$ifent]) && !isset($config['dhcpd'][$ifent]['enable']) && (!is_ipaddrv4($oc['ipaddr']))) ||
762
	    (!is_array($config['dhcpd'][$ifent]) && (!is_ipaddrv4($oc['ipaddr'])))) {
763
		continue;
764
	}
765

    
766
	if ($ifent == $if) {
767
		$active = true;
768
	} else {
769
		$active = false;
770
	}
771

    
772
	$tab_array[] = array($ifname, $active, "services_dhcp.php?if={$ifent}");
773
	$tabscounter++;
774
}
775

    
776
if ($tabscounter == 0) {
777
	print_info_box(gettext("The DHCP Server can only be enabled on interfaces configured with a static IPv4 address. This system has none."));
778
	include("foot.inc");
779
	exit;
780
}
781

    
782
display_top_tabs($tab_array);
783

    
784
$form = new Form();
785

    
786
$section = new Form_Section('General Options');
787

    
788
if (!is_numeric($pool) && !($act == "newpool")) {
789
	$section->addInput(new Form_Checkbox(
790
		'enable',
791
		'Enable',
792
		sprintf(gettext("Enable DHCP server on %s interface"), htmlspecialchars($iflist[$if])),
793
		$pconfig['enable']
794
	));
795
} else {
796
	print_info_box(gettext('Editing pool-specific options. To return to the Interface, click its tab above.'), 'info', false);
797
}
798

    
799
$section->addInput(new Form_Checkbox(
800
	'ignorebootp',
801
	'BOOTP',
802
	'Ignore BOOTP queries',
803
	$pconfig['ignorebootp']
804
));
805

    
806
$section->addInput(new Form_Checkbox(
807
	'denyunknown',
808
	'Deny unknown clients',
809
	'Only the clients defined below will get DHCP leases from this server.',
810
	$pconfig['denyunknown']
811
));
812

    
813
$section->addInput(new Form_Checkbox(
814
	'nonak',
815
	'Ignore denied clients',
816
	'Denied clients will be ignored rather than rejected.',
817
	$pconfig['nonak']
818
))->setHelp("This option is not compatible with failover and cannot be enabled when a Failover Peer IP address is configured.");
819

    
820

    
821
if (is_numeric($pool) || ($act == "newpool")) {
822
	$section->addInput(new Form_Input(
823
		'descr',
824
		'Pool Description',
825
		'text',
826
		$pconfig['descr']
827
	));
828
}
829

    
830
$section->addInput(new Form_StaticText(
831
	'Subnet',
832
	gen_subnet($ifcfgip, $ifcfgsn)
833
));
834

    
835
$section->addInput(new Form_StaticText(
836
	'Subnet mask',
837
	gen_subnet_mask($ifcfgsn)
838
));
839

    
840
// Compose a string to display the required address ranges
841
$rangestr = ip_after($subnet_start) . ' - ' . ip_before($subnet_end);
842

    
843
if (is_numeric($pool) || ($act == "newpool")) {
844
	$rangestr .= '<br />' . gettext('In-use DHCP Pool Ranges:');
845
	if (is_array($config['dhcpd'][$if]['range'])) {
846
		$rangestr .= '<br />' . $config['dhcpd'][$if]['range']['from'] . ' - ' . $config['dhcpd'][$if]['range']['to'];
847
	}
848

    
849
	foreach ($a_pools as $p) {
850
		if (is_array($p['range'])) {
851
			$rangestr .= '<br />' . $p['range']['from'] . ' - ' . $p['range']['to'];
852
		}
853
	}
854
}
855

    
856
$section->addInput(new Form_StaticText(
857
	'Available range',
858
	$rangestr
859
));
860

    
861
if ($is_olsr_enabled) {
862
	$section->addInput(new Form_Select(
863
		'netmask',
864
		'Subnet mask',
865
		$pconfig['netmask'],
866
		array_combine(range(32, 1, -1), range(32, 1, -1))
867
	));
868
}
869

    
870
$group = new Form_Group('Range');
871

    
872
$group->add(new Form_IpAddress(
873
	'range_from',
874
	null,
875
	$pconfig['range_from']
876
))->setHelp('From');
877

    
878
$group->add(new Form_IpAddress(
879
	'range_to',
880
	null,
881
	$pconfig['range_to']
882
))->setHelp('To');
883

    
884
$section->add($group);
885

    
886
$form->add($section);
887

    
888
if (!is_numeric($pool) && !($act == "newpool")) {
889
	$section = new Form_Section('Additional Pools');
890

    
891
	$btnaddpool = new Form_Button(
892
		'btnaddpool',
893
		'Add pool',
894
		'services_dhcp.php?if=' . htmlspecialchars($if) . '&act=newpool',
895
		'fa-plus'
896
	);
897
	$btnaddpool->addClass('btn-success');
898

    
899
	$section->addInput(new Form_StaticText(
900
		'Add',
901
		$btnaddpool
902
	))->setHelp('If additional pools of addresses are needed inside of this subnet outside the above Range, they may be specified here.');
903

    
904
	if (is_array($a_pools)) {
905
		$section->addInput(new Form_StaticText(
906
			null,
907
			build_pooltable()
908
		));
909
	}
910

    
911
	$form->add($section);
912
}
913

    
914
$section = new Form_Section('Servers');
915

    
916
$section->addInput(new Form_IpAddress(
917
	'wins1',
918
	'WINS servers',
919
	$pconfig['wins1']
920
))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'WINS Server 1');
921

    
922
$section->addInput(new Form_IpAddress(
923
	'wins2',
924
	null,
925
	$pconfig['wins2']
926
))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'WINS Server 2');
927

    
928
for ($idx=1; $idx<=4; $idx++) {
929
	$section->addInput(new Form_IpAddress(
930
		'dns' . $idx,
931
		($idx == 1) ? 'DNS servers':null,
932
		$pconfig['dns' . $idx]
933
	))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'DNS Server ' . $idx)->setHelp(($idx == 4) ? 'Leave blank to use the system default DNS servers: this interface\'s IP if DNS Forwarder or Resolver is enabled, otherwise the servers configured on the System / General Setup page.':'');
934
}
935

    
936
$form->add($section);
937

    
938
$section = new Form_Section('Other Options');
939

    
940
$section->addInput(new Form_IpAddress(
941
	'gateway',
942
	'Gateway',
943
	$pconfig['gateway']
944
))->setPattern('[.a-zA-Z0-9_]+')
945
  ->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 the network. Type "none" for no gateway assignment.');
946

    
947
$section->addInput(new Form_Input(
948
	'domain',
949
	'Domain name',
950
	'text',
951
	$pconfig['domain']
952
))->setHelp('The default is to use the domain name of this system as the default domain name provided by DHCP. An alternate domain name may be specified here.');
953

    
954
$section->addInput(new Form_Input(
955
	'domainsearchlist',
956
	'Domain search list',
957
	'text',
958
	$pconfig['domainsearchlist']
959
))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator.');
960

    
961
$section->addInput(new Form_Input(
962
	'deftime',
963
	'Default lease time',
964
	'number',
965
	$pconfig['deftime']
966
))->setHelp('This is used for clients that do not ask for a specific expiration time. The default is 7200 seconds.');
967

    
968
$section->addInput(new Form_Input(
969
	'maxtime',
970
	'Maximum lease time',
971
	'number',
972
	$pconfig['maxtime']
973
))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds.');
974

    
975
if (!is_numeric($pool) && !($act == "newpool")) {
976
	$section->addInput(new Form_IpAddress(
977
		'failover_peerip',
978
		'Failover peer IP',
979
		$pconfig['failover_peerip']
980
	))->setHelp('Leave blank to disable. Enter the interface IP address of the other machine. Machines must be using CARP. ' .
981
				'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).');
982
}
983

    
984
if (!is_numeric($pool) && !($act == "newpool")) {
985
	$section->addInput(new Form_Checkbox(
986
		'staticarp',
987
		'Static ARP',
988
		'Enable Static ARP entries',
989
		$pconfig['staticarp']
990
	))->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.');
991

    
992
	$section->addInput(new Form_Checkbox(
993
		'dhcpleaseinlocaltime',
994
		'Time format change',
995
		'Change DHCP display lease time from UTC to local time',
996
		$pconfig['dhcpleaseinlocaltime']
997
	))->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.' .
998
				' This will be used for all DHCP interfaces lease time.');
999
	$section->addInput(new Form_Checkbox(
1000
		'statsgraph',
1001
		'Statistics graphs',
1002
		'Enable RRD statistics graphs',
1003
		$pconfig['statsgraph']
1004
	))->setHelp('Enable this to add DHCP leases statistics to the RRD graphs. Disabled by default.');
1005
}
1006

    
1007
// DDNS
1008
$btnadv = new Form_Button(
1009
	'btnadvdns',
1010
	'Display Advanced',
1011
	null,
1012
	'fa-cog'
1013
);
1014

    
1015
$btnadv->setAttribute('type','button')->addClass('btn-info btn-sm');
1016

    
1017
$section->addInput(new Form_StaticText(
1018
	'Dynamic DNS',
1019
	$btnadv
1020
));
1021

    
1022
$section->addInput(new Form_Checkbox(
1023
	'ddnsupdate',
1024
	null,
1025
	'Enable registration of DHCP client names in DNS',
1026
	$pconfig['ddnsupdate']
1027
));
1028

    
1029
$section->addInput(new Form_Input(
1030
	'ddnsdomain',
1031
	'DDNS Domain',
1032
	'text',
1033
	$pconfig['ddnsdomain']
1034
))->setHelp('Leave blank to disable dynamic DNS registration.' . '<br />' .
1035
			'Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
1036

    
1037
$section->addInput(new Form_IpAddress(
1038
	'ddnsdomainprimary',
1039
	'Primary DDNS address',
1040
	$pconfig['ddnsdomainprimary']
1041
))->setHelp('Primary domain name server IP address for the dynamic domain name.');
1042

    
1043
$section->addInput(new Form_Input(
1044
	'ddnsdomainkeyname',
1045
	'DNS Domain key',
1046
	'text',
1047
	$pconfig['ddnsdomainkeyname']
1048
))->setHelp('Dynamic DNS domain key name which will be used to register client names in the DNS server.');
1049

    
1050
$section->addInput(new Form_Input(
1051
	'ddnsdomainkey',
1052
	'DNS Domain key secret',
1053
	'text',
1054
	$pconfig['ddnsdomainkey']
1055
))->setHelp('Dynamic DNS domain key secret (HMAC-MD5) which will be used to register client names in the DNS server.');
1056

    
1057
// Advanced MAC
1058
$btnadv = new Form_Button(
1059
	'btnadvmac',
1060
	'Display Advanced',
1061
	null,
1062
	'fa-cog'
1063
);
1064

    
1065
$btnadv->setAttribute('type','button')->addClass('btn-info btn-sm');
1066

    
1067
$section->addInput(new Form_StaticText(
1068
	'MAC address control',
1069
	$btnadv
1070
));
1071

    
1072
$section->addInput(new Form_Input(
1073
	'mac_allow',
1074
	'MAC Allow',
1075
	'text',
1076
	$pconfig['mac_allow']
1077
))->setHelp('List of partial MAC addresses to allow, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1078

    
1079
$section->addInput(new Form_Input(
1080
	'mac_deny',
1081
	'MAC Deny',
1082
	'text',
1083
	$pconfig['mac_deny']
1084
))->setHelp('List of partial MAC addresses to deny access, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1085

    
1086
// Advanced NTP
1087
$btnadv = new Form_Button(
1088
	'btnadvntp',
1089
	'Display Advanced',
1090
	null,
1091
	'fa-cog'
1092
);
1093

    
1094
$btnadv->setAttribute('type','button')->addClass('btn-info btn-sm');
1095

    
1096
$section->addInput(new Form_StaticText(
1097
	'NTP',
1098
	$btnadv
1099
));
1100

    
1101
$section->addInput(new Form_IpAddress(
1102
	'ntp1',
1103
	'NTP Server 1',
1104
	$pconfig['ntp1']
1105
))->setPattern('[.a-zA-Z0-9_]+');
1106

    
1107
$section->addInput(new Form_IpAddress(
1108
	'ntp2',
1109
	'NTP Server 2',
1110
	$pconfig['ntp2']
1111
))->setPattern('[.a-zA-Z0-9_]+');
1112

    
1113
// Advanced TFTP
1114
$btnadv = new Form_Button(
1115
	'btnadvtftp',
1116
	'Display Advanced',
1117
	null,
1118
	'fa-cog'
1119
);
1120

    
1121
$btnadv->setAttribute('type','button')->addClass('btn-info btn-sm');
1122

    
1123
$section->addInput(new Form_StaticText(
1124
	'TFTP',
1125
	$btnadv
1126
));
1127

    
1128
$section->addInput(new Form_Input(
1129
	'tftp',
1130
	'TFTP Server',
1131
	$pconfig['tftp']
1132
))->setHelp('Leave blank to disable. Enter a valid IP address, hostname or URL for the TFTP server.');
1133

    
1134
// Advanced LDAP
1135
$btnadv = new Form_Button(
1136
	'btnadvldap',
1137
	'Display Advanced',
1138
	null,
1139
	'fa-cog'
1140
);
1141

    
1142
$btnadv->setAttribute('type','button')->addClass('btn-info btn-sm');
1143

    
1144
$section->addInput(new Form_StaticText(
1145
	'LDAP',
1146
	$btnadv
1147
));
1148

    
1149
$section->addInput(new Form_Input(
1150
	'ldap',
1151
	'LDAP Server URI',
1152
	'text',
1153
	$pconfig['ldap']
1154
))->setHelp('Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com ');
1155

    
1156
// Advanced Network Booting options
1157
$btnadv = new Form_Button(
1158
	'btnadvnwkboot',
1159
	'Display Advanced',
1160
	null,
1161
	'fa-cog'
1162
);
1163

    
1164
$btnadv->setAttribute('type','button')->addClass('btn-info btn-sm');
1165

    
1166
$section->addInput(new Form_StaticText(
1167
	'Network Booting',
1168
	$btnadv
1169
));
1170

    
1171
$section->addInput(new Form_Checkbox(
1172
	'netboot',
1173
	'Enable',
1174
	'Enables network booting',
1175
	$pconfig['netboot']
1176
));
1177

    
1178
$section->addInput(new Form_IpAddress(
1179
	'nextserver',
1180
	'Next Server',
1181
	$pconfig['nextserver']
1182
))->setHelp('Enter the IP address of the next server');
1183

    
1184
$section->addInput(new Form_Input(
1185
	'filename',
1186
	'Default BIOS file name',
1187
	'text',
1188
	$pconfig['filename']
1189
));
1190

    
1191
$section->addInput(new Form_Input(
1192
	'filename32',
1193
	'UEFI 32 bit file name',
1194
	'text',
1195
	$pconfig['filename32']
1196
));
1197

    
1198
$section->addInput(new Form_Input(
1199
	'filename64',
1200
	'UEFI 64 bit file name',
1201
	'text',
1202
	$pconfig['filename64']
1203
))->setHelp('Both a filename and a boot server must be configured for this to work! ' .
1204
			'All three filenames and a configured boot server are necessary for UEFI to work! ');
1205

    
1206
$section->addInput(new Form_Input(
1207
	'rootpath',
1208
	'Root path',
1209
	'text',
1210
	$pconfig['rootpath']
1211
))->setHelp('string-format: iscsi:(servername):(protocol):(port):(LUN):targetname ');
1212

    
1213
// Advanced Additional options
1214
$btnadv = new Form_Button(
1215
	'btnadvopts',
1216
	'Display Advanced',
1217
	null,
1218
	'fa-cog'
1219
);
1220

    
1221
$btnadv->setAttribute('type','button')->addClass('btn-info btn-sm');
1222

    
1223
$section->addInput(new Form_StaticText(
1224
	'Additional BOOTP/DHCP Options',
1225
	$btnadv
1226
));
1227

    
1228
$form->add($section);
1229

    
1230
$section = new Form_Section('Additional BOOTP/DHCP Options');
1231
$section->addClass('adnlopts');
1232

    
1233
$section->addInput(new Form_StaticText(
1234
	null,
1235
	'<div class="alert alert-info"> ' . gettext('Enter the DHCP option number and the value for each item to include in the DHCP lease information.') . ' ' .
1236
	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>')
1237
));
1238

    
1239
if (!$pconfig['numberoptions']) {
1240
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1241
}
1242

    
1243
$customitemtypes = array(
1244
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1245
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1246
	'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')
1247
);
1248

    
1249
$numrows = count($item) -1;
1250
$counter = 0;
1251

    
1252
$numrows = count($pconfig['numberoptions']['item']) -1;
1253

    
1254
foreach ($pconfig['numberoptions']['item'] as $item) {
1255
	$number = $item['number'];
1256
	$itemtype = $item['type'];
1257
	$value = base64_decode($item['value']);
1258

    
1259
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1260
	$group->addClass('repeatable');
1261

    
1262
	$group->add(new Form_Input(
1263
		'number' . $counter,
1264
		null,
1265
		'text',
1266
		$number
1267
	))->setHelp($numrows == $counter ? 'Number':null);
1268

    
1269

    
1270
	$group->add(new Form_Select(
1271
		'itemtype' . $counter,
1272
		null,
1273
		$itemtype,
1274
		$customitemtypes
1275
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1276

    
1277
	$group->add(new Form_Input(
1278
		'value' . $counter,
1279
		null,
1280
		'text',
1281
		$value
1282
	))->setHelp($numrows == $counter ? 'Value':null);
1283

    
1284
	$group->add(new Form_Button(
1285
		'deleterow' . $counter,
1286
		'Delete',
1287
		null,
1288
		'fa-trash'
1289
	))->addClass('btn-warning');
1290

    
1291
	$section->add($group);
1292

    
1293
	$counter++;
1294
}
1295

    
1296
$section->addInput(new Form_Button(
1297
	'addrow',
1298
	'Add',
1299
	null,
1300
	'fa-plus'
1301
))->addClass('btn-success');
1302

    
1303
$form->add($section);
1304

    
1305
if ($act == "newpool") {
1306
	$form->addGlobal(new Form_Input(
1307
		'act',
1308
		null,
1309
		'hidden',
1310
		'newpool'
1311
	));
1312
}
1313

    
1314
if (is_numeric($pool)) {
1315
	$form->addGlobal(new Form_Input(
1316
		'pool',
1317
		null,
1318
		'hidden',
1319
		$pool
1320
	));
1321
}
1322

    
1323
$form->addGlobal(new Form_Input(
1324
	'if',
1325
	null,
1326
	'hidden',
1327
	$if
1328
));
1329

    
1330
print($form);
1331

    
1332
// DHCP Static Mappings table
1333

    
1334
if (!is_numeric($pool) && !($act == "newpool")) {
1335
?>
1336

    
1337
<div class="panel panel-default">
1338
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("DHCP Static Mappings for this Interface")?></h2></div>
1339
	<div class="table-responsive">
1340
			<table class="table table-striped table-hover table-condensed sortable-theme-bootstrap" data-sortable>
1341
				<thead>
1342
					<tr>
1343
						<th><?=gettext("Static ARP")?></th>
1344
						<th><?=gettext("MAC address")?></th>
1345
						<th><?=gettext("IP address")?></th>
1346
						<th><?=gettext("Hostname")?></th>
1347
						<th><?=gettext("Description")?></th>
1348
						<th></th>
1349
					</tr>
1350
				</thead>
1351
<?php
1352
	if (is_array($a_maps)) {
1353
		$i = 0;
1354
?>
1355
				<tbody>
1356
<?php
1357
		foreach ($a_maps as $mapent) {
1358
?>
1359
					<tr>
1360
						<td class="text-center" ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1361
							<?php if (isset($mapent['arp_table_static_entry'])): ?>
1362
								<i class="fa fa-check"></i>
1363
							<?php endif; ?>
1364
						</td>
1365
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1366
							<?=htmlspecialchars($mapent['mac'])?>
1367
						</td>
1368
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1369
							<?=htmlspecialchars($mapent['ipaddr'])?>
1370
						</td>
1371
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1372
							<?=htmlspecialchars($mapent['hostname'])?>
1373
						</td>
1374
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1375
							<?=htmlspecialchars($mapent['descr'])?>
1376
						</td>
1377
						<td>
1378
							<a class="fa fa-pencil"	title="<?=gettext('Edit static mapping')?>"	href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>"></a>
1379
							<a class="fa fa-trash"	title="<?=gettext('Delete static mapping')?>"	href="services_dhcp.php?if=<?=htmlspecialchars($if)?>&amp;act=del&amp;id=<?=$i?>"></a>
1380
						</td>
1381
					</tr>
1382
<?php
1383
		$i++;
1384
		}
1385
?>
1386
				</tbody>
1387
<?php
1388
	}
1389
?>
1390
		</table>
1391
	</div>
1392
</div>
1393

    
1394
<nav class="action-buttons">
1395
	<a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>" class="btn btn-sm btn-success">
1396
		<i class="fa fa-plus icon-embed-btn"></i>
1397
		<?=gettext("Add")?>
1398
	</a>
1399
</nav>
1400
<?php
1401
}
1402
?>
1403

    
1404
<script type="text/javascript">
1405
//<![CDATA[
1406
events.push(function() {
1407

    
1408
	// Show advanced DNS options ======================================================================================
1409
	var showadvdns = false;
1410

    
1411
	function show_advdns(ispageload) {
1412
		var text;
1413
		// On page load decide the initial state based on the data.
1414
		if (ispageload) {
1415
<?php
1416
			if (!$pconfig['ddnsupdate'] && empty($pconfig['ddnsdomain']) && empty($pconfig['ddnsdomainprimary']) &&
1417
			    empty($pconfig['ddnsdomainkeyname']) && empty($pconfig['ddnsdomainkey'])) {
1418
				$showadv = false;
1419
			} else {
1420
				$showadv = true;
1421
			}
1422
?>
1423
			showadvdns = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1424
		} else {
1425
			// It was a click, swap the state.
1426
			showadvdns = !showadvdns;
1427
		}
1428

    
1429
		hideCheckbox('ddnsupdate', !showadvdns);
1430
		hideInput('ddnsdomain', !showadvdns);
1431
		hideInput('ddnsdomainprimary', !showadvdns);
1432
		hideInput('ddnsdomainkeyname', !showadvdns);
1433
		hideInput('ddnsdomainkey', !showadvdns);
1434

    
1435
		if (showadvdns) {
1436
			text = "<?=gettext('Hide Advanced');?>";
1437
		} else {
1438
			text = "<?=gettext('Display Advanced');?>";
1439
		}
1440
		$('#btnadvdns').html('<i class="fa fa-cog"></i> ' + text);
1441
	}
1442

    
1443
	$('#btnadvdns').click(function(event) {
1444
		show_advdns();
1445
	});
1446

    
1447
	// Show advanced MAC options ======================================================================================
1448
	var showadvmac = false;
1449

    
1450
	function show_advmac(ispageload) {
1451
		var text;
1452
		// On page load decide the initial state based on the data.
1453
		if (ispageload) {
1454
<?php
1455
			if (empty($pconfig['mac_allow']) && empty($pconfig['mac_deny'])) {
1456
				$showadv = false;
1457
			} else {
1458
				$showadv = true;
1459
			}
1460
?>
1461
			showadvmac = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1462
		} else {
1463
			// It was a click, swap the state.
1464
			showadvmac = !showadvmac;
1465
		}
1466

    
1467
		hideInput('mac_allow', !showadvmac);
1468
		hideInput('mac_deny', !showadvmac);
1469

    
1470
		if (showadvmac) {
1471
			text = "<?=gettext('Hide Advanced');?>";
1472
		} else {
1473
			text = "<?=gettext('Display Advanced');?>";
1474
		}
1475
		$('#btnadvmac').html('<i class="fa fa-cog"></i> ' + text);
1476
	}
1477

    
1478
	$('#btnadvmac').click(function(event) {
1479
		show_advmac();
1480
	});
1481

    
1482
	// Show advanced NTP options ======================================================================================
1483
	var showadvntp = false;
1484

    
1485
	function show_advntp(ispageload) {
1486
		var text;
1487
		// On page load decide the initial state based on the data.
1488
		if (ispageload) {
1489
<?php
1490
			if (empty($pconfig['ntp1']) && empty($pconfig['ntp2'])) {
1491
				$showadv = false;
1492
			} else {
1493
				$showadv = true;
1494
			}
1495
?>
1496
			showadvntp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1497
		} else {
1498
			// It was a click, swap the state.
1499
			showadvntp = !showadvntp;
1500
		}
1501

    
1502
		hideInput('ntp1', !showadvntp);
1503
		hideInput('ntp2', !showadvntp);
1504

    
1505
		if (showadvntp) {
1506
			text = "<?=gettext('Hide Advanced');?>";
1507
		} else {
1508
			text = "<?=gettext('Display Advanced');?>";
1509
		}
1510
		$('#btnadvntp').html('<i class="fa fa-cog"></i> ' + text);
1511
	}
1512

    
1513
	$('#btnadvntp').click(function(event) {
1514
		show_advntp();
1515
	});
1516

    
1517
	// Show advanced TFTP options ======================================================================================
1518
	var showadvtftp = false;
1519

    
1520
	function show_advtftp(ispageload) {
1521
		var text;
1522
		// On page load decide the initial state based on the data.
1523
		if (ispageload) {
1524
<?php
1525
			if (empty($pconfig['tftp'])) {
1526
				$showadv = false;
1527
			} else {
1528
				$showadv = true;
1529
			}
1530
?>
1531
			showadvtftp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1532
		} else {
1533
			// It was a click, swap the state.
1534
			showadvtftp = !showadvtftp;
1535
		}
1536

    
1537
		hideInput('tftp', !showadvtftp);
1538

    
1539
		if (showadvtftp) {
1540
			text = "<?=gettext('Hide Advanced');?>";
1541
		} else {
1542
			text = "<?=gettext('Display Advanced');?>";
1543
		}
1544
		$('#btnadvtftp').html('<i class="fa fa-cog"></i> ' + text);
1545
	}
1546

    
1547
	$('#btnadvtftp').click(function(event) {
1548
		show_advtftp();
1549
	});
1550

    
1551
	// Show advanced LDAP options ======================================================================================
1552
	var showadvldap = false;
1553

    
1554
	function show_advldap(ispageload) {
1555
		var text;
1556
		// On page load decide the initial state based on the data.
1557
		if (ispageload) {
1558
<?php
1559
			if (empty($pconfig['ldap'])) {
1560
				$showadv = false;
1561
			} else {
1562
				$showadv = true;
1563
			}
1564
?>
1565
			showadvldap = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1566
		} else {
1567
			// It was a click, swap the state.
1568
			showadvldap = !showadvldap;
1569
		}
1570

    
1571
		hideInput('ldap', !showadvldap);
1572

    
1573
		if (showadvldap) {
1574
			text = "<?=gettext('Hide Advanced');?>";
1575
		} else {
1576
			text = "<?=gettext('Display Advanced');?>";
1577
		}
1578
		$('#btnadvldap').html('<i class="fa fa-cog"></i> ' + text);
1579
	}
1580

    
1581
	$('#btnadvldap').click(function(event) {
1582
		show_advldap();
1583
	});
1584

    
1585
	// Show advanced additional opts options ===========================================================================
1586
	var showadvopts = false;
1587

    
1588
	function show_advopts(ispageload) {
1589
		var text;
1590
		// On page load decide the initial state based on the data.
1591
		if (ispageload) {
1592
<?php
1593
			if (empty($pconfig['numberoptions']) ||
1594
			    (empty($pconfig['numberoptions']['item'][0]['number']) && (empty($pconfig['numberoptions']['item'][0]['value'])))) {
1595
				$showadv = false;
1596
			} else {
1597
				$showadv = true;
1598
			}
1599
?>
1600
			showadvopts = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1601
		} else {
1602
			// It was a click, swap the state.
1603
			showadvopts = !showadvopts;
1604
		}
1605

    
1606
		hideClass('adnlopts', !showadvopts);
1607

    
1608
		if (showadvopts) {
1609
			text = "<?=gettext('Hide Advanced');?>";
1610
		} else {
1611
			text = "<?=gettext('Display Advanced');?>";
1612
		}
1613
		$('#btnadvopts').html('<i class="fa fa-cog"></i> ' + text);
1614
	}
1615

    
1616
	$('#btnadvopts').click(function(event) {
1617
		show_advopts();
1618
	});
1619

    
1620
	// Show advanced Network Booting options ===========================================================================
1621
	var showadvnwkboot = false;
1622

    
1623
	function show_advnwkboot(ispageload) {
1624
		var text;
1625
		// On page load decide the initial state based on the data.
1626
		if (ispageload) {
1627
<?php
1628
			if (empty($pconfig['netboot'])) {
1629
				$showadv = false;
1630
			} else {
1631
				$showadv = true;
1632
			}
1633
?>
1634
			showadvnwkboot = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1635
		} else {
1636
			// It was a click, swap the state.
1637
			showadvnwkboot = !showadvnwkboot;
1638
		}
1639

    
1640
		hideCheckbox('netboot', !showadvnwkboot);
1641
		hideInput('nextserver', !showadvnwkboot);
1642
		hideInput('filename', !showadvnwkboot);
1643
		hideInput('filename32', !showadvnwkboot);
1644
		hideInput('filename64', !showadvnwkboot);
1645
		hideInput('rootpath', !showadvnwkboot);
1646

    
1647
		if (showadvnwkboot) {
1648
			text = "<?=gettext('Hide Advanced');?>";
1649
		} else {
1650
			text = "<?=gettext('Display Advanced');?>";
1651
		}
1652
		$('#btnadvnwkboot').html('<i class="fa fa-cog"></i> ' + text);
1653
	}
1654

    
1655
	$('#btnadvnwkboot').click(function(event) {
1656
		show_advnwkboot();
1657
	});
1658

    
1659
	// ---------- On initial page load ------------------------------------------------------------
1660

    
1661
	show_advdns(true);
1662
	show_advmac(true);
1663
	show_advntp(true);
1664
	show_advtftp(true);
1665
	show_advldap(true);
1666
	show_advopts(true);
1667
	show_advnwkboot(true);
1668

    
1669
	// Suppress "Delete row" button if there are fewer than two rows
1670
	checkLastRow();
1671
});
1672
//]]>
1673
</script>
1674

    
1675
<?php include("foot.inc");
(119-119/227)