Project

General

Profile

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

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

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

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

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

    
226
	return true;
227
}
228

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

    
231
	unset($input_errors);
232

    
233
	$pconfig = $_POST;
234

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

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

    
249
	/* input validation */
250

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
425
	// 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.
426
	if (!$input_errors && $_POST['range_from'] && $_POST['range_to']) {
427
		/* make sure the range lies within the current subnet */
428
		if (ip_greater_than($_POST['range_from'], $_POST['range_to'])) {
429
			$input_errors[] = gettext("The range is invalid (first element higher than second element).");
430
		}
431

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

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

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

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

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

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

    
498
		$dhcpd_enable_changed = false;
499

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

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

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

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

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

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

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

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

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

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

    
598
		$dhcpdconf['numberoptions'] = $numberoptions;
599

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

    
608
		write_config();
609
	}
610
}
611

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

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

    
645
	$savemsg = get_std_save_message($retval);
646
}
647

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

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

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

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

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

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

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

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

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

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

    
716
	$pooltbl .=			'</tbody>';
717
	$pooltbl .=		'</table>';
718
	$pooltbl .= '</div>';
719

    
720
	return($pooltbl);
721
}
722

    
723
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
724

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

    
730
include("head.inc");
731

    
732
if ($input_errors) {
733
	print_input_errors($input_errors);
734
}
735

    
736
if ($savemsg) {
737
	print_info_box($savemsg, 'success');
738
}
739

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

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

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

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

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

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

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

    
785
display_top_tabs($tab_array);
786

    
787
$form = new Form();
788

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

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

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

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

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

    
832
$section->addInput(new Form_Checkbox(
833
	'ignoreclientuids',
834
	'Ignore client identifiers',
835
	'If a client includes a unique identifier in its DHCP request, that UID will not be recorded in its lease.',
836
	$pconfig['ignoreclientuids']
837
))->setHelp("This option may be useful when a client can dual boot using different client identifiers but the same hardware (MAC) address.  Note that the resulting server behavior violates the official DHCP specification.");
838

    
839

    
840
if (is_numeric($pool) || ($act == "newpool")) {
841
	$section->addInput(new Form_Input(
842
		'descr',
843
		'Pool Description',
844
		'text',
845
		$pconfig['descr']
846
	));
847
}
848

    
849
$section->addInput(new Form_StaticText(
850
	'Subnet',
851
	gen_subnet($ifcfgip, $ifcfgsn)
852
));
853

    
854
$section->addInput(new Form_StaticText(
855
	'Subnet mask',
856
	gen_subnet_mask($ifcfgsn)
857
));
858

    
859
// Compose a string to display the required address ranges
860
$rangestr = ip_after($subnet_start) . ' - ' . ip_before($subnet_end);
861

    
862
if (is_numeric($pool) || ($act == "newpool")) {
863
	$rangestr .= '<br />' . gettext('In-use DHCP Pool Ranges:');
864
	if (is_array($config['dhcpd'][$if]['range'])) {
865
		$rangestr .= '<br />' . $config['dhcpd'][$if]['range']['from'] . ' - ' . $config['dhcpd'][$if]['range']['to'];
866
	}
867

    
868
	foreach ($a_pools as $p) {
869
		if (is_array($p['range'])) {
870
			$rangestr .= '<br />' . $p['range']['from'] . ' - ' . $p['range']['to'];
871
		}
872
	}
873
}
874

    
875
$section->addInput(new Form_StaticText(
876
	'Available range',
877
	$rangestr
878
));
879

    
880
if ($is_olsr_enabled) {
881
	$section->addInput(new Form_Select(
882
		'netmask',
883
		'Subnet mask',
884
		$pconfig['netmask'],
885
		array_combine(range(32, 1, -1), range(32, 1, -1))
886
	));
887
}
888

    
889
$group = new Form_Group('Range');
890

    
891
$group->add(new Form_IpAddress(
892
	'range_from',
893
	null,
894
	$pconfig['range_from'],
895
	'V4'
896
))->setHelp('From');
897

    
898
$group->add(new Form_IpAddress(
899
	'range_to',
900
	null,
901
	$pconfig['range_to'],
902
	'V4'
903
))->setHelp('To');
904

    
905
$section->add($group);
906

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

    
909
if (!is_numeric($pool) && !($act == "newpool")) {
910
	$section = new Form_Section('Additional Pools');
911

    
912
	$btnaddpool = new Form_Button(
913
		'btnaddpool',
914
		'Add pool',
915
		'services_dhcp.php?if=' . htmlspecialchars($if) . '&act=newpool',
916
		'fa-plus'
917
	);
918
	$btnaddpool->addClass('btn-success');
919

    
920
	$section->addInput(new Form_StaticText(
921
		'Add',
922
		$btnaddpool
923
	))->setHelp('If additional pools of addresses are needed inside of this subnet outside the above Range, they may be specified here.');
924

    
925
	if (is_array($a_pools)) {
926
		$section->addInput(new Form_StaticText(
927
			null,
928
			build_pooltable()
929
		));
930
	}
931

    
932
	$form->add($section);
933
}
934

    
935
$section = new Form_Section('Servers');
936

    
937
$section->addInput(new Form_IpAddress(
938
	'wins1',
939
	'WINS servers',
940
	$pconfig['wins1'],
941
	'V4'
942
))->setAttribute('placeholder', 'WINS Server 1');
943

    
944
$section->addInput(new Form_IpAddress(
945
	'wins2',
946
	null,
947
	$pconfig['wins2'],
948
	'V4'
949
))->setAttribute('placeholder', 'WINS Server 2');
950

    
951
for ($idx=1; $idx<=4; $idx++) {
952
	$section->addInput(new Form_IpAddress(
953
		'dns' . $idx,
954
		($idx == 1) ? 'DNS servers':null,
955
		$pconfig['dns' . $idx],
956
		'V4'
957
	))->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.':'');
958
}
959

    
960
$form->add($section);
961

    
962
$section = new Form_Section('Other Options');
963

    
964
$section->addInput(new Form_IpAddress(
965
	'gateway',
966
	'Gateway',
967
	$pconfig['gateway'],
968
	'V4'
969
))->setPattern('[.a-zA-Z0-9_]+')
970
  ->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.');
971

    
972
$section->addInput(new Form_Input(
973
	'domain',
974
	'Domain name',
975
	'text',
976
	$pconfig['domain']
977
))->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.');
978

    
979
$section->addInput(new Form_Input(
980
	'domainsearchlist',
981
	'Domain search list',
982
	'text',
983
	$pconfig['domainsearchlist']
984
))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator.');
985

    
986
$section->addInput(new Form_Input(
987
	'deftime',
988
	'Default lease time',
989
	'number',
990
	$pconfig['deftime']
991
))->setHelp('This is used for clients that do not ask for a specific expiration time. The default is 7200 seconds.');
992

    
993
$section->addInput(new Form_Input(
994
	'maxtime',
995
	'Maximum lease time',
996
	'number',
997
	$pconfig['maxtime']
998
))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds.');
999

    
1000
if (!is_numeric($pool) && !($act == "newpool")) {
1001
	$section->addInput(new Form_IpAddress(
1002
		'failover_peerip',
1003
		'Failover peer IP',
1004
		$pconfig['failover_peerip'],
1005
		'V4'
1006
	))->setHelp('Leave blank to disable. Enter the interface IP address of the other machine. Machines must be using CARP. ' .
1007
				'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).');
1008
}
1009

    
1010
if (!is_numeric($pool) && !($act == "newpool")) {
1011
	$section->addInput(new Form_Checkbox(
1012
		'staticarp',
1013
		'Static ARP',
1014
		'Enable Static ARP entries',
1015
		$pconfig['staticarp']
1016
	))->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.');
1017

    
1018
	$section->addInput(new Form_Checkbox(
1019
		'dhcpleaseinlocaltime',
1020
		'Time format change',
1021
		'Change DHCP display lease time from UTC to local time',
1022
		$pconfig['dhcpleaseinlocaltime']
1023
	))->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.' .
1024
				' This will be used for all DHCP interfaces lease time.');
1025
	$section->addInput(new Form_Checkbox(
1026
		'statsgraph',
1027
		'Statistics graphs',
1028
		'Enable RRD statistics graphs',
1029
		$pconfig['statsgraph']
1030
	))->setHelp('Enable this to add DHCP leases statistics to the RRD graphs. Disabled by default.');
1031
}
1032

    
1033
// DDNS
1034
$btnadv = new Form_Button(
1035
	'btnadvdns',
1036
	'Display Advanced',
1037
	null,
1038
	'fa-cog'
1039
);
1040

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

    
1043
$section->addInput(new Form_StaticText(
1044
	'Dynamic DNS',
1045
	$btnadv
1046
));
1047

    
1048
$section->addInput(new Form_Checkbox(
1049
	'ddnsupdate',
1050
	null,
1051
	'Enable registration of DHCP client names in DNS',
1052
	$pconfig['ddnsupdate']
1053
));
1054

    
1055
$section->addInput(new Form_Input(
1056
	'ddnsdomain',
1057
	'DDNS Domain',
1058
	'text',
1059
	$pconfig['ddnsdomain']
1060
))->setHelp('Leave blank to disable dynamic DNS registration.' . '<br />' .
1061
			'Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
1062

    
1063
$section->addInput(new Form_Checkbox(
1064
	'ddnsforcehostname',
1065
	'DDNS Hostnames',
1066
	'Force dynamic DNS hostname to be the same as configured hostname for Static Mappings',
1067
	$pconfig['ddnsforcehostname']
1068
))->setHelp('Default registers host name option supplied by DHCP client.');
1069

    
1070
$section->addInput(new Form_IpAddress(
1071
	'ddnsdomainprimary',
1072
	'Primary DDNS address',
1073
	$pconfig['ddnsdomainprimary'],
1074
	'V4'
1075
))->setHelp('Primary domain name server IP address for the dynamic domain name.');
1076

    
1077
$section->addInput(new Form_Input(
1078
	'ddnsdomainkeyname',
1079
	'DNS Domain key',
1080
	'text',
1081
	$pconfig['ddnsdomainkeyname']
1082
))->setHelp('Dynamic DNS domain key name which will be used to register client names in the DNS server.');
1083

    
1084
$section->addInput(new Form_Input(
1085
	'ddnsdomainkey',
1086
	'DNS Domain key secret',
1087
	'text',
1088
	$pconfig['ddnsdomainkey']
1089
))->setHelp('Dynamic DNS domain key secret (HMAC-MD5) which will be used to register client names in the DNS server.');
1090

    
1091
// Advanced MAC
1092
$btnadv = new Form_Button(
1093
	'btnadvmac',
1094
	'Display Advanced',
1095
	null,
1096
	'fa-cog'
1097
);
1098

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

    
1101
$section->addInput(new Form_StaticText(
1102
	'MAC address control',
1103
	$btnadv
1104
));
1105

    
1106
$section->addInput(new Form_Input(
1107
	'mac_allow',
1108
	'MAC Allow',
1109
	'text',
1110
	$pconfig['mac_allow']
1111
))->setHelp('List of partial MAC addresses to allow, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1112

    
1113
$section->addInput(new Form_Input(
1114
	'mac_deny',
1115
	'MAC Deny',
1116
	'text',
1117
	$pconfig['mac_deny']
1118
))->setHelp('List of partial MAC addresses to deny access, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1119

    
1120
// Advanced NTP
1121
$btnadv = new Form_Button(
1122
	'btnadvntp',
1123
	'Display Advanced',
1124
	null,
1125
	'fa-cog'
1126
);
1127

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

    
1130
$section->addInput(new Form_StaticText(
1131
	'NTP',
1132
	$btnadv
1133
));
1134

    
1135
$section->addInput(new Form_IpAddress(
1136
	'ntp1',
1137
	'NTP Server 1',
1138
	$pconfig['ntp1'],
1139
	'HOSTV4'
1140
));
1141

    
1142
$section->addInput(new Form_IpAddress(
1143
	'ntp2',
1144
	'NTP Server 2',
1145
	$pconfig['ntp2'],
1146
	'HOSTV4'
1147
));
1148

    
1149
// Advanced TFTP
1150
$btnadv = new Form_Button(
1151
	'btnadvtftp',
1152
	'Display Advanced',
1153
	null,
1154
	'fa-cog'
1155
);
1156

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

    
1159
$section->addInput(new Form_StaticText(
1160
	'TFTP',
1161
	$btnadv
1162
));
1163

    
1164
$section->addInput(new Form_Input(
1165
	'tftp',
1166
	'TFTP Server',
1167
	'text',
1168
	$pconfig['tftp']
1169
))->setHelp('Leave blank to disable. Enter a valid IP address, hostname or URL for the TFTP server.');
1170

    
1171
// Advanced LDAP
1172
$btnadv = new Form_Button(
1173
	'btnadvldap',
1174
	'Display Advanced',
1175
	null,
1176
	'fa-cog'
1177
);
1178

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

    
1181
$section->addInput(new Form_StaticText(
1182
	'LDAP',
1183
	$btnadv
1184
));
1185

    
1186
$section->addInput(new Form_Input(
1187
	'ldap',
1188
	'LDAP Server URI',
1189
	'text',
1190
	$pconfig['ldap']
1191
))->setHelp('Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com ');
1192

    
1193
// Advanced Network Booting options
1194
$btnadv = new Form_Button(
1195
	'btnadvnwkboot',
1196
	'Display Advanced',
1197
	null,
1198
	'fa-cog'
1199
);
1200

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

    
1203
$section->addInput(new Form_StaticText(
1204
	'Network Booting',
1205
	$btnadv
1206
));
1207

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

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

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

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

    
1236
$section->addInput(new Form_Input(
1237
	'filename64',
1238
	'UEFI 64 bit file name',
1239
	'text',
1240
	$pconfig['filename64']
1241
))->setHelp('Both a filename and a boot server must be configured for this to work! ' .
1242
			'All three filenames and a configured boot server are necessary for UEFI to work! ');
1243

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

    
1251
// Advanced Additional options
1252
$btnadv = new Form_Button(
1253
	'btnadvopts',
1254
	'Display Advanced',
1255
	null,
1256
	'fa-cog'
1257
);
1258

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

    
1261
$section->addInput(new Form_StaticText(
1262
	'Additional BOOTP/DHCP Options',
1263
	$btnadv
1264
));
1265

    
1266
$form->add($section);
1267

    
1268
$section = new Form_Section('Additional BOOTP/DHCP Options');
1269
$section->addClass('adnlopts');
1270

    
1271
$section->addInput(new Form_StaticText(
1272
	null,
1273
	'<div class="alert alert-info"> ' . gettext('Enter the DHCP option number and the value for each item to include in the DHCP lease information.') . ' ' .
1274
	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>')
1275
));
1276

    
1277
if (!$pconfig['numberoptions']) {
1278
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1279
}
1280

    
1281
$customitemtypes = array(
1282
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1283
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1284
	'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')
1285
);
1286

    
1287
$numrows = count($item) -1;
1288
$counter = 0;
1289

    
1290
$numrows = count($pconfig['numberoptions']['item']) -1;
1291

    
1292
foreach ($pconfig['numberoptions']['item'] as $item) {
1293
	$number = $item['number'];
1294
	$itemtype = $item['type'];
1295
	$value = base64_decode($item['value']);
1296

    
1297
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1298
	$group->addClass('repeatable');
1299

    
1300
	$group->add(new Form_Input(
1301
		'number' . $counter,
1302
		null,
1303
		'text',
1304
		$number
1305
	))->setHelp($numrows == $counter ? 'Number':null);
1306

    
1307

    
1308
	$group->add(new Form_Select(
1309
		'itemtype' . $counter,
1310
		null,
1311
		$itemtype,
1312
		$customitemtypes
1313
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1314

    
1315
	$group->add(new Form_Input(
1316
		'value' . $counter,
1317
		null,
1318
		'text',
1319
		$value
1320
	))->setHelp($numrows == $counter ? 'Value':null);
1321

    
1322
	$group->add(new Form_Button(
1323
		'deleterow' . $counter,
1324
		'Delete',
1325
		null,
1326
		'fa-trash'
1327
	))->addClass('btn-warning');
1328

    
1329
	$section->add($group);
1330

    
1331
	$counter++;
1332
}
1333

    
1334
$section->addInput(new Form_Button(
1335
	'addrow',
1336
	'Add',
1337
	null,
1338
	'fa-plus'
1339
))->addClass('btn-success');
1340

    
1341
$form->add($section);
1342

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

    
1352
if (is_numeric($pool)) {
1353
	$form->addGlobal(new Form_Input(
1354
		'pool',
1355
		null,
1356
		'hidden',
1357
		$pool
1358
	));
1359
}
1360

    
1361
$form->addGlobal(new Form_Input(
1362
	'if',
1363
	null,
1364
	'hidden',
1365
	$if
1366
));
1367

    
1368
print($form);
1369

    
1370
// DHCP Static Mappings table
1371

    
1372
if (!is_numeric($pool) && !($act == "newpool")) {
1373
?>
1374

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

    
1432
<nav class="action-buttons">
1433
	<a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>" class="btn btn-sm btn-success">
1434
		<i class="fa fa-plus icon-embed-btn"></i>
1435
		<?=gettext("Add")?>
1436
	</a>
1437
</nav>
1438
<?php
1439
}
1440
?>
1441

    
1442
<script type="text/javascript">
1443
//<![CDATA[
1444
events.push(function() {
1445

    
1446
	// Show advanced DNS options ======================================================================================
1447
	var showadvdns = false;
1448

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

    
1467
		hideCheckbox('ddnsupdate', !showadvdns);
1468
		hideInput('ddnsdomain', !showadvdns);
1469
		hideCheckbox('ddnsforcehostname', !showadvdns);
1470
		hideInput('ddnsdomainprimary', !showadvdns);
1471
		hideInput('ddnsdomainkeyname', !showadvdns);
1472
		hideInput('ddnsdomainkey', !showadvdns);
1473

    
1474
		if (showadvdns) {
1475
			text = "<?=gettext('Hide Advanced');?>";
1476
		} else {
1477
			text = "<?=gettext('Display Advanced');?>";
1478
		}
1479
		$('#btnadvdns').html('<i class="fa fa-cog"></i> ' + text);
1480
	}
1481

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

    
1486
	// Show advanced MAC options ======================================================================================
1487
	var showadvmac = false;
1488

    
1489
	function show_advmac(ispageload) {
1490
		var text;
1491
		// On page load decide the initial state based on the data.
1492
		if (ispageload) {
1493
<?php
1494
			if (empty($pconfig['mac_allow']) && empty($pconfig['mac_deny'])) {
1495
				$showadv = false;
1496
			} else {
1497
				$showadv = true;
1498
			}
1499
?>
1500
			showadvmac = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1501
		} else {
1502
			// It was a click, swap the state.
1503
			showadvmac = !showadvmac;
1504
		}
1505

    
1506
		hideInput('mac_allow', !showadvmac);
1507
		hideInput('mac_deny', !showadvmac);
1508

    
1509
		if (showadvmac) {
1510
			text = "<?=gettext('Hide Advanced');?>";
1511
		} else {
1512
			text = "<?=gettext('Display Advanced');?>";
1513
		}
1514
		$('#btnadvmac').html('<i class="fa fa-cog"></i> ' + text);
1515
	}
1516

    
1517
	$('#btnadvmac').click(function(event) {
1518
		show_advmac();
1519
	});
1520

    
1521
	// Show advanced NTP options ======================================================================================
1522
	var showadvntp = false;
1523

    
1524
	function show_advntp(ispageload) {
1525
		var text;
1526
		// On page load decide the initial state based on the data.
1527
		if (ispageload) {
1528
<?php
1529
			if (empty($pconfig['ntp1']) && empty($pconfig['ntp2'])) {
1530
				$showadv = false;
1531
			} else {
1532
				$showadv = true;
1533
			}
1534
?>
1535
			showadvntp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1536
		} else {
1537
			// It was a click, swap the state.
1538
			showadvntp = !showadvntp;
1539
		}
1540

    
1541
		hideInput('ntp1', !showadvntp);
1542
		hideInput('ntp2', !showadvntp);
1543

    
1544
		if (showadvntp) {
1545
			text = "<?=gettext('Hide Advanced');?>";
1546
		} else {
1547
			text = "<?=gettext('Display Advanced');?>";
1548
		}
1549
		$('#btnadvntp').html('<i class="fa fa-cog"></i> ' + text);
1550
	}
1551

    
1552
	$('#btnadvntp').click(function(event) {
1553
		show_advntp();
1554
	});
1555

    
1556
	// Show advanced TFTP options ======================================================================================
1557
	var showadvtftp = false;
1558

    
1559
	function show_advtftp(ispageload) {
1560
		var text;
1561
		// On page load decide the initial state based on the data.
1562
		if (ispageload) {
1563
<?php
1564
			if (empty($pconfig['tftp'])) {
1565
				$showadv = false;
1566
			} else {
1567
				$showadv = true;
1568
			}
1569
?>
1570
			showadvtftp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1571
		} else {
1572
			// It was a click, swap the state.
1573
			showadvtftp = !showadvtftp;
1574
		}
1575

    
1576
		hideInput('tftp', !showadvtftp);
1577

    
1578
		if (showadvtftp) {
1579
			text = "<?=gettext('Hide Advanced');?>";
1580
		} else {
1581
			text = "<?=gettext('Display Advanced');?>";
1582
		}
1583
		$('#btnadvtftp').html('<i class="fa fa-cog"></i> ' + text);
1584
	}
1585

    
1586
	$('#btnadvtftp').click(function(event) {
1587
		show_advtftp();
1588
	});
1589

    
1590
	// Show advanced LDAP options ======================================================================================
1591
	var showadvldap = false;
1592

    
1593
	function show_advldap(ispageload) {
1594
		var text;
1595
		// On page load decide the initial state based on the data.
1596
		if (ispageload) {
1597
<?php
1598
			if (empty($pconfig['ldap'])) {
1599
				$showadv = false;
1600
			} else {
1601
				$showadv = true;
1602
			}
1603
?>
1604
			showadvldap = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1605
		} else {
1606
			// It was a click, swap the state.
1607
			showadvldap = !showadvldap;
1608
		}
1609

    
1610
		hideInput('ldap', !showadvldap);
1611

    
1612
		if (showadvldap) {
1613
			text = "<?=gettext('Hide Advanced');?>";
1614
		} else {
1615
			text = "<?=gettext('Display Advanced');?>";
1616
		}
1617
		$('#btnadvldap').html('<i class="fa fa-cog"></i> ' + text);
1618
	}
1619

    
1620
	$('#btnadvldap').click(function(event) {
1621
		show_advldap();
1622
	});
1623

    
1624
	// Show advanced additional opts options ===========================================================================
1625
	var showadvopts = false;
1626

    
1627
	function show_advopts(ispageload) {
1628
		var text;
1629
		// On page load decide the initial state based on the data.
1630
		if (ispageload) {
1631
<?php
1632
			if (empty($pconfig['numberoptions']) ||
1633
			    (empty($pconfig['numberoptions']['item'][0]['number']) && (empty($pconfig['numberoptions']['item'][0]['value'])))) {
1634
				$showadv = false;
1635
			} else {
1636
				$showadv = true;
1637
			}
1638
?>
1639
			showadvopts = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1640
		} else {
1641
			// It was a click, swap the state.
1642
			showadvopts = !showadvopts;
1643
		}
1644

    
1645
		hideClass('adnlopts', !showadvopts);
1646

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

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

    
1659
	// Show advanced Network Booting options ===========================================================================
1660
	var showadvnwkboot = false;
1661

    
1662
	function show_advnwkboot(ispageload) {
1663
		var text;
1664
		// On page load decide the initial state based on the data.
1665
		if (ispageload) {
1666
<?php
1667
			if (empty($pconfig['netboot'])) {
1668
				$showadv = false;
1669
			} else {
1670
				$showadv = true;
1671
			}
1672
?>
1673
			showadvnwkboot = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1674
		} else {
1675
			// It was a click, swap the state.
1676
			showadvnwkboot = !showadvnwkboot;
1677
		}
1678

    
1679
		hideCheckbox('netboot', !showadvnwkboot);
1680
		hideInput('nextserver', !showadvnwkboot);
1681
		hideInput('filename', !showadvnwkboot);
1682
		hideInput('filename32', !showadvnwkboot);
1683
		hideInput('filename64', !showadvnwkboot);
1684
		hideInput('rootpath', !showadvnwkboot);
1685

    
1686
		if (showadvnwkboot) {
1687
			text = "<?=gettext('Hide Advanced');?>";
1688
		} else {
1689
			text = "<?=gettext('Display Advanced');?>";
1690
		}
1691
		$('#btnadvnwkboot').html('<i class="fa fa-cog"></i> ' + text);
1692
	}
1693

    
1694
	$('#btnadvnwkboot').click(function(event) {
1695
		show_advnwkboot();
1696
	});
1697

    
1698
	// ---------- On initial page load ------------------------------------------------------------
1699

    
1700
	show_advdns(true);
1701
	show_advmac(true);
1702
	show_advntp(true);
1703
	show_advtftp(true);
1704
	show_advldap(true);
1705
	show_advopts(true);
1706
	show_advnwkboot(true);
1707

    
1708
	// Suppress "Delete row" button if there are fewer than two rows
1709
	checkLastRow();
1710
});
1711
//]]>
1712
</script>
1713

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