Project

General

Profile

Download (52.5 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['ddnsforcehostname'] = isset($dhcpdconf['ddnsforcehostname']);
211
	$pconfig['mac_allow'] = $dhcpdconf['mac_allow'];
212
	$pconfig['mac_deny'] = $dhcpdconf['mac_deny'];
213
	list($pconfig['ntp1'], $pconfig['ntp2']) = $dhcpdconf['ntpserver'];
214
	$pconfig['tftp'] = $dhcpdconf['tftp'];
215
	$pconfig['ldap'] = $dhcpdconf['ldap'];
216
	$pconfig['netboot'] = isset($dhcpdconf['netboot']);
217
	$pconfig['nextserver'] = $dhcpdconf['nextserver'];
218
	$pconfig['filename'] = $dhcpdconf['filename'];
219
	$pconfig['filename32'] = $dhcpdconf['filename32'];
220
	$pconfig['filename64'] = $dhcpdconf['filename64'];
221
	$pconfig['rootpath'] = $dhcpdconf['rootpath'];
222
	$pconfig['netmask'] = $dhcpdconf['netmask'];
223
	$pconfig['numberoptions'] = $dhcpdconf['numberoptions'];
224
	$pconfig['statsgraph'] = $dhcpdconf['statsgraph'];
225
}
226

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

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

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

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

    
243
	return true;
244
}
245

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

    
248
	unset($input_errors);
249

    
250
	$pconfig = $_POST;
251

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
509
		$dhcpd_enable_changed = false;
510

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

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

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

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

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

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

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

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

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

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

    
608
		$dhcpdconf['numberoptions'] = $numberoptions;
609

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

    
618
		write_config();
619
	}
620
}
621

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

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

    
655
	$savemsg = get_std_save_message($retval);
656
}
657

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

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

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

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

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

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

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

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

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

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

    
722
	$pooltbl .=			'</tbody>';
723
	$pooltbl .=		'</table>';
724
	$pooltbl .= '</div>';
725

    
726
	return($pooltbl);
727
}
728

    
729
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
730

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

    
736
include("head.inc");
737

    
738
if ($input_errors) {
739
	print_input_errors($input_errors);
740
}
741

    
742
if ($savemsg) {
743
	print_info_box($savemsg, 'success');
744
}
745

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

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

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

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

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

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

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

    
784
display_top_tabs($tab_array);
785

    
786
$form = new Form();
787

    
788
$section = new Form_Section('General Options');
789

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

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

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

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

    
822

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

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

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

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

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

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

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

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

    
872
$group = new Form_Group('Range');
873

    
874
$group->add(new Form_IpAddress(
875
	'range_from',
876
	null,
877
	$pconfig['range_from'],
878
	'V4'
879
))->setHelp('From');
880

    
881
$group->add(new Form_IpAddress(
882
	'range_to',
883
	null,
884
	$pconfig['range_to'],
885
	'V4'
886
))->setHelp('To');
887

    
888
$section->add($group);
889

    
890
$form->add($section);
891

    
892
if (!is_numeric($pool) && !($act == "newpool")) {
893
	$section = new Form_Section('Additional Pools');
894

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

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

    
908
	if (is_array($a_pools)) {
909
		$section->addInput(new Form_StaticText(
910
			null,
911
			build_pooltable()
912
		));
913
	}
914

    
915
	$form->add($section);
916
}
917

    
918
$section = new Form_Section('Servers');
919

    
920
$section->addInput(new Form_IpAddress(
921
	'wins1',
922
	'WINS servers',
923
	$pconfig['wins1'],
924
	'V4'
925
))->setAttribute('placeholder', 'WINS Server 1');
926

    
927
$section->addInput(new Form_IpAddress(
928
	'wins2',
929
	null,
930
	$pconfig['wins2'],
931
	'V4'
932
))->setAttribute('placeholder', 'WINS Server 2');
933

    
934
for ($idx=1; $idx<=4; $idx++) {
935
	$section->addInput(new Form_IpAddress(
936
		'dns' . $idx,
937
		($idx == 1) ? 'DNS servers':null,
938
		$pconfig['dns' . $idx],
939
		'V4'
940
	))->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.':'');
941
}
942

    
943
$form->add($section);
944

    
945
$section = new Form_Section('Other Options');
946

    
947
$section->addInput(new Form_IpAddress(
948
	'gateway',
949
	'Gateway',
950
	$pconfig['gateway'],
951
	'V4'
952
))->setPattern('[.a-zA-Z0-9_]+')
953
  ->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.');
954

    
955
$section->addInput(new Form_Input(
956
	'domain',
957
	'Domain name',
958
	'text',
959
	$pconfig['domain']
960
))->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.');
961

    
962
$section->addInput(new Form_Input(
963
	'domainsearchlist',
964
	'Domain search list',
965
	'text',
966
	$pconfig['domainsearchlist']
967
))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator.');
968

    
969
$section->addInput(new Form_Input(
970
	'deftime',
971
	'Default lease time',
972
	'number',
973
	$pconfig['deftime']
974
))->setHelp('This is used for clients that do not ask for a specific expiration time. The default is 7200 seconds.');
975

    
976
$section->addInput(new Form_Input(
977
	'maxtime',
978
	'Maximum lease time',
979
	'number',
980
	$pconfig['maxtime']
981
))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds.');
982

    
983
if (!is_numeric($pool) && !($act == "newpool")) {
984
	$section->addInput(new Form_IpAddress(
985
		'failover_peerip',
986
		'Failover peer IP',
987
		$pconfig['failover_peerip'],
988
		'V4'
989
	))->setHelp('Leave blank to disable. Enter the interface IP address of the other machine. Machines must be using CARP. ' .
990
				'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).');
991
}
992

    
993
if (!is_numeric($pool) && !($act == "newpool")) {
994
	$section->addInput(new Form_Checkbox(
995
		'staticarp',
996
		'Static ARP',
997
		'Enable Static ARP entries',
998
		$pconfig['staticarp']
999
	))->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.');
1000

    
1001
	$section->addInput(new Form_Checkbox(
1002
		'dhcpleaseinlocaltime',
1003
		'Time format change',
1004
		'Change DHCP display lease time from UTC to local time',
1005
		$pconfig['dhcpleaseinlocaltime']
1006
	))->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.' .
1007
				' This will be used for all DHCP interfaces lease time.');
1008
	$section->addInput(new Form_Checkbox(
1009
		'statsgraph',
1010
		'Statistics graphs',
1011
		'Enable RRD statistics graphs',
1012
		$pconfig['statsgraph']
1013
	))->setHelp('Enable this to add DHCP leases statistics to the RRD graphs. Disabled by default.');
1014
}
1015

    
1016
// DDNS
1017
$btnadv = new Form_Button(
1018
	'btnadvdns',
1019
	'Display Advanced',
1020
	null,
1021
	'fa-cog'
1022
);
1023

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

    
1026
$section->addInput(new Form_StaticText(
1027
	'Dynamic DNS',
1028
	$btnadv
1029
));
1030

    
1031
$section->addInput(new Form_Checkbox(
1032
	'ddnsupdate',
1033
	null,
1034
	'Enable registration of DHCP client names in DNS',
1035
	$pconfig['ddnsupdate']
1036
));
1037

    
1038
$section->addInput(new Form_Input(
1039
	'ddnsdomain',
1040
	'DDNS Domain',
1041
	'text',
1042
	$pconfig['ddnsdomain']
1043
))->setHelp('Leave blank to disable dynamic DNS registration.' . '<br />' .
1044
			'Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
1045

    
1046
$section->addInput(new Form_Checkbox(
1047
	'ddnsforcehostname',
1048
	'DDNS Hostnames',
1049
	'Force dynamic DNS hostname to be the same as configured hostname for Static Mappings',
1050
	$pconfig['ddnsforcehostname']
1051
))->setHelp('Default registers host name option supplied by DHCP client.');
1052

    
1053
$section->addInput(new Form_IpAddress(
1054
	'ddnsdomainprimary',
1055
	'Primary DDNS address',
1056
	$pconfig['ddnsdomainprimary'],
1057
	'V4'
1058
))->setHelp('Primary domain name server IP address for the dynamic domain name.');
1059

    
1060
$section->addInput(new Form_Input(
1061
	'ddnsdomainkeyname',
1062
	'DNS Domain key',
1063
	'text',
1064
	$pconfig['ddnsdomainkeyname']
1065
))->setHelp('Dynamic DNS domain key name which will be used to register client names in the DNS server.');
1066

    
1067
$section->addInput(new Form_Input(
1068
	'ddnsdomainkey',
1069
	'DNS Domain key secret',
1070
	'text',
1071
	$pconfig['ddnsdomainkey']
1072
))->setHelp('Dynamic DNS domain key secret (HMAC-MD5) which will be used to register client names in the DNS server.');
1073

    
1074
// Advanced MAC
1075
$btnadv = new Form_Button(
1076
	'btnadvmac',
1077
	'Display Advanced',
1078
	null,
1079
	'fa-cog'
1080
);
1081

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

    
1084
$section->addInput(new Form_StaticText(
1085
	'MAC address control',
1086
	$btnadv
1087
));
1088

    
1089
$section->addInput(new Form_Input(
1090
	'mac_allow',
1091
	'MAC Allow',
1092
	'text',
1093
	$pconfig['mac_allow']
1094
))->setHelp('List of partial MAC addresses to allow, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1095

    
1096
$section->addInput(new Form_Input(
1097
	'mac_deny',
1098
	'MAC Deny',
1099
	'text',
1100
	$pconfig['mac_deny']
1101
))->setHelp('List of partial MAC addresses to deny access, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1102

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

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

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

    
1118
$section->addInput(new Form_IpAddress(
1119
	'ntp1',
1120
	'NTP Server 1',
1121
	$pconfig['ntp1'],
1122
	'V4'
1123
))->setPattern('[.a-zA-Z0-9-]+');
1124

    
1125
$section->addInput(new Form_IpAddress(
1126
	'ntp2',
1127
	'NTP Server 2',
1128
	$pconfig['ntp2'],
1129
	'V4'
1130
))->setPattern('[.a-zA-Z0-9-]+');
1131

    
1132
// Advanced TFTP
1133
$btnadv = new Form_Button(
1134
	'btnadvtftp',
1135
	'Display Advanced',
1136
	null,
1137
	'fa-cog'
1138
);
1139

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

    
1142
$section->addInput(new Form_StaticText(
1143
	'TFTP',
1144
	$btnadv
1145
));
1146

    
1147
$section->addInput(new Form_Input(
1148
	'tftp',
1149
	'TFTP Server',
1150
	$pconfig['tftp']
1151
))->setHelp('Leave blank to disable. Enter a valid IP address, hostname or URL for the TFTP server.');
1152

    
1153
// Advanced LDAP
1154
$btnadv = new Form_Button(
1155
	'btnadvldap',
1156
	'Display Advanced',
1157
	null,
1158
	'fa-cog'
1159
);
1160

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

    
1163
$section->addInput(new Form_StaticText(
1164
	'LDAP',
1165
	$btnadv
1166
));
1167

    
1168
$section->addInput(new Form_Input(
1169
	'ldap',
1170
	'LDAP Server URI',
1171
	'text',
1172
	$pconfig['ldap']
1173
))->setHelp('Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com ');
1174

    
1175
// Advanced Network Booting options
1176
$btnadv = new Form_Button(
1177
	'btnadvnwkboot',
1178
	'Display Advanced',
1179
	null,
1180
	'fa-cog'
1181
);
1182

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

    
1185
$section->addInput(new Form_StaticText(
1186
	'Network Booting',
1187
	$btnadv
1188
));
1189

    
1190
$section->addInput(new Form_Checkbox(
1191
	'netboot',
1192
	'Enable',
1193
	'Enables network booting',
1194
	$pconfig['netboot']
1195
));
1196

    
1197
$section->addInput(new Form_IpAddress(
1198
	'nextserver',
1199
	'Next Server',
1200
	$pconfig['nextserver'],
1201
	'V4'
1202
))->setHelp('Enter the IP address of the next server');
1203

    
1204
$section->addInput(new Form_Input(
1205
	'filename',
1206
	'Default BIOS file name',
1207
	'text',
1208
	$pconfig['filename']
1209
));
1210

    
1211
$section->addInput(new Form_Input(
1212
	'filename32',
1213
	'UEFI 32 bit file name',
1214
	'text',
1215
	$pconfig['filename32']
1216
));
1217

    
1218
$section->addInput(new Form_Input(
1219
	'filename64',
1220
	'UEFI 64 bit file name',
1221
	'text',
1222
	$pconfig['filename64']
1223
))->setHelp('Both a filename and a boot server must be configured for this to work! ' .
1224
			'All three filenames and a configured boot server are necessary for UEFI to work! ');
1225

    
1226
$section->addInput(new Form_Input(
1227
	'rootpath',
1228
	'Root path',
1229
	'text',
1230
	$pconfig['rootpath']
1231
))->setHelp('string-format: iscsi:(servername):(protocol):(port):(LUN):targetname ');
1232

    
1233
// Advanced Additional options
1234
$btnadv = new Form_Button(
1235
	'btnadvopts',
1236
	'Display Advanced',
1237
	null,
1238
	'fa-cog'
1239
);
1240

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

    
1243
$section->addInput(new Form_StaticText(
1244
	'Additional BOOTP/DHCP Options',
1245
	$btnadv
1246
));
1247

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

    
1250
$section = new Form_Section('Additional BOOTP/DHCP Options');
1251
$section->addClass('adnlopts');
1252

    
1253
$section->addInput(new Form_StaticText(
1254
	null,
1255
	'<div class="alert alert-info"> ' . gettext('Enter the DHCP option number and the value for each item to include in the DHCP lease information.') . ' ' .
1256
	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>')
1257
));
1258

    
1259
if (!$pconfig['numberoptions']) {
1260
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1261
}
1262

    
1263
$customitemtypes = array(
1264
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1265
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1266
	'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')
1267
);
1268

    
1269
$numrows = count($item) -1;
1270
$counter = 0;
1271

    
1272
$numrows = count($pconfig['numberoptions']['item']) -1;
1273

    
1274
foreach ($pconfig['numberoptions']['item'] as $item) {
1275
	$number = $item['number'];
1276
	$itemtype = $item['type'];
1277
	$value = base64_decode($item['value']);
1278

    
1279
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1280
	$group->addClass('repeatable');
1281

    
1282
	$group->add(new Form_Input(
1283
		'number' . $counter,
1284
		null,
1285
		'text',
1286
		$number
1287
	))->setHelp($numrows == $counter ? 'Number':null);
1288

    
1289

    
1290
	$group->add(new Form_Select(
1291
		'itemtype' . $counter,
1292
		null,
1293
		$itemtype,
1294
		$customitemtypes
1295
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1296

    
1297
	$group->add(new Form_Input(
1298
		'value' . $counter,
1299
		null,
1300
		'text',
1301
		$value
1302
	))->setHelp($numrows == $counter ? 'Value':null);
1303

    
1304
	$group->add(new Form_Button(
1305
		'deleterow' . $counter,
1306
		'Delete',
1307
		null,
1308
		'fa-trash'
1309
	))->addClass('btn-warning');
1310

    
1311
	$section->add($group);
1312

    
1313
	$counter++;
1314
}
1315

    
1316
$section->addInput(new Form_Button(
1317
	'addrow',
1318
	'Add',
1319
	null,
1320
	'fa-plus'
1321
))->addClass('btn-success');
1322

    
1323
$form->add($section);
1324

    
1325
if ($act == "newpool") {
1326
	$form->addGlobal(new Form_Input(
1327
		'act',
1328
		null,
1329
		'hidden',
1330
		'newpool'
1331
	));
1332
}
1333

    
1334
if (is_numeric($pool)) {
1335
	$form->addGlobal(new Form_Input(
1336
		'pool',
1337
		null,
1338
		'hidden',
1339
		$pool
1340
	));
1341
}
1342

    
1343
$form->addGlobal(new Form_Input(
1344
	'if',
1345
	null,
1346
	'hidden',
1347
	$if
1348
));
1349

    
1350
print($form);
1351

    
1352
// DHCP Static Mappings table
1353

    
1354
if (!is_numeric($pool) && !($act == "newpool")) {
1355
?>
1356

    
1357
<div class="panel panel-default">
1358
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("DHCP Static Mappings for this Interface")?></h2></div>
1359
	<div class="table-responsive">
1360
			<table class="table table-striped table-hover table-condensed sortable-theme-bootstrap" data-sortable>
1361
				<thead>
1362
					<tr>
1363
						<th><?=gettext("Static ARP")?></th>
1364
						<th><?=gettext("MAC address")?></th>
1365
						<th><?=gettext("IP address")?></th>
1366
						<th><?=gettext("Hostname")?></th>
1367
						<th><?=gettext("Description")?></th>
1368
						<th></th>
1369
					</tr>
1370
				</thead>
1371
<?php
1372
	if (is_array($a_maps)) {
1373
		$i = 0;
1374
?>
1375
				<tbody>
1376
<?php
1377
		foreach ($a_maps as $mapent) {
1378
?>
1379
					<tr>
1380
						<td class="text-center" ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1381
							<?php if (isset($mapent['arp_table_static_entry'])): ?>
1382
								<i class="fa fa-check"></i>
1383
							<?php endif; ?>
1384
						</td>
1385
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1386
							<?=htmlspecialchars($mapent['mac'])?>
1387
						</td>
1388
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1389
							<?=htmlspecialchars($mapent['ipaddr'])?>
1390
						</td>
1391
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1392
							<?=htmlspecialchars($mapent['hostname'])?>
1393
						</td>
1394
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1395
							<?=htmlspecialchars($mapent['descr'])?>
1396
						</td>
1397
						<td>
1398
							<a class="fa fa-pencil"	title="<?=gettext('Edit static mapping')?>"	href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>"></a>
1399
							<a class="fa fa-trash"	title="<?=gettext('Delete static mapping')?>"	href="services_dhcp.php?if=<?=htmlspecialchars($if)?>&amp;act=del&amp;id=<?=$i?>"></a>
1400
						</td>
1401
					</tr>
1402
<?php
1403
		$i++;
1404
		}
1405
?>
1406
				</tbody>
1407
<?php
1408
	}
1409
?>
1410
		</table>
1411
	</div>
1412
</div>
1413

    
1414
<nav class="action-buttons">
1415
	<a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>" class="btn btn-sm btn-success">
1416
		<i class="fa fa-plus icon-embed-btn"></i>
1417
		<?=gettext("Add")?>
1418
	</a>
1419
</nav>
1420
<?php
1421
}
1422
?>
1423

    
1424
<script type="text/javascript">
1425
//<![CDATA[
1426
events.push(function() {
1427

    
1428
	// Show advanced DNS options ======================================================================================
1429
	var showadvdns = false;
1430

    
1431
	function show_advdns(ispageload) {
1432
		var text;
1433
		// On page load decide the initial state based on the data.
1434
		if (ispageload) {
1435
<?php
1436
			if (!$pconfig['ddnsupdate'] && !$pconfig['ddnsforcehostname'] && empty($pconfig['ddnsdomain']) && empty($pconfig['ddnsdomainprimary']) &&
1437
			    empty($pconfig['ddnsdomainkeyname']) && empty($pconfig['ddnsdomainkey'])) {
1438
				$showadv = false;
1439
			} else {
1440
				$showadv = true;
1441
			}
1442
?>
1443
			showadvdns = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1444
		} else {
1445
			// It was a click, swap the state.
1446
			showadvdns = !showadvdns;
1447
		}
1448

    
1449
		hideCheckbox('ddnsupdate', !showadvdns);
1450
		hideInput('ddnsdomain', !showadvdns);
1451
		hideCheckbox('ddnsforcehostname', !showadvdns);
1452
		hideInput('ddnsdomainprimary', !showadvdns);
1453
		hideInput('ddnsdomainkeyname', !showadvdns);
1454
		hideInput('ddnsdomainkey', !showadvdns);
1455

    
1456
		if (showadvdns) {
1457
			text = "<?=gettext('Hide Advanced');?>";
1458
		} else {
1459
			text = "<?=gettext('Display Advanced');?>";
1460
		}
1461
		$('#btnadvdns').html('<i class="fa fa-cog"></i> ' + text);
1462
	}
1463

    
1464
	$('#btnadvdns').click(function(event) {
1465
		show_advdns();
1466
	});
1467

    
1468
	// Show advanced MAC options ======================================================================================
1469
	var showadvmac = false;
1470

    
1471
	function show_advmac(ispageload) {
1472
		var text;
1473
		// On page load decide the initial state based on the data.
1474
		if (ispageload) {
1475
<?php
1476
			if (empty($pconfig['mac_allow']) && empty($pconfig['mac_deny'])) {
1477
				$showadv = false;
1478
			} else {
1479
				$showadv = true;
1480
			}
1481
?>
1482
			showadvmac = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1483
		} else {
1484
			// It was a click, swap the state.
1485
			showadvmac = !showadvmac;
1486
		}
1487

    
1488
		hideInput('mac_allow', !showadvmac);
1489
		hideInput('mac_deny', !showadvmac);
1490

    
1491
		if (showadvmac) {
1492
			text = "<?=gettext('Hide Advanced');?>";
1493
		} else {
1494
			text = "<?=gettext('Display Advanced');?>";
1495
		}
1496
		$('#btnadvmac').html('<i class="fa fa-cog"></i> ' + text);
1497
	}
1498

    
1499
	$('#btnadvmac').click(function(event) {
1500
		show_advmac();
1501
	});
1502

    
1503
	// Show advanced NTP options ======================================================================================
1504
	var showadvntp = false;
1505

    
1506
	function show_advntp(ispageload) {
1507
		var text;
1508
		// On page load decide the initial state based on the data.
1509
		if (ispageload) {
1510
<?php
1511
			if (empty($pconfig['ntp1']) && empty($pconfig['ntp2'])) {
1512
				$showadv = false;
1513
			} else {
1514
				$showadv = true;
1515
			}
1516
?>
1517
			showadvntp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1518
		} else {
1519
			// It was a click, swap the state.
1520
			showadvntp = !showadvntp;
1521
		}
1522

    
1523
		hideInput('ntp1', !showadvntp);
1524
		hideInput('ntp2', !showadvntp);
1525

    
1526
		if (showadvntp) {
1527
			text = "<?=gettext('Hide Advanced');?>";
1528
		} else {
1529
			text = "<?=gettext('Display Advanced');?>";
1530
		}
1531
		$('#btnadvntp').html('<i class="fa fa-cog"></i> ' + text);
1532
	}
1533

    
1534
	$('#btnadvntp').click(function(event) {
1535
		show_advntp();
1536
	});
1537

    
1538
	// Show advanced TFTP options ======================================================================================
1539
	var showadvtftp = false;
1540

    
1541
	function show_advtftp(ispageload) {
1542
		var text;
1543
		// On page load decide the initial state based on the data.
1544
		if (ispageload) {
1545
<?php
1546
			if (empty($pconfig['tftp'])) {
1547
				$showadv = false;
1548
			} else {
1549
				$showadv = true;
1550
			}
1551
?>
1552
			showadvtftp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1553
		} else {
1554
			// It was a click, swap the state.
1555
			showadvtftp = !showadvtftp;
1556
		}
1557

    
1558
		hideInput('tftp', !showadvtftp);
1559

    
1560
		if (showadvtftp) {
1561
			text = "<?=gettext('Hide Advanced');?>";
1562
		} else {
1563
			text = "<?=gettext('Display Advanced');?>";
1564
		}
1565
		$('#btnadvtftp').html('<i class="fa fa-cog"></i> ' + text);
1566
	}
1567

    
1568
	$('#btnadvtftp').click(function(event) {
1569
		show_advtftp();
1570
	});
1571

    
1572
	// Show advanced LDAP options ======================================================================================
1573
	var showadvldap = false;
1574

    
1575
	function show_advldap(ispageload) {
1576
		var text;
1577
		// On page load decide the initial state based on the data.
1578
		if (ispageload) {
1579
<?php
1580
			if (empty($pconfig['ldap'])) {
1581
				$showadv = false;
1582
			} else {
1583
				$showadv = true;
1584
			}
1585
?>
1586
			showadvldap = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1587
		} else {
1588
			// It was a click, swap the state.
1589
			showadvldap = !showadvldap;
1590
		}
1591

    
1592
		hideInput('ldap', !showadvldap);
1593

    
1594
		if (showadvldap) {
1595
			text = "<?=gettext('Hide Advanced');?>";
1596
		} else {
1597
			text = "<?=gettext('Display Advanced');?>";
1598
		}
1599
		$('#btnadvldap').html('<i class="fa fa-cog"></i> ' + text);
1600
	}
1601

    
1602
	$('#btnadvldap').click(function(event) {
1603
		show_advldap();
1604
	});
1605

    
1606
	// Show advanced additional opts options ===========================================================================
1607
	var showadvopts = false;
1608

    
1609
	function show_advopts(ispageload) {
1610
		var text;
1611
		// On page load decide the initial state based on the data.
1612
		if (ispageload) {
1613
<?php
1614
			if (empty($pconfig['numberoptions']) ||
1615
			    (empty($pconfig['numberoptions']['item'][0]['number']) && (empty($pconfig['numberoptions']['item'][0]['value'])))) {
1616
				$showadv = false;
1617
			} else {
1618
				$showadv = true;
1619
			}
1620
?>
1621
			showadvopts = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1622
		} else {
1623
			// It was a click, swap the state.
1624
			showadvopts = !showadvopts;
1625
		}
1626

    
1627
		hideClass('adnlopts', !showadvopts);
1628

    
1629
		if (showadvopts) {
1630
			text = "<?=gettext('Hide Advanced');?>";
1631
		} else {
1632
			text = "<?=gettext('Display Advanced');?>";
1633
		}
1634
		$('#btnadvopts').html('<i class="fa fa-cog"></i> ' + text);
1635
	}
1636

    
1637
	$('#btnadvopts').click(function(event) {
1638
		show_advopts();
1639
	});
1640

    
1641
	// Show advanced Network Booting options ===========================================================================
1642
	var showadvnwkboot = false;
1643

    
1644
	function show_advnwkboot(ispageload) {
1645
		var text;
1646
		// On page load decide the initial state based on the data.
1647
		if (ispageload) {
1648
<?php
1649
			if (empty($pconfig['netboot'])) {
1650
				$showadv = false;
1651
			} else {
1652
				$showadv = true;
1653
			}
1654
?>
1655
			showadvnwkboot = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1656
		} else {
1657
			// It was a click, swap the state.
1658
			showadvnwkboot = !showadvnwkboot;
1659
		}
1660

    
1661
		hideCheckbox('netboot', !showadvnwkboot);
1662
		hideInput('nextserver', !showadvnwkboot);
1663
		hideInput('filename', !showadvnwkboot);
1664
		hideInput('filename32', !showadvnwkboot);
1665
		hideInput('filename64', !showadvnwkboot);
1666
		hideInput('rootpath', !showadvnwkboot);
1667

    
1668
		if (showadvnwkboot) {
1669
			text = "<?=gettext('Hide Advanced');?>";
1670
		} else {
1671
			text = "<?=gettext('Display Advanced');?>";
1672
		}
1673
		$('#btnadvnwkboot').html('<i class="fa fa-cog"></i> ' + text);
1674
	}
1675

    
1676
	$('#btnadvnwkboot').click(function(event) {
1677
		show_advnwkboot();
1678
	});
1679

    
1680
	// ---------- On initial page load ------------------------------------------------------------
1681

    
1682
	show_advdns(true);
1683
	show_advmac(true);
1684
	show_advntp(true);
1685
	show_advtftp(true);
1686
	show_advldap(true);
1687
	show_advopts(true);
1688
	show_advnwkboot(true);
1689

    
1690
	// Suppress "Delete row" button if there are fewer than two rows
1691
	checkLastRow();
1692
});
1693
//]]>
1694
</script>
1695

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