Project

General

Profile

Download (51.7 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 Electric Sheep Fencing, LLC
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['denyunknown'] = isset($dhcpdconf['denyunknown']);
203
	$pconfig['nonak'] = isset($dhcpdconf['nonak']);
204
	$pconfig['ddnsdomain'] = $dhcpdconf['ddnsdomain'];
205
	$pconfig['ddnsdomainprimary'] = $dhcpdconf['ddnsdomainprimary'];
206
	$pconfig['ddnsdomainkeyname'] = $dhcpdconf['ddnsdomainkeyname'];
207
	$pconfig['ddnsdomainkey'] = $dhcpdconf['ddnsdomainkey'];
208
	$pconfig['ddnsupdate'] = isset($dhcpdconf['ddnsupdate']);
209
	$pconfig['mac_allow'] = $dhcpdconf['mac_allow'];
210
	$pconfig['mac_deny'] = $dhcpdconf['mac_deny'];
211
	list($pconfig['ntp1'], $pconfig['ntp2']) = $dhcpdconf['ntpserver'];
212
	$pconfig['tftp'] = $dhcpdconf['tftp'];
213
	$pconfig['ldap'] = $dhcpdconf['ldap'];
214
	$pconfig['netboot'] = isset($dhcpdconf['netboot']);
215
	$pconfig['nextserver'] = $dhcpdconf['nextserver'];
216
	$pconfig['filename'] = $dhcpdconf['filename'];
217
	$pconfig['filename32'] = $dhcpdconf['filename32'];
218
	$pconfig['filename64'] = $dhcpdconf['filename64'];
219
	$pconfig['rootpath'] = $dhcpdconf['rootpath'];
220
	$pconfig['netmask'] = $dhcpdconf['netmask'];
221
	$pconfig['numberoptions'] = $dhcpdconf['numberoptions'];
222
	$pconfig['statsgraph'] = $dhcpdconf['statsgraph'];
223
}
224

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

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

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

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

    
241
	return true;
242
}
243

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

    
246
	unset($input_errors);
247

    
248
	$pconfig = $_POST;
249

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
398
		if ($_POST['staticarp'] && $noip) {
399
			$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.");
400
		}
401

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

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

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

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

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

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

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

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

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

    
507
		$dhcpd_enable_changed = false;
508

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

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

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

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

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

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

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

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

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

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

    
604
		$dhcpdconf['numberoptions'] = $numberoptions;
605

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

    
614
		write_config();
615
	}
616
}
617

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

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

    
651
	$savemsg = get_std_save_message($retval);
652
}
653

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

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

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

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

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

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

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

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

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

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

    
718
	$pooltbl .=			'</tbody>';
719
	$pooltbl .=		'</table>';
720
	$pooltbl .= '</div>';
721

    
722
	return($pooltbl);
723
}
724

    
725
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
726

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

    
732
include("head.inc");
733

    
734
if ($input_errors) {
735
	print_input_errors($input_errors);
736
}
737

    
738
if ($savemsg) {
739
	print_info_box($savemsg, 'success');
740
}
741

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

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

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

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

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

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

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

    
780
display_top_tabs($tab_array);
781

    
782
$form = new Form();
783

    
784
$section = new Form_Section('General Options');
785

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

    
797
$section->addInput(new Form_Checkbox(
798
	'denyunknown',
799
	'Deny unknown clients',
800
	'Only the clients defined below will get DHCP leases from this server.',
801
	$pconfig['denyunknown']
802
));
803

    
804
$section->addInput(new Form_Checkbox(
805
	'nonak',
806
	'Ignore denied clients',
807
	'Denied clients will be ignored rather than rejected.',
808
	$pconfig['nonak']
809
))->setHelp("This option is not compatible with failover and cannot be enabled when a Failover Peer IP address is configured.");
810

    
811

    
812
if (is_numeric($pool) || ($act == "newpool")) {
813
	$section->addInput(new Form_Input(
814
		'descr',
815
		'Pool Description',
816
		'text',
817
		$pconfig['descr']
818
	));
819
}
820

    
821
$section->addInput(new Form_StaticText(
822
	'Subnet',
823
	gen_subnet($ifcfgip, $ifcfgsn)
824
));
825

    
826
$section->addInput(new Form_StaticText(
827
	'Subnet mask',
828
	gen_subnet_mask($ifcfgsn)
829
));
830

    
831
// Compose a string to display the required address ranges
832
$rangestr = ip_after($subnet_start) . ' - ' . ip_before($subnet_end);
833

    
834
if (is_numeric($pool) || ($act == "newpool")) {
835
	$rangestr .= '<br />' . gettext('In-use DHCP Pool Ranges:');
836
	if (is_array($config['dhcpd'][$if]['range'])) {
837
		$rangestr .= '<br />' . $config['dhcpd'][$if]['range']['from'] . ' - ' . $config['dhcpd'][$if]['range']['to'];
838
	}
839

    
840
	foreach ($a_pools as $p) {
841
		if (is_array($p['range'])) {
842
			$rangestr .= '<br />' . $p['range']['from'] . ' - ' . $p['range']['to'];
843
		}
844
	}
845
}
846

    
847
$section->addInput(new Form_StaticText(
848
	'Available range',
849
	$rangestr
850
));
851

    
852
if ($is_olsr_enabled) {
853
	$section->addInput(new Form_Select(
854
		'netmask',
855
		'Subnet mask',
856
		$pconfig['netmask'],
857
		array_combine(range(32, 1, -1), range(32, 1, -1))
858
	));
859
}
860

    
861
$group = new Form_Group('Range');
862

    
863
$group->add(new Form_IpAddress(
864
	'range_from',
865
	null,
866
	$pconfig['range_from']
867
))->setHelp('From');
868

    
869
$group->add(new Form_IpAddress(
870
	'range_to',
871
	null,
872
	$pconfig['range_to']
873
))->setHelp('To');
874

    
875
$section->add($group);
876

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

    
879
if (!is_numeric($pool) && !($act == "newpool")) {
880
	$section = new Form_Section('Additional Pools');
881

    
882
	$btnaddpool = new Form_Button(
883
		'btnaddpool',
884
		'Add pool',
885
		'services_dhcp.php?if=' . htmlspecialchars($if) . '&act=newpool',
886
		'fa-plus'
887
	);
888
	$btnaddpool->addClass('btn-success');
889

    
890
	$section->addInput(new Form_StaticText(
891
		'Add',
892
		$btnaddpool
893
	))->setHelp('If additional pools of addresses are needed inside of this subnet outside the above Range, they may be specified here.');
894

    
895
	if (is_array($a_pools)) {
896
		$section->addInput(new Form_StaticText(
897
			null,
898
			build_pooltable()
899
		));
900
	}
901

    
902
	$form->add($section);
903
}
904

    
905
$section = new Form_Section('Servers');
906

    
907
$section->addInput(new Form_IpAddress(
908
	'wins1',
909
	'WINS servers',
910
	$pconfig['wins1']
911
))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'WINS Server 1');
912

    
913
$section->addInput(new Form_IpAddress(
914
	'wins2',
915
	null,
916
	$pconfig['wins2']
917
))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'WINS Server 2');
918

    
919
for ($idx=1; $idx<=4; $idx++) {
920
	$section->addInput(new Form_IpAddress(
921
		'dns' . $idx,
922
		($idx == 1) ? 'DNS servers':null,
923
		$pconfig['dns' . $idx]
924
	))->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.':'');
925
}
926

    
927
$form->add($section);
928

    
929
$section = new Form_Section('Other Options');
930

    
931
$section->addInput(new Form_IpAddress(
932
	'gateway',
933
	'Gateway',
934
	$pconfig['gateway']
935
))->setPattern('[.a-zA-Z0-9_]+')
936
  ->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.');
937

    
938
$section->addInput(new Form_Input(
939
	'domain',
940
	'Domain name',
941
	'text',
942
	$pconfig['domain']
943
))->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.');
944

    
945
$section->addInput(new Form_Input(
946
	'domainsearchlist',
947
	'Domain search list',
948
	'text',
949
	$pconfig['domainsearchlist']
950
))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator.');
951

    
952
$section->addInput(new Form_Input(
953
	'deftime',
954
	'Default lease time',
955
	'number',
956
	$pconfig['deftime']
957
))->setHelp('This is used for clients that do not ask for a specific expiration time. The default is 7200 seconds.');
958

    
959
$section->addInput(new Form_Input(
960
	'maxtime',
961
	'Maximum lease time',
962
	'number',
963
	$pconfig['maxtime']
964
))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds.');
965

    
966
if (!is_numeric($pool) && !($act == "newpool")) {
967
	$section->addInput(new Form_IpAddress(
968
		'failover_peerip',
969
		'Failover peer IP',
970
		$pconfig['failover_peerip']
971
	))->setHelp('Leave blank to disable. Enter the interface IP address of the other machine. Machines must be using CARP. ' .
972
				'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).');
973
}
974

    
975
if (!is_numeric($pool) && !($act == "newpool")) {
976
	$section->addInput(new Form_Checkbox(
977
		'staticarp',
978
		'Static ARP',
979
		'Enable Static ARP entries',
980
		$pconfig['staticarp']
981
	))->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.');
982

    
983
	$section->addInput(new Form_Checkbox(
984
		'dhcpleaseinlocaltime',
985
		'Time format change',
986
		'Change DHCP display lease time from UTC to local time',
987
		$pconfig['dhcpleaseinlocaltime']
988
	))->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.' .
989
				' This will be used for all DHCP interfaces lease time.');
990
	$section->addInput(new Form_Checkbox(
991
		'statsgraph',
992
		'Statistics graphs',
993
		'Enable RRD statistics graphs',
994
		$pconfig['statsgraph']
995
	))->setHelp('Enable this to add DHCP leases statistics to the RRD graphs. Disabled by default.');
996
}
997

    
998
// DDNS
999
$btnadv = new Form_Button(
1000
	'btnadvdns',
1001
	'Display Advanced',
1002
	null,
1003
	'fa-cog'
1004
);
1005

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

    
1008
$section->addInput(new Form_StaticText(
1009
	'Dynamic DNS',
1010
	$btnadv
1011
));
1012

    
1013
$section->addInput(new Form_Checkbox(
1014
	'ddnsupdate',
1015
	null,
1016
	'Enable registration of DHCP client names in DNS',
1017
	$pconfig['ddnsupdate']
1018
));
1019

    
1020
$section->addInput(new Form_Input(
1021
	'ddnsdomain',
1022
	'DDNS Domain',
1023
	'text',
1024
	$pconfig['ddnsdomain']
1025
))->setHelp('Leave blank to disable dynamic DNS registration.' . '<br />' .
1026
			'Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
1027

    
1028
$section->addInput(new Form_IpAddress(
1029
	'ddnsdomainprimary',
1030
	'Primary DDNS address',
1031
	$pconfig['ddnsdomainprimary']
1032
))->setHelp('Primary domain name server IP address for the dynamic domain name.');
1033

    
1034
$section->addInput(new Form_Input(
1035
	'ddnsdomainkeyname',
1036
	'DNS Domain key',
1037
	'text',
1038
	$pconfig['ddnsdomainkeyname']
1039
))->setHelp('Dynamic DNS domain key name which will be used to register client names in the DNS server.');
1040

    
1041
$section->addInput(new Form_Input(
1042
	'ddnsdomainkey',
1043
	'DNS Domain key secret',
1044
	'text',
1045
	$pconfig['ddnsdomainkey']
1046
))->setHelp('Dynamic DNS domain key secret (HMAC-MD5) which will be used to register client names in the DNS server.');
1047

    
1048
// Advanced MAC
1049
$btnadv = new Form_Button(
1050
	'btnadvmac',
1051
	'Display Advanced',
1052
	null,
1053
	'fa-cog'
1054
);
1055

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

    
1058
$section->addInput(new Form_StaticText(
1059
	'MAC address control',
1060
	$btnadv
1061
));
1062

    
1063
$section->addInput(new Form_Input(
1064
	'mac_allow',
1065
	'MAC Allow',
1066
	'text',
1067
	$pconfig['mac_allow']
1068
))->setHelp('List of partial MAC addresses to allow, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1069

    
1070
$section->addInput(new Form_Input(
1071
	'mac_deny',
1072
	'MAC Deny',
1073
	'text',
1074
	$pconfig['mac_deny']
1075
))->setHelp('List of partial MAC addresses to deny access, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1076

    
1077
// Advanced NTP
1078
$btnadv = new Form_Button(
1079
	'btnadvntp',
1080
	'Display Advanced',
1081
	null,
1082
	'fa-cog'
1083
);
1084

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

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

    
1092
$section->addInput(new Form_IpAddress(
1093
	'ntp1',
1094
	'NTP Server 1',
1095
	$pconfig['ntp1']
1096
))->setPattern('[.a-zA-Z0-9_]+');
1097

    
1098
$section->addInput(new Form_IpAddress(
1099
	'ntp2',
1100
	'NTP Server 2',
1101
	$pconfig['ntp2']
1102
))->setPattern('[.a-zA-Z0-9_]+');
1103

    
1104
// Advanced TFTP
1105
$btnadv = new Form_Button(
1106
	'btnadvtftp',
1107
	'Display Advanced',
1108
	null,
1109
	'fa-cog'
1110
);
1111

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

    
1114
$section->addInput(new Form_StaticText(
1115
	'TFTP',
1116
	$btnadv
1117
));
1118

    
1119
$section->addInput(new Form_IpAddress(
1120
	'tftp',
1121
	'TFTP Server',
1122
	'text',
1123
	$pconfig['tftp']
1124
))->setHelp('Leave blank to disable.  Enter a full hostname or IP for the TFTP server.')->setPattern('[.a-zA-Z0-9_-]+');
1125

    
1126
// Advanced LDAP
1127
$btnadv = new Form_Button(
1128
	'btnadvldap',
1129
	'Display Advanced',
1130
	null,
1131
	'fa-cog'
1132
);
1133

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

    
1136
$section->addInput(new Form_StaticText(
1137
	'LDAP',
1138
	$btnadv
1139
));
1140

    
1141
$section->addInput(new Form_Input(
1142
	'ldap',
1143
	'LDAP Server URI',
1144
	'text',
1145
	$pconfig['ldap']
1146
))->setHelp('Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com ');
1147

    
1148
// Advanced Network Booting options
1149
$btnadv = new Form_Button(
1150
	'btnadvnwkboot',
1151
	'Display Advanced',
1152
	null,
1153
	'fa-cog'
1154
);
1155

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

    
1158
$section->addInput(new Form_StaticText(
1159
	'Network Booting',
1160
	$btnadv
1161
));
1162

    
1163
$section->addInput(new Form_Checkbox(
1164
	'netboot',
1165
	'Enable',
1166
	'Enables network booting',
1167
	$pconfig['netboot']
1168
));
1169

    
1170
$section->addInput(new Form_IpAddress(
1171
	'nextserver',
1172
	'Next Server',
1173
	$pconfig['nextserver']
1174
))->setHelp('Enter the IP address of the next server');
1175

    
1176
$section->addInput(new Form_Input(
1177
	'filename',
1178
	'Default BIOS file name',
1179
	'text',
1180
	$pconfig['filename']
1181
));
1182

    
1183
$section->addInput(new Form_Input(
1184
	'filename32',
1185
	'UEFI 32 bit file name',
1186
	'text',
1187
	$pconfig['filename32']
1188
));
1189

    
1190
$section->addInput(new Form_Input(
1191
	'filename64',
1192
	'UEFI 64 bit file name',
1193
	'text',
1194
	$pconfig['filename64']
1195
))->setHelp('Both a filename and a boot server must be configured for this to work! ' .
1196
			'All three filenames and a configured boot server are necessary for UEFI to work! ');
1197

    
1198
$section->addInput(new Form_Input(
1199
	'rootpath',
1200
	'Root path',
1201
	'text',
1202
	$pconfig['rootpath']
1203
))->setHelp('string-format: iscsi:(servername):(protocol):(port):(LUN):targetname ');
1204

    
1205
// Advanced Additional options
1206
$btnadv = new Form_Button(
1207
	'btnadvopts',
1208
	'Display Advanced',
1209
	null,
1210
	'fa-cog'
1211
);
1212

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

    
1215
$section->addInput(new Form_StaticText(
1216
	'Additional BOOTP/DHCP Options',
1217
	$btnadv
1218
));
1219

    
1220
$form->add($section);
1221

    
1222
$section = new Form_Section('Additional BOOTP/DHCP Options');
1223
$section->addClass('adnlopts');
1224

    
1225
$section->addInput(new Form_StaticText(
1226
	null,
1227
	'<div class="alert alert-info"> ' . gettext('Enter the DHCP option number and the value for each item to include in the DHCP lease information.') . ' ' .
1228
	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>')
1229
));
1230

    
1231
if (!$pconfig['numberoptions']) {
1232
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1233
}
1234

    
1235
$customitemtypes = array(
1236
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1237
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1238
	'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')
1239
);
1240

    
1241
$numrows = count($item) -1;
1242
$counter = 0;
1243

    
1244
$numrows = count($pconfig['numberoptions']['item']) -1;
1245

    
1246
foreach ($pconfig['numberoptions']['item'] as $item) {
1247
	$number = $item['number'];
1248
	$itemtype = $item['type'];
1249
	$value = base64_decode($item['value']);
1250

    
1251
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1252
	$group->addClass('repeatable');
1253

    
1254
	$group->add(new Form_Input(
1255
		'number' . $counter,
1256
		null,
1257
		'text',
1258
		$number
1259
	))->setHelp($numrows == $counter ? 'Number':null);
1260

    
1261

    
1262
	$group->add(new Form_Select(
1263
		'itemtype' . $counter,
1264
		null,
1265
		$itemtype,
1266
		$customitemtypes
1267
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1268

    
1269
	$group->add(new Form_Input(
1270
		'value' . $counter,
1271
		null,
1272
		'text',
1273
		$value
1274
	))->setHelp($numrows == $counter ? 'Value':null);
1275

    
1276
	$group->add(new Form_Button(
1277
		'deleterow' . $counter,
1278
		'Delete',
1279
		null,
1280
		'fa-trash'
1281
	))->addClass('btn-warning');
1282

    
1283
	$section->add($group);
1284

    
1285
	$counter++;
1286
}
1287

    
1288
$section->addInput(new Form_Button(
1289
	'addrow',
1290
	'Add',
1291
	null,
1292
	'fa-plus'
1293
))->addClass('btn-success');
1294

    
1295
$form->add($section);
1296

    
1297
if ($act == "newpool") {
1298
	$form->addGlobal(new Form_Input(
1299
		'act',
1300
		null,
1301
		'hidden',
1302
		'newpool'
1303
	));
1304
}
1305

    
1306
if (is_numeric($pool)) {
1307
	$form->addGlobal(new Form_Input(
1308
		'pool',
1309
		null,
1310
		'hidden',
1311
		$pool
1312
	));
1313
}
1314

    
1315
$form->addGlobal(new Form_Input(
1316
	'if',
1317
	null,
1318
	'hidden',
1319
	$if
1320
));
1321

    
1322
print($form);
1323

    
1324
// DHCP Static Mappings table
1325

    
1326
if (!is_numeric($pool) && !($act == "newpool")) {
1327
?>
1328

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

    
1386
<nav class="action-buttons">
1387
	<a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>" class="btn btn-sm btn-success">
1388
		<i class="fa fa-plus icon-embed-btn"></i>
1389
		<?=gettext("Add")?>
1390
	</a>
1391
</nav>
1392
<?php
1393
}
1394
?>
1395

    
1396
<script type="text/javascript">
1397
//<![CDATA[
1398
events.push(function() {
1399

    
1400
	// Show advanced DNS options ======================================================================================
1401
	var showadvdns = false;
1402

    
1403
	function show_advdns(ispageload) {
1404
		var text;
1405
		// On page load decide the initial state based on the data.
1406
		if (ispageload) {
1407
<?php
1408
			if (!$pconfig['ddnsupdate'] && empty($pconfig['ddnsdomain']) && empty($pconfig['ddnsdomainprimary']) &&
1409
			    empty($pconfig['ddnsdomainkeyname']) && empty($pconfig['ddnsdomainkey'])) {
1410
				$showadv = false;
1411
			} else {
1412
				$showadv = true;
1413
			}
1414
?>
1415
			showadvdns = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1416
		} else {
1417
			// It was a click, swap the state.
1418
			showadvdns = !showadvdns;
1419
		}
1420

    
1421
		hideCheckbox('ddnsupdate', !showadvdns);
1422
		hideInput('ddnsdomain', !showadvdns);
1423
		hideInput('ddnsdomainprimary', !showadvdns);
1424
		hideInput('ddnsdomainkeyname', !showadvdns);
1425
		hideInput('ddnsdomainkey', !showadvdns);
1426

    
1427
		if (showadvdns) {
1428
			text = "<?=gettext('Hide Advanced');?>";
1429
		} else {
1430
			text = "<?=gettext('Display Advanced');?>";
1431
		}
1432
		$('#btnadvdns').html('<i class="fa fa-cog"></i> ' + text);
1433
	}
1434

    
1435
	$('#btnadvdns').click(function(event) {
1436
		show_advdns();
1437
	});
1438

    
1439
	// Show advanced MAC options ======================================================================================
1440
	var showadvmac = false;
1441

    
1442
	function show_advmac(ispageload) {
1443
		var text;
1444
		// On page load decide the initial state based on the data.
1445
		if (ispageload) {
1446
<?php
1447
			if (empty($pconfig['mac_allow']) && empty($pconfig['mac_deny'])) {
1448
				$showadv = false;
1449
			} else {
1450
				$showadv = true;
1451
			}
1452
?>
1453
			showadvmac = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1454
		} else {
1455
			// It was a click, swap the state.
1456
			showadvmac = !showadvmac;
1457
		}
1458

    
1459
		hideInput('mac_allow', !showadvmac);
1460
		hideInput('mac_deny', !showadvmac);
1461

    
1462
		if (showadvmac) {
1463
			text = "<?=gettext('Hide Advanced');?>";
1464
		} else {
1465
			text = "<?=gettext('Display Advanced');?>";
1466
		}
1467
		$('#btnadvmac').html('<i class="fa fa-cog"></i> ' + text);
1468
	}
1469

    
1470
	$('#btnadvmac').click(function(event) {
1471
		show_advmac();
1472
	});
1473

    
1474
	// Show advanced NTP options ======================================================================================
1475
	var showadvntp = false;
1476

    
1477
	function show_advntp(ispageload) {
1478
		var text;
1479
		// On page load decide the initial state based on the data.
1480
		if (ispageload) {
1481
<?php
1482
			if (empty($pconfig['ntp1']) && empty($pconfig['ntp2'])) {
1483
				$showadv = false;
1484
			} else {
1485
				$showadv = true;
1486
			}
1487
?>
1488
			showadvntp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1489
		} else {
1490
			// It was a click, swap the state.
1491
			showadvntp = !showadvntp;
1492
		}
1493

    
1494
		hideInput('ntp1', !showadvntp);
1495
		hideInput('ntp2', !showadvntp);
1496

    
1497
		if (showadvntp) {
1498
			text = "<?=gettext('Hide Advanced');?>";
1499
		} else {
1500
			text = "<?=gettext('Display Advanced');?>";
1501
		}
1502
		$('#btnadvntp').html('<i class="fa fa-cog"></i> ' + text);
1503
	}
1504

    
1505
	$('#btnadvntp').click(function(event) {
1506
		show_advntp();
1507
	});
1508

    
1509
	// Show advanced TFTP options ======================================================================================
1510
	var showadvtftp = false;
1511

    
1512
	function show_advtftp(ispageload) {
1513
		var text;
1514
		// On page load decide the initial state based on the data.
1515
		if (ispageload) {
1516
<?php
1517
			if (empty($pconfig['tftp'])) {
1518
				$showadv = false;
1519
			} else {
1520
				$showadv = true;
1521
			}
1522
?>
1523
			showadvtftp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1524
		} else {
1525
			// It was a click, swap the state.
1526
			showadvtftp = !showadvtftp;
1527
		}
1528

    
1529
		hideInput('tftp', !showadvtftp);
1530

    
1531
		if (showadvtftp) {
1532
			text = "<?=gettext('Hide Advanced');?>";
1533
		} else {
1534
			text = "<?=gettext('Display Advanced');?>";
1535
		}
1536
		$('#btnadvtftp').html('<i class="fa fa-cog"></i> ' + text);
1537
	}
1538

    
1539
	$('#btnadvtftp').click(function(event) {
1540
		show_advtftp();
1541
	});
1542

    
1543
	// Show advanced LDAP options ======================================================================================
1544
	var showadvldap = false;
1545

    
1546
	function show_advldap(ispageload) {
1547
		var text;
1548
		// On page load decide the initial state based on the data.
1549
		if (ispageload) {
1550
<?php
1551
			if (empty($pconfig['ldap'])) {
1552
				$showadv = false;
1553
			} else {
1554
				$showadv = true;
1555
			}
1556
?>
1557
			showadvldap = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1558
		} else {
1559
			// It was a click, swap the state.
1560
			showadvldap = !showadvldap;
1561
		}
1562

    
1563
		hideInput('ldap', !showadvldap);
1564

    
1565
		if (showadvldap) {
1566
			text = "<?=gettext('Hide Advanced');?>";
1567
		} else {
1568
			text = "<?=gettext('Display Advanced');?>";
1569
		}
1570
		$('#btnadvldap').html('<i class="fa fa-cog"></i> ' + text);
1571
	}
1572

    
1573
	$('#btnadvldap').click(function(event) {
1574
		show_advldap();
1575
	});
1576

    
1577
	// Show advanced additional opts options ===========================================================================
1578
	var showadvopts = false;
1579

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

    
1598
		hideClass('adnlopts', !showadvopts);
1599

    
1600
		if (showadvopts) {
1601
			text = "<?=gettext('Hide Advanced');?>";
1602
		} else {
1603
			text = "<?=gettext('Display Advanced');?>";
1604
		}
1605
		$('#btnadvopts').html('<i class="fa fa-cog"></i> ' + text);
1606
	}
1607

    
1608
	$('#btnadvopts').click(function(event) {
1609
		show_advopts();
1610
	});
1611

    
1612
	// Show advanced Network Booting options ===========================================================================
1613
	var showadvnwkboot = false;
1614

    
1615
	function show_advnwkboot(ispageload) {
1616
		var text;
1617
		// On page load decide the initial state based on the data.
1618
		if (ispageload) {
1619
<?php
1620
			if (empty($pconfig['netboot'])) {
1621
				$showadv = false;
1622
			} else {
1623
				$showadv = true;
1624
			}
1625
?>
1626
			showadvnwkboot = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1627
		} else {
1628
			// It was a click, swap the state.
1629
			showadvnwkboot = !showadvnwkboot;
1630
		}
1631

    
1632
		hideCheckbox('netboot', !showadvnwkboot);
1633
		hideInput('nextserver', !showadvnwkboot);
1634
		hideInput('filename', !showadvnwkboot);
1635
		hideInput('filename32', !showadvnwkboot);
1636
		hideInput('filename64', !showadvnwkboot);
1637
		hideInput('rootpath', !showadvnwkboot);
1638

    
1639
		if (showadvnwkboot) {
1640
			text = "<?=gettext('Hide Advanced');?>";
1641
		} else {
1642
			text = "<?=gettext('Display Advanced');?>";
1643
		}
1644
		$('#btnadvnwkboot').html('<i class="fa fa-cog"></i> ' + text);
1645
	}
1646

    
1647
	$('#btnadvnwkboot').click(function(event) {
1648
		show_advnwkboot();
1649
	});
1650

    
1651
	// ---------- On initial page load ------------------------------------------------------------
1652

    
1653
	show_advdns(true);
1654
	show_advmac(true);
1655
	show_advntp(true);
1656
	show_advtftp(true);
1657
	show_advldap(true);
1658
	show_advopts(true);
1659
	show_advnwkboot(true);
1660

    
1661
	// Suppress "Delete row" button if there are fewer than two rows
1662
	checkLastRow();
1663
});
1664
//]]>
1665
</script>
1666

    
1667
<?php include("foot.inc");
(117-117/225)