Project

General

Profile

Download (51.9 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
 * Licensed under the Apache License, Version 2.0 (the "License");
14
 * you may not use this file except in compliance with the License.
15
 * You may obtain a copy of the License at
16
 *
17
 * http://www.apache.org/licenses/LICENSE-2.0
18
 *
19
 * Unless required by applicable law or agreed to in writing, software
20
 * distributed under the License is distributed on an "AS IS" BASIS,
21
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
 * See the License for the specific language governing permissions and
23
 * limitations under the License.
24
 */
25

    
26
##|+PRIV
27
##|*IDENT=page-services-dhcpserver
28
##|*NAME=Services: DHCP Server
29
##|*DESCR=Allow access to the 'Services: DHCP Server' page.
30
##|*MATCH=services_dhcp.php*
31
##|-PRIV
32

    
33
require_once("guiconfig.inc");
34
require_once("filter.inc");
35
require_once('rrd.inc');
36
require_once("shaper.inc");
37

    
38
if (!$g['services_dhcp_server_enable']) {
39
	header("Location: /");
40
	exit;
41
}
42

    
43
$if = $_GET['if'];
44
if (!empty($_POST['if'])) {
45
	$if = $_POST['if'];
46
}
47

    
48
/* if OLSRD is enabled, allow WAN to house DHCP. */
49
if ($config['installedpackages']['olsrd']) {
50
	foreach ($config['installedpackages']['olsrd']['config'] as $olsrd) {
51
		if ($olsrd['enable']) {
52
			$is_olsr_enabled = true;
53
			break;
54
		}
55
	}
56
}
57

    
58
$iflist = get_configured_interface_with_descr();
59

    
60
/* set the starting interface */
61
if (!$if || !isset($iflist[$if])) {
62
	$found_starting_if = false;
63
	// First look for an interface with DHCP already enabled.
64
	foreach ($iflist as $ifent => $ifname) {
65
		$oc = $config['interfaces'][$ifent];
66
		if (is_array($config['dhcpd'][$ifent]) &&
67
		    isset($config['dhcpd'][$ifent]['enable']) &&
68
		    is_ipaddrv4($oc['ipaddr']) && $oc['subnet'] < 31) {
69
			$if = $ifent;
70
			$found_starting_if = true;
71
			break;
72
		}
73
	}
74

    
75
	/*
76
	 * If there is no DHCP-enabled interface and LAN is a candidate,
77
	 * then choose LAN.
78
	 */
79
	if (!$found_starting_if && isset($iflist['lan']) &&
80
	    is_ipaddrv4($config['interfaces']['lan']['ipaddr']) &&
81
	    $config['interfaces']['lan']['subnet'] < 31) {
82
		$if = 'lan';
83
		$found_starting_if = true;
84
	}
85

    
86
	// At the last select whatever can be found.
87
	if (!$found_starting_if) {
88
		foreach ($iflist as $ifent => $ifname) {
89
			$oc = $config['interfaces'][$ifent];
90

    
91
			/* Not static IPv4 or subnet >= 31 */
92
			if (!is_ipaddrv4($oc['ipaddr']) ||
93
			    empty($oc['subnet']) || $oc['subnet'] < 31) {
94
				continue;
95
			}
96

    
97
			if (!is_array($config['dhcpd'][$ifent]) ||
98
			    !isset($config['dhcpd'][$ifent]['enable'])) {
99
				continue;
100
			}
101

    
102
			$if = $ifent;
103
			break;
104
		}
105
	}
106
}
107

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

    
113
$a_pools = array();
114

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

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

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

    
131
	$a_pools = &$config['dhcpd'][$if]['pool'];
132

    
133
	if (is_numeric($pool) && $a_pools[$pool]) {
134
		$dhcpdconf = &$a_pools[$pool];
135
	} elseif ($act == "newpool") {
136
		$dhcpdconf = array();
137
	} else {
138
		$dhcpdconf = &$config['dhcpd'][$if];
139
	}
140

    
141
	if (!is_array($config['dhcpd'][$if]['staticmap'])) {
142
		$dhcpdconf['staticmap'] = array();
143
	}
144

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

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

    
165
		$pconfig['dhcpleaseinlocaltime'] = $dhcpleaseinlocaltime;
166
	} else {
167
		// Options that exist only in pools
168
		$pconfig['descr'] = $dhcpdconf['descr'];
169
	}
170

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

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

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

    
212
$subnet_start = gen_subnetv4($ifcfgip, $ifcfgsn);
213
$subnet_end = gen_subnetv4_max($ifcfgip, $ifcfgsn);
214

    
215
function validate_partial_mac_list($maclist) {
216
	$macs = explode(',', $maclist);
217

    
218
	// Loop through and look for invalid MACs.
219
	foreach ($macs as $mac) {
220
		if (!is_macaddr($mac, true)) {
221
			return false;
222
		}
223
	}
224

    
225
	return true;
226
}
227

    
228
if (isset($_POST['save'])) {
229

    
230
	unset($input_errors);
231

    
232
	$pconfig = $_POST;
233

    
234
	$numberoptions = array();
235
	for ($x = 0; $x < 99; $x++) {
236
		if (isset($_POST["number{$x}"]) && ctype_digit($_POST["number{$x}"])) {
237
			$numbervalue = array();
238
			$numbervalue['number'] = htmlspecialchars($_POST["number{$x}"]);
239
			$numbervalue['type'] = htmlspecialchars($_POST["itemtype{$x}"]);
240
			$numbervalue['value'] = base64_encode($_POST["value{$x}"]);
241
			$numberoptions['item'][] = $numbervalue;
242
		}
243
	}
244

    
245
	// Reload the new pconfig variable that the form uses.
246
	$pconfig['numberoptions'] = $numberoptions;
247

    
248
	/* input validation */
249

    
250
	// Note: if DHCP Server is not enabled, then it is OK to adjust other parameters without specifying range from-to.
251
	if ($_POST['enable'] || is_numeric($pool) || $act == "newpool") {
252
		$reqdfields = explode(" ", "range_from range_to");
253
		$reqdfieldsn = array(gettext("Range begin"), gettext("Range end"));
254

    
255
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
256
	}
257

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

    
262
	if ($_POST['range_from'] && !is_ipaddrv4($_POST['range_from'])) {
263
		$input_errors[] = gettext("A valid IPv4 address must be specified for range from.");
264
	}
265
	if ($_POST['range_to'] && !is_ipaddrv4($_POST['range_to'])) {
266
		$input_errors[] = gettext("A valid IPv4 address must be specified for range to.");
267
	}
268
	if (($_POST['range_from'] && !$_POST['range_to']) || ($_POST['range_to'] && !$_POST['range_from'])) {
269
		$input_errors[] = gettext("Range From and Range To must both be entered.");
270
	}
271
	if (($_POST['gateway'] && $_POST['gateway'] != "none" && !is_ipaddrv4($_POST['gateway']))) {
272
		$input_errors[] = gettext("A valid IP address must be specified for the gateway.");
273
	}
274
	if (($_POST['wins1'] && !is_ipaddrv4($_POST['wins1'])) || ($_POST['wins2'] && !is_ipaddrv4($_POST['wins2']))) {
275
		$input_errors[] = gettext("A valid IP address must be specified for the primary/secondary WINS servers.");
276
	}
277
	$parent_ip = get_interface_ip($_POST['if']);
278
	if (is_ipaddrv4($parent_ip) && $_POST['gateway'] && $_POST['gateway'] != "none") {
279
		$parent_sn = get_interface_subnet($_POST['if']);
280
		if (!ip_in_subnet($_POST['gateway'], gen_subnet($parent_ip, $parent_sn) . "/" . $parent_sn) && !ip_in_interface_alias_subnet($_POST['if'], $_POST['gateway'])) {
281
			$input_errors[] = sprintf(gettext("The gateway address %s does not lie within the chosen interface's subnet."), $_POST['gateway']);
282
		}
283
	}
284

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

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

    
293
	if (isset($config['captiveportal']) && is_array($config['captiveportal'])) {
294
		$deftime = 7200; // Default value if it's empty
295
		if (is_numeric($_POST['deftime'])) {
296
			$deftime = $_POST['deftime'];
297
		}
298

    
299
		foreach ($config['captiveportal'] as $cpZone => $cpdata) {
300
			if (!isset($cpdata['enable'])) {
301
				continue;
302
			}
303
			if (!isset($cpdata['timeout']) || !is_numeric($cpdata['timeout'])) {
304
				continue;
305
			}
306
			$cp_ifs = explode(',', $cpdata['interface']);
307
			if (!in_array($if, $cp_ifs)) {
308
				continue;
309
			}
310
			if ($cpdata['timeout'] > $deftime) {
311
				$input_errors[] = sprintf(gettext(
312
					'The Captive Portal zone (%1$s) has Hard Timeout parameter set to a value bigger than Default lease time (%2$s).'), $cpZone, $deftime);
313
			}
314
		}
315
	}
316

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

    
340
	// Validate MACs
341
	if (!empty($_POST['mac_allow']) && !validate_partial_mac_list($_POST['mac_allow'])) {
342
		$input_errors[] = gettext("If a mac allow list is specified, it must contain only valid partial MAC addresses.");
343
	}
344
	if (!empty($_POST['mac_deny']) && !validate_partial_mac_list($_POST['mac_deny'])) {
345
		$input_errors[] = gettext("If a mac deny list is specified, it must contain only valid partial MAC addresses.");
346
	}
347

    
348
	if (($_POST['ntp1'] && (!is_ipaddrv4($_POST['ntp1']) && !is_hostname($_POST['ntp1']))) || ($_POST['ntp2'] && (!is_ipaddrv4($_POST['ntp2']) && !is_hostname($_POST['ntp2'])))) {
349
		$input_errors[] = gettext("A valid IP address or hostname must be specified for the primary/secondary NTP servers.");
350
	}
351
	if (($_POST['domain'] && !is_domain($_POST['domain']))) {
352
		$input_errors[] = gettext("A valid domain name must be specified for the DNS domain.");
353
	}
354
	if ($_POST['tftp'] && !is_ipaddrv4($_POST['tftp']) && !is_domain($_POST['tftp']) && !filter_var($_POST['tftp'], FILTER_VALIDATE_URL)) {
355
		$input_errors[] = gettext("A valid IP address, hostname or URL must be specified for the TFTP server.");
356
	}
357
	if (($_POST['nextserver'] && !is_ipaddrv4($_POST['nextserver']))) {
358
		$input_errors[] = gettext("A valid IP address must be specified for the network boot server.");
359
	}
360

    
361
	if (gen_subnet($ifcfgip, $ifcfgsn) == $_POST['range_from']) {
362
		$input_errors[] = gettext("The network address cannot be used in the starting subnet range.");
363
	}
364
	if (gen_subnet_max($ifcfgip, $ifcfgsn) == $_POST['range_to']) {
365
		$input_errors[] = gettext("The broadcast address cannot be used in the ending subnet range.");
366
	}
367

    
368
	// Disallow a range that includes the virtualip
369
	if (is_array($config['virtualip']['vip'])) {
370
		foreach ($config['virtualip']['vip'] as $vip) {
371
			if ($vip['interface'] == $if) {
372
				if ($vip['subnet'] && is_inrange_v4($vip['subnet'], $_POST['range_from'], $_POST['range_to'])) {
373
					$input_errors[] = sprintf(gettext("The subnet range cannot overlap with virtual IP address %s."), $vip['subnet']);
374
				}
375
			}
376
		}
377
	}
378

    
379
	$noip = false;
380
	if (is_array($a_maps)) {
381
		foreach ($a_maps as $map) {
382
			if (empty($map['ipaddr'])) {
383
				$noip = true;
384
			}
385
		}
386
	}
387

    
388
	if ($_POST['staticarp'] && $noip) {
389
		$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.");
390
	}
391

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

    
419
	/* If enabling DHCP Server, make sure that the DHCP Relay isn't enabled on this interface */
420
	if ($_POST['enable'] && isset($config['dhcrelay']['enable']) && (stristr($config['dhcrelay']['interface'], $if) !== false)) {
421
		$input_errors[] = sprintf(gettext("The DHCP relay on the %s interface must be disabled before enabling the DHCP server."), $iflist[$if]);
422
	}
423

    
424
	// If nothing is wrong so far, and we have range from and to, then check conditions related to the values of range from and to.
425
	if (!$input_errors && $_POST['range_from'] && $_POST['range_to']) {
426
		/* make sure the range lies within the current subnet */
427
		if (ip_greater_than($_POST['range_from'], $_POST['range_to'])) {
428
			$input_errors[] = gettext("The range is invalid (first element higher than second element).");
429
		}
430

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

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

    
447
		foreach ($a_pools as $id => $p) {
448
			if (is_numeric($pool) && ($id == $pool)) {
449
				continue;
450
			}
451

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

    
461
		if (is_array($a_maps)) {
462
			foreach ($a_maps as $map) {
463
				if (empty($map['ipaddr'])) {
464
					continue;
465
				}
466
				if (is_inrange_v4($map['ipaddr'], $_POST['range_from'], $_POST['range_to'])) {
467
					$input_errors[] = sprintf(gettext("The DHCP range cannot overlap any static DHCP mappings."));
468
					break;
469
				}
470
			}
471
		}
472
	}
473

    
474
	if (!$input_errors) {
475
		if (!is_numeric($pool)) {
476
			if ($act == "newpool") {
477
				$dhcpdconf = array();
478
			} else {
479
				if (!is_array($config['dhcpd'][$if])) {
480
					$config['dhcpd'][$if] = array();
481
				}
482
				$dhcpdconf = $config['dhcpd'][$if];
483
			}
484
		} else {
485
			if (is_array($a_pools[$pool])) {
486
				$dhcpdconf = $a_pools[$pool];
487
			} else {
488
				// Someone specified a pool but it doesn't exist. Punt.
489
				header("Location: services_dhcp.php");
490
				exit;
491
			}
492
		}
493
		if (!is_array($dhcpdconf['range'])) {
494
			$dhcpdconf['range'] = array();
495
		}
496

    
497
		$dhcpd_enable_changed = false;
498

    
499
		// Global Options
500
		if (!is_numeric($pool) && !($act == "newpool")) {
501
			$old_dhcpd_enable = isset($dhcpdconf['enable']);
502
			$new_dhcpd_enable = ($_POST['enable']) ? true : false;
503
			if ($old_dhcpd_enable != $new_dhcpd_enable) {
504
				/* DHCP has been enabled or disabled. The pf ruleset will need to be rebuilt to allow or disallow DHCP. */
505
				$dhcpd_enable_changed = true;
506
			}
507

    
508
			$dhcpdconf['enable'] = $new_dhcpd_enable;
509
			$dhcpdconf['staticarp'] = ($_POST['staticarp']) ? true : false;
510
			$previous = $dhcpdconf['failover_peerip'];
511
			if ($previous != $_POST['failover_peerip']) {
512
				mwexec("/bin/rm -rf /var/dhcpd/var/db/*");
513
			}
514

    
515
			$dhcpdconf['failover_peerip'] = $_POST['failover_peerip'];
516
			// dhcpleaseinlocaltime is global to all interfaces. So update the setting on all interfaces.
517
			foreach ($config['dhcpd'] as &$dhcpdifitem) {
518
				$dhcpdifitem['dhcpleaseinlocaltime'] = $_POST['dhcpleaseinlocaltime'];
519
			}
520
		} else {
521
			// Options that exist only in pools
522
			$dhcpdconf['descr'] = $_POST['descr'];
523
		}
524

    
525
		// Options that can be global or per-pool.
526
		$dhcpdconf['range']['from'] = $_POST['range_from'];
527
		$dhcpdconf['range']['to'] = $_POST['range_to'];
528
		$dhcpdconf['defaultleasetime'] = $_POST['deftime'];
529
		$dhcpdconf['maxleasetime'] = $_POST['maxtime'];
530
		$dhcpdconf['netmask'] = $_POST['netmask'];
531

    
532
		unset($dhcpdconf['winsserver']);
533
		if ($_POST['wins1']) {
534
			$dhcpdconf['winsserver'][] = $_POST['wins1'];
535
		}
536
		if ($_POST['wins2']) {
537
			$dhcpdconf['winsserver'][] = $_POST['wins2'];
538
		}
539

    
540
		unset($dhcpdconf['dnsserver']);
541
		if ($_POST['dns1']) {
542
			$dhcpdconf['dnsserver'][] = $_POST['dns1'];
543
		}
544
		if ($_POST['dns2']) {
545
			$dhcpdconf['dnsserver'][] = $_POST['dns2'];
546
		}
547
		if ($_POST['dns3']) {
548
			$dhcpdconf['dnsserver'][] = $_POST['dns3'];
549
		}
550
		if ($_POST['dns4']) {
551
			$dhcpdconf['dnsserver'][] = $_POST['dns4'];
552
		}
553

    
554
		$dhcpdconf['gateway'] = $_POST['gateway'];
555
		$dhcpdconf['domain'] = $_POST['domain'];
556
		$dhcpdconf['domainsearchlist'] = $_POST['domainsearchlist'];
557
		$dhcpdconf['ignorebootp'] = ($_POST['ignorebootp']) ? true : false;
558
		$dhcpdconf['denyunknown'] = ($_POST['denyunknown']) ? true : false;
559
		$dhcpdconf['nonak'] = ($_POST['nonak']) ? true : false;
560
		$dhcpdconf['ddnsdomain'] = $_POST['ddnsdomain'];
561
		$dhcpdconf['ddnsdomainprimary'] = $_POST['ddnsdomainprimary'];
562
		$dhcpdconf['ddnsdomainkeyname'] = $_POST['ddnsdomainkeyname'];
563
		$dhcpdconf['ddnsdomainkey'] = $_POST['ddnsdomainkey'];
564
		$dhcpdconf['ddnsupdate'] = ($_POST['ddnsupdate']) ? true : false;
565
		$dhcpdconf['ddnsforcehostname'] = ($_POST['ddnsforcehostname']) ? true : false;
566
		$dhcpdconf['mac_allow'] = $_POST['mac_allow'];
567
		$dhcpdconf['mac_deny'] = $_POST['mac_deny'];
568

    
569
		unset($dhcpdconf['ntpserver']);
570
		if ($_POST['ntp1']) {
571
			$dhcpdconf['ntpserver'][] = $_POST['ntp1'];
572
		}
573
		if ($_POST['ntp2']) {
574
			$dhcpdconf['ntpserver'][] = $_POST['ntp2'];
575
		}
576

    
577
		$dhcpdconf['tftp'] = $_POST['tftp'];
578
		$dhcpdconf['ldap'] = $_POST['ldap'];
579
		$dhcpdconf['netboot'] = ($_POST['netboot']) ? true : false;
580
		$dhcpdconf['nextserver'] = $_POST['nextserver'];
581
		$dhcpdconf['filename'] = $_POST['filename'];
582
		$dhcpdconf['filename32'] = $_POST['filename32'];
583
		$dhcpdconf['filename64'] = $_POST['filename64'];
584
		$dhcpdconf['rootpath'] = $_POST['rootpath'];
585
		unset($dhcpdconf['statsgraph']);
586
		if ($_POST['statsgraph']) {
587
			$dhcpdconf['statsgraph'] = $_POST['statsgraph'];
588
			enable_rrd_graphing();
589
		}
590

    
591
		// Handle the custom options rowhelper
592
		if (isset($dhcpdconf['numberoptions']['item'])) {
593
			unset($dhcpdconf['numberoptions']['item']);
594
		}
595

    
596
		$dhcpdconf['numberoptions'] = $numberoptions;
597

    
598
		if (is_numeric($pool) && is_array($a_pools[$pool])) {
599
			$a_pools[$pool] = $dhcpdconf;
600
		} elseif ($act == "newpool") {
601
			$a_pools[] = $dhcpdconf;
602
		} else {
603
			$config['dhcpd'][$if] = $dhcpdconf;
604
		}
605

    
606
		write_config();
607
	}
608
}
609

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

    
639
	if ($retvaldhcp == 1 || $retvaldns == 1 || $retvalfc == 1) {
640
		$retval = 1;
641
	}
642

    
643
	$savemsg = get_std_save_message($retval);
644
}
645

    
646
if ($act == "delpool") {
647
	if ($a_pools[$_GET['id']]) {
648
		unset($a_pools[$_GET['id']]);
649
		write_config();
650
		header("Location: services_dhcp.php?if={$if}");
651
		exit;
652
	}
653
}
654

    
655
if ($act == "del") {
656
	if ($a_maps[$_GET['id']]) {
657
		/* Remove static ARP entry, if necessary */
658
		if (isset($a_maps[$_GET['id']]['arp_table_static_entry'])) {
659
			mwexec("/usr/sbin/arp -d " . escapeshellarg($a_maps[$_GET['id']]['ipaddr']));
660
		}
661
		unset($a_maps[$_GET['id']]);
662
		write_config();
663
		if (isset($config['dhcpd'][$if]['enable'])) {
664
			mark_subsystem_dirty('staticmaps');
665
			if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic'])) {
666
				mark_subsystem_dirty('hosts');
667
			}
668
		}
669

    
670
		header("Location: services_dhcp.php?if={$if}");
671
		exit;
672
	}
673
}
674

    
675
// Build an HTML table that can be inserted into a Form_StaticText element
676
function build_pooltable() {
677
	global $a_pools, $if;
678

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

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

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

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

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

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

    
714
	$pooltbl .=			'</tbody>';
715
	$pooltbl .=		'</table>';
716
	$pooltbl .= '</div>';
717

    
718
	return($pooltbl);
719
}
720

    
721
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
722

    
723
if (!empty($if) && isset($iflist[$if])) {
724
	$pgtitle[] = $iflist[$if];
725
}
726
$shortcut_section = "dhcp";
727

    
728
include("head.inc");
729

    
730
if ($input_errors) {
731
	print_input_errors($input_errors);
732
}
733

    
734
if ($savemsg) {
735
	print_info_box($savemsg, 'success');
736
}
737

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

    
742
/* active tabs */
743
$tab_array = array();
744
$tabscounter = 0;
745
$i = 0;
746
$have_small_subnet = false;
747

    
748
foreach ($iflist as $ifent => $ifname) {
749
	$oc = $config['interfaces'][$ifent];
750

    
751
	/* Not static IPv4 or subnet >= 31 */
752
	if ($oc['subnet'] >= 31) {
753
		$have_small_subnet = true;
754
		$example_name = $ifname;
755
		$example_cidr = $oc['subnet'];
756
		continue;
757
	}
758
	if (!is_ipaddrv4($oc['ipaddr']) || empty($oc['subnet'])) {
759
		continue;
760
	}
761

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

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

    
772
if ($tabscounter == 0) {
773
	if ($have_small_subnet) {
774
		$sentence2 = sprintf(gettext('%1$s has a CIDR mask of %2$s, which does not contain enough addresses.'), htmlspecialchars($example_name), htmlspecialchars($example_cidr));
775
	} else {
776
		$sentence2 = gettext("This system has no interfaces configured with a static IPv4 address.");
777
	}
778
	print_info_box(gettext("The DHCP Server requires a static IPv4 subnet large enough to serve addresses to clients.") . " " . $sentence2);
779
	include("foot.inc");
780
	exit;
781
}
782

    
783
display_top_tabs($tab_array);
784

    
785
$form = new Form();
786

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

    
789
if (!is_numeric($pool) && !($act == "newpool")) {
790
	if (isset($config['dhcrelay']['enable'])) {
791
		$section->addInput(new Form_Checkbox(
792
			'enable',
793
			'Enable',
794
			gettext("DHCP Relay is currently enabled. DHCP Server canot be enabled while the DHCP Relay is enabled on any interface."),
795
			$pconfig['enable']
796
		))->setAttribute('disabled', true);
797
	} else {
798
		$section->addInput(new Form_Checkbox(
799
			'enable',
800
			'Enable',
801
			sprintf(gettext("Enable DHCP server on %s interface"), htmlspecialchars($iflist[$if])),
802
			$pconfig['enable']
803
		));
804
	}
805
} else {
806
	print_info_box(gettext('Editing pool-specific options. To return to the Interface, click its tab above.'), 'info', false);
807
}
808

    
809
$section->addInput(new Form_Checkbox(
810
	'ignorebootp',
811
	'BOOTP',
812
	'Ignore BOOTP queries',
813
	$pconfig['ignorebootp']
814
));
815

    
816
$section->addInput(new Form_Checkbox(
817
	'denyunknown',
818
	'Deny unknown clients',
819
	'Only the clients defined below will get DHCP leases from this server.',
820
	$pconfig['denyunknown']
821
));
822

    
823
$section->addInput(new Form_Checkbox(
824
	'nonak',
825
	'Ignore denied clients',
826
	'Denied clients will be ignored rather than rejected.',
827
	$pconfig['nonak']
828
))->setHelp("This option is not compatible with failover and cannot be enabled when a Failover Peer IP address is configured.");
829

    
830

    
831
if (is_numeric($pool) || ($act == "newpool")) {
832
	$section->addInput(new Form_Input(
833
		'descr',
834
		'Pool Description',
835
		'text',
836
		$pconfig['descr']
837
	));
838
}
839

    
840
$section->addInput(new Form_StaticText(
841
	'Subnet',
842
	gen_subnet($ifcfgip, $ifcfgsn)
843
));
844

    
845
$section->addInput(new Form_StaticText(
846
	'Subnet mask',
847
	gen_subnet_mask($ifcfgsn)
848
));
849

    
850
// Compose a string to display the required address ranges
851
$rangestr = ip_after($subnet_start) . ' - ' . ip_before($subnet_end);
852

    
853
if (is_numeric($pool) || ($act == "newpool")) {
854
	$rangestr .= '<br />' . gettext('In-use DHCP Pool Ranges:');
855
	if (is_array($config['dhcpd'][$if]['range'])) {
856
		$rangestr .= '<br />' . $config['dhcpd'][$if]['range']['from'] . ' - ' . $config['dhcpd'][$if]['range']['to'];
857
	}
858

    
859
	foreach ($a_pools as $p) {
860
		if (is_array($p['range'])) {
861
			$rangestr .= '<br />' . $p['range']['from'] . ' - ' . $p['range']['to'];
862
		}
863
	}
864
}
865

    
866
$section->addInput(new Form_StaticText(
867
	'Available range',
868
	$rangestr
869
));
870

    
871
if ($is_olsr_enabled) {
872
	$section->addInput(new Form_Select(
873
		'netmask',
874
		'Subnet mask',
875
		$pconfig['netmask'],
876
		array_combine(range(32, 1, -1), range(32, 1, -1))
877
	));
878
}
879

    
880
$group = new Form_Group('Range');
881

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

    
889
$group->add(new Form_IpAddress(
890
	'range_to',
891
	null,
892
	$pconfig['range_to'],
893
	'V4'
894
))->setHelp('To');
895

    
896
$section->add($group);
897

    
898
$form->add($section);
899

    
900
if (!is_numeric($pool) && !($act == "newpool")) {
901
	$section = new Form_Section('Additional Pools');
902

    
903
	$btnaddpool = new Form_Button(
904
		'btnaddpool',
905
		'Add pool',
906
		'services_dhcp.php?if=' . htmlspecialchars($if) . '&act=newpool',
907
		'fa-plus'
908
	);
909
	$btnaddpool->addClass('btn-success');
910

    
911
	$section->addInput(new Form_StaticText(
912
		'Add',
913
		$btnaddpool
914
	))->setHelp('If additional pools of addresses are needed inside of this subnet outside the above Range, they may be specified here.');
915

    
916
	if (is_array($a_pools)) {
917
		$section->addInput(new Form_StaticText(
918
			null,
919
			build_pooltable()
920
		));
921
	}
922

    
923
	$form->add($section);
924
}
925

    
926
$section = new Form_Section('Servers');
927

    
928
$section->addInput(new Form_IpAddress(
929
	'wins1',
930
	'WINS servers',
931
	$pconfig['wins1'],
932
	'V4'
933
))->setAttribute('placeholder', 'WINS Server 1');
934

    
935
$section->addInput(new Form_IpAddress(
936
	'wins2',
937
	null,
938
	$pconfig['wins2'],
939
	'V4'
940
))->setAttribute('placeholder', 'WINS Server 2');
941

    
942
for ($idx=1; $idx<=4; $idx++) {
943
	$section->addInput(new Form_IpAddress(
944
		'dns' . $idx,
945
		($idx == 1) ? 'DNS servers':null,
946
		$pconfig['dns' . $idx],
947
		'V4'
948
	))->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.':'');
949
}
950

    
951
$form->add($section);
952

    
953
$section = new Form_Section('Other Options');
954

    
955
$section->addInput(new Form_IpAddress(
956
	'gateway',
957
	'Gateway',
958
	$pconfig['gateway'],
959
	'V4'
960
))->setPattern('[.a-zA-Z0-9_]+')
961
  ->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.');
962

    
963
$section->addInput(new Form_Input(
964
	'domain',
965
	'Domain name',
966
	'text',
967
	$pconfig['domain']
968
))->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.');
969

    
970
$section->addInput(new Form_Input(
971
	'domainsearchlist',
972
	'Domain search list',
973
	'text',
974
	$pconfig['domainsearchlist']
975
))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator.');
976

    
977
$section->addInput(new Form_Input(
978
	'deftime',
979
	'Default lease time',
980
	'number',
981
	$pconfig['deftime']
982
))->setHelp('This is used for clients that do not ask for a specific expiration time. The default is 7200 seconds.');
983

    
984
$section->addInput(new Form_Input(
985
	'maxtime',
986
	'Maximum lease time',
987
	'number',
988
	$pconfig['maxtime']
989
))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds.');
990

    
991
if (!is_numeric($pool) && !($act == "newpool")) {
992
	$section->addInput(new Form_IpAddress(
993
		'failover_peerip',
994
		'Failover peer IP',
995
		$pconfig['failover_peerip'],
996
		'V4'
997
	))->setHelp('Leave blank to disable. Enter the interface IP address of the other machine. Machines must be using CARP. ' .
998
				'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).');
999
}
1000

    
1001
if (!is_numeric($pool) && !($act == "newpool")) {
1002
	$section->addInput(new Form_Checkbox(
1003
		'staticarp',
1004
		'Static ARP',
1005
		'Enable Static ARP entries',
1006
		$pconfig['staticarp']
1007
	))->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.');
1008

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

    
1024
// DDNS
1025
$btnadv = new Form_Button(
1026
	'btnadvdns',
1027
	'Display Advanced',
1028
	null,
1029
	'fa-cog'
1030
);
1031

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

    
1034
$section->addInput(new Form_StaticText(
1035
	'Dynamic DNS',
1036
	$btnadv
1037
));
1038

    
1039
$section->addInput(new Form_Checkbox(
1040
	'ddnsupdate',
1041
	null,
1042
	'Enable registration of DHCP client names in DNS',
1043
	$pconfig['ddnsupdate']
1044
));
1045

    
1046
$section->addInput(new Form_Input(
1047
	'ddnsdomain',
1048
	'DDNS Domain',
1049
	'text',
1050
	$pconfig['ddnsdomain']
1051
))->setHelp('Leave blank to disable dynamic DNS registration.' . '<br />' .
1052
			'Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
1053

    
1054
$section->addInput(new Form_Checkbox(
1055
	'ddnsforcehostname',
1056
	'DDNS Hostnames',
1057
	'Force dynamic DNS hostname to be the same as configured hostname for Static Mappings',
1058
	$pconfig['ddnsforcehostname']
1059
))->setHelp('Default registers host name option supplied by DHCP client.');
1060

    
1061
$section->addInput(new Form_IpAddress(
1062
	'ddnsdomainprimary',
1063
	'Primary DDNS address',
1064
	$pconfig['ddnsdomainprimary'],
1065
	'V4'
1066
))->setHelp('Primary domain name server IP address for the dynamic domain name.');
1067

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

    
1075
$section->addInput(new Form_Input(
1076
	'ddnsdomainkey',
1077
	'DNS Domain key secret',
1078
	'text',
1079
	$pconfig['ddnsdomainkey']
1080
))->setHelp('Dynamic DNS domain key secret (HMAC-MD5) which will be used to register client names in the DNS server.');
1081

    
1082
// Advanced MAC
1083
$btnadv = new Form_Button(
1084
	'btnadvmac',
1085
	'Display Advanced',
1086
	null,
1087
	'fa-cog'
1088
);
1089

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

    
1092
$section->addInput(new Form_StaticText(
1093
	'MAC address control',
1094
	$btnadv
1095
));
1096

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

    
1104
$section->addInput(new Form_Input(
1105
	'mac_deny',
1106
	'MAC Deny',
1107
	'text',
1108
	$pconfig['mac_deny']
1109
))->setHelp('List of partial MAC addresses to deny access, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1110

    
1111
// Advanced NTP
1112
$btnadv = new Form_Button(
1113
	'btnadvntp',
1114
	'Display Advanced',
1115
	null,
1116
	'fa-cog'
1117
);
1118

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

    
1121
$section->addInput(new Form_StaticText(
1122
	'NTP',
1123
	$btnadv
1124
));
1125

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

    
1133
$section->addInput(new Form_IpAddress(
1134
	'ntp2',
1135
	'NTP Server 2',
1136
	$pconfig['ntp2'],
1137
	'V4'
1138
))->setPattern('[.a-zA-Z0-9-]+');
1139

    
1140
// Advanced TFTP
1141
$btnadv = new Form_Button(
1142
	'btnadvtftp',
1143
	'Display Advanced',
1144
	null,
1145
	'fa-cog'
1146
);
1147

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

    
1150
$section->addInput(new Form_StaticText(
1151
	'TFTP',
1152
	$btnadv
1153
));
1154

    
1155
$section->addInput(new Form_Input(
1156
	'tftp',
1157
	'TFTP Server',
1158
	'text',
1159
	$pconfig['tftp']
1160
))->setHelp('Leave blank to disable. Enter a valid IP address, hostname or URL for the TFTP server.');
1161

    
1162
// Advanced LDAP
1163
$btnadv = new Form_Button(
1164
	'btnadvldap',
1165
	'Display Advanced',
1166
	null,
1167
	'fa-cog'
1168
);
1169

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

    
1172
$section->addInput(new Form_StaticText(
1173
	'LDAP',
1174
	$btnadv
1175
));
1176

    
1177
$section->addInput(new Form_Input(
1178
	'ldap',
1179
	'LDAP Server URI',
1180
	'text',
1181
	$pconfig['ldap']
1182
))->setHelp('Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com ');
1183

    
1184
// Advanced Network Booting options
1185
$btnadv = new Form_Button(
1186
	'btnadvnwkboot',
1187
	'Display Advanced',
1188
	null,
1189
	'fa-cog'
1190
);
1191

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

    
1194
$section->addInput(new Form_StaticText(
1195
	'Network Booting',
1196
	$btnadv
1197
));
1198

    
1199
$section->addInput(new Form_Checkbox(
1200
	'netboot',
1201
	'Enable',
1202
	'Enables network booting',
1203
	$pconfig['netboot']
1204
));
1205

    
1206
$section->addInput(new Form_IpAddress(
1207
	'nextserver',
1208
	'Next Server',
1209
	$pconfig['nextserver'],
1210
	'V4'
1211
))->setHelp('Enter the IP address of the next server');
1212

    
1213
$section->addInput(new Form_Input(
1214
	'filename',
1215
	'Default BIOS file name',
1216
	'text',
1217
	$pconfig['filename']
1218
));
1219

    
1220
$section->addInput(new Form_Input(
1221
	'filename32',
1222
	'UEFI 32 bit file name',
1223
	'text',
1224
	$pconfig['filename32']
1225
));
1226

    
1227
$section->addInput(new Form_Input(
1228
	'filename64',
1229
	'UEFI 64 bit file name',
1230
	'text',
1231
	$pconfig['filename64']
1232
))->setHelp('Both a filename and a boot server must be configured for this to work! ' .
1233
			'All three filenames and a configured boot server are necessary for UEFI to work! ');
1234

    
1235
$section->addInput(new Form_Input(
1236
	'rootpath',
1237
	'Root path',
1238
	'text',
1239
	$pconfig['rootpath']
1240
))->setHelp('string-format: iscsi:(servername):(protocol):(port):(LUN):targetname ');
1241

    
1242
// Advanced Additional options
1243
$btnadv = new Form_Button(
1244
	'btnadvopts',
1245
	'Display Advanced',
1246
	null,
1247
	'fa-cog'
1248
);
1249

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

    
1252
$section->addInput(new Form_StaticText(
1253
	'Additional BOOTP/DHCP Options',
1254
	$btnadv
1255
));
1256

    
1257
$form->add($section);
1258

    
1259
$section = new Form_Section('Additional BOOTP/DHCP Options');
1260
$section->addClass('adnlopts');
1261

    
1262
$section->addInput(new Form_StaticText(
1263
	null,
1264
	'<div class="alert alert-info"> ' . gettext('Enter the DHCP option number and the value for each item to include in the DHCP lease information.') . ' ' .
1265
	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>')
1266
));
1267

    
1268
if (!$pconfig['numberoptions']) {
1269
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1270
}
1271

    
1272
$customitemtypes = array(
1273
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1274
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1275
	'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')
1276
);
1277

    
1278
$numrows = count($item) -1;
1279
$counter = 0;
1280

    
1281
$numrows = count($pconfig['numberoptions']['item']) -1;
1282

    
1283
foreach ($pconfig['numberoptions']['item'] as $item) {
1284
	$number = $item['number'];
1285
	$itemtype = $item['type'];
1286
	$value = base64_decode($item['value']);
1287

    
1288
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1289
	$group->addClass('repeatable');
1290

    
1291
	$group->add(new Form_Input(
1292
		'number' . $counter,
1293
		null,
1294
		'text',
1295
		$number
1296
	))->setHelp($numrows == $counter ? 'Number':null);
1297

    
1298

    
1299
	$group->add(new Form_Select(
1300
		'itemtype' . $counter,
1301
		null,
1302
		$itemtype,
1303
		$customitemtypes
1304
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1305

    
1306
	$group->add(new Form_Input(
1307
		'value' . $counter,
1308
		null,
1309
		'text',
1310
		$value
1311
	))->setHelp($numrows == $counter ? 'Value':null);
1312

    
1313
	$group->add(new Form_Button(
1314
		'deleterow' . $counter,
1315
		'Delete',
1316
		null,
1317
		'fa-trash'
1318
	))->addClass('btn-warning');
1319

    
1320
	$section->add($group);
1321

    
1322
	$counter++;
1323
}
1324

    
1325
$section->addInput(new Form_Button(
1326
	'addrow',
1327
	'Add',
1328
	null,
1329
	'fa-plus'
1330
))->addClass('btn-success');
1331

    
1332
$form->add($section);
1333

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

    
1343
if (is_numeric($pool)) {
1344
	$form->addGlobal(new Form_Input(
1345
		'pool',
1346
		null,
1347
		'hidden',
1348
		$pool
1349
	));
1350
}
1351

    
1352
$form->addGlobal(new Form_Input(
1353
	'if',
1354
	null,
1355
	'hidden',
1356
	$if
1357
));
1358

    
1359
print($form);
1360

    
1361
// DHCP Static Mappings table
1362

    
1363
if (!is_numeric($pool) && !($act == "newpool")) {
1364
?>
1365

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

    
1423
<nav class="action-buttons">
1424
	<a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>" class="btn btn-sm btn-success">
1425
		<i class="fa fa-plus icon-embed-btn"></i>
1426
		<?=gettext("Add")?>
1427
	</a>
1428
</nav>
1429
<?php
1430
}
1431
?>
1432

    
1433
<script type="text/javascript">
1434
//<![CDATA[
1435
events.push(function() {
1436

    
1437
	// Show advanced DNS options ======================================================================================
1438
	var showadvdns = false;
1439

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

    
1458
		hideCheckbox('ddnsupdate', !showadvdns);
1459
		hideInput('ddnsdomain', !showadvdns);
1460
		hideCheckbox('ddnsforcehostname', !showadvdns);
1461
		hideInput('ddnsdomainprimary', !showadvdns);
1462
		hideInput('ddnsdomainkeyname', !showadvdns);
1463
		hideInput('ddnsdomainkey', !showadvdns);
1464

    
1465
		if (showadvdns) {
1466
			text = "<?=gettext('Hide Advanced');?>";
1467
		} else {
1468
			text = "<?=gettext('Display Advanced');?>";
1469
		}
1470
		$('#btnadvdns').html('<i class="fa fa-cog"></i> ' + text);
1471
	}
1472

    
1473
	$('#btnadvdns').click(function(event) {
1474
		show_advdns();
1475
	});
1476

    
1477
	// Show advanced MAC options ======================================================================================
1478
	var showadvmac = false;
1479

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

    
1497
		hideInput('mac_allow', !showadvmac);
1498
		hideInput('mac_deny', !showadvmac);
1499

    
1500
		if (showadvmac) {
1501
			text = "<?=gettext('Hide Advanced');?>";
1502
		} else {
1503
			text = "<?=gettext('Display Advanced');?>";
1504
		}
1505
		$('#btnadvmac').html('<i class="fa fa-cog"></i> ' + text);
1506
	}
1507

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

    
1512
	// Show advanced NTP options ======================================================================================
1513
	var showadvntp = false;
1514

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

    
1532
		hideInput('ntp1', !showadvntp);
1533
		hideInput('ntp2', !showadvntp);
1534

    
1535
		if (showadvntp) {
1536
			text = "<?=gettext('Hide Advanced');?>";
1537
		} else {
1538
			text = "<?=gettext('Display Advanced');?>";
1539
		}
1540
		$('#btnadvntp').html('<i class="fa fa-cog"></i> ' + text);
1541
	}
1542

    
1543
	$('#btnadvntp').click(function(event) {
1544
		show_advntp();
1545
	});
1546

    
1547
	// Show advanced TFTP options ======================================================================================
1548
	var showadvtftp = false;
1549

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

    
1567
		hideInput('tftp', !showadvtftp);
1568

    
1569
		if (showadvtftp) {
1570
			text = "<?=gettext('Hide Advanced');?>";
1571
		} else {
1572
			text = "<?=gettext('Display Advanced');?>";
1573
		}
1574
		$('#btnadvtftp').html('<i class="fa fa-cog"></i> ' + text);
1575
	}
1576

    
1577
	$('#btnadvtftp').click(function(event) {
1578
		show_advtftp();
1579
	});
1580

    
1581
	// Show advanced LDAP options ======================================================================================
1582
	var showadvldap = false;
1583

    
1584
	function show_advldap(ispageload) {
1585
		var text;
1586
		// On page load decide the initial state based on the data.
1587
		if (ispageload) {
1588
<?php
1589
			if (empty($pconfig['ldap'])) {
1590
				$showadv = false;
1591
			} else {
1592
				$showadv = true;
1593
			}
1594
?>
1595
			showadvldap = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1596
		} else {
1597
			// It was a click, swap the state.
1598
			showadvldap = !showadvldap;
1599
		}
1600

    
1601
		hideInput('ldap', !showadvldap);
1602

    
1603
		if (showadvldap) {
1604
			text = "<?=gettext('Hide Advanced');?>";
1605
		} else {
1606
			text = "<?=gettext('Display Advanced');?>";
1607
		}
1608
		$('#btnadvldap').html('<i class="fa fa-cog"></i> ' + text);
1609
	}
1610

    
1611
	$('#btnadvldap').click(function(event) {
1612
		show_advldap();
1613
	});
1614

    
1615
	// Show advanced additional opts options ===========================================================================
1616
	var showadvopts = false;
1617

    
1618
	function show_advopts(ispageload) {
1619
		var text;
1620
		// On page load decide the initial state based on the data.
1621
		if (ispageload) {
1622
<?php
1623
			if (empty($pconfig['numberoptions']) ||
1624
			    (empty($pconfig['numberoptions']['item'][0]['number']) && (empty($pconfig['numberoptions']['item'][0]['value'])))) {
1625
				$showadv = false;
1626
			} else {
1627
				$showadv = true;
1628
			}
1629
?>
1630
			showadvopts = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1631
		} else {
1632
			// It was a click, swap the state.
1633
			showadvopts = !showadvopts;
1634
		}
1635

    
1636
		hideClass('adnlopts', !showadvopts);
1637

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

    
1646
	$('#btnadvopts').click(function(event) {
1647
		show_advopts();
1648
	});
1649

    
1650
	// Show advanced Network Booting options ===========================================================================
1651
	var showadvnwkboot = false;
1652

    
1653
	function show_advnwkboot(ispageload) {
1654
		var text;
1655
		// On page load decide the initial state based on the data.
1656
		if (ispageload) {
1657
<?php
1658
			if (empty($pconfig['netboot'])) {
1659
				$showadv = false;
1660
			} else {
1661
				$showadv = true;
1662
			}
1663
?>
1664
			showadvnwkboot = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1665
		} else {
1666
			// It was a click, swap the state.
1667
			showadvnwkboot = !showadvnwkboot;
1668
		}
1669

    
1670
		hideCheckbox('netboot', !showadvnwkboot);
1671
		hideInput('nextserver', !showadvnwkboot);
1672
		hideInput('filename', !showadvnwkboot);
1673
		hideInput('filename32', !showadvnwkboot);
1674
		hideInput('filename64', !showadvnwkboot);
1675
		hideInput('rootpath', !showadvnwkboot);
1676

    
1677
		if (showadvnwkboot) {
1678
			text = "<?=gettext('Hide Advanced');?>";
1679
		} else {
1680
			text = "<?=gettext('Display Advanced');?>";
1681
		}
1682
		$('#btnadvnwkboot').html('<i class="fa fa-cog"></i> ' + text);
1683
	}
1684

    
1685
	$('#btnadvnwkboot').click(function(event) {
1686
		show_advnwkboot();
1687
	});
1688

    
1689
	// ---------- On initial page load ------------------------------------------------------------
1690

    
1691
	show_advdns(true);
1692
	show_advmac(true);
1693
	show_advntp(true);
1694
	show_advtftp(true);
1695
	show_advldap(true);
1696
	show_advopts(true);
1697
	show_advnwkboot(true);
1698

    
1699
	// Suppress "Delete row" button if there are fewer than two rows
1700
	checkLastRow();
1701
});
1702
//]]>
1703
</script>
1704

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