Project

General

Profile

Download (51.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]) && isset($config['dhcpd'][$ifent]['enable']) && (is_ipaddrv4($oc['ipaddr']))) {
67
			$if = $ifent;
68
			$found_starting_if = true;
69
			break;
70
		}
71
	}
72

    
73
	// If there is no DHCP-enabled interface and LAN is a candidate, then choose LAN.
74
	if (!$found_starting_if && isset($iflist['lan']) && is_ipaddrv4($config['interfaces']['lan']['ipaddr'])) {
75
		$if = 'lan';
76
		$found_starting_if = true;
77
	}
78

    
79
	// At the last select whatever can be found.
80
	if (!$found_starting_if) {
81
		foreach ($iflist as $ifent => $ifname) {
82
			$oc = $config['interfaces'][$ifent];
83
			if ((is_array($config['dhcpd'][$ifent]) && !isset($config['dhcpd'][$ifent]['enable']) && (!is_ipaddrv4($oc['ipaddr']))) ||
84
				(!is_array($config['dhcpd'][$ifent]) && (!is_ipaddrv4($oc['ipaddr'])))) {
85
				continue;
86
			}
87

    
88
			$if = $ifent;
89
			break;
90
		}
91
	}
92
}
93

    
94
$act = $_GET['act'];
95
if (!empty($_POST['act'])) {
96
	$act = $_POST['act'];
97
}
98

    
99
$a_pools = array();
100

    
101
if (is_array($config['dhcpd'][$if])) {
102
	$pool = $_GET['pool'];
103
	if (is_numeric($_POST['pool'])) {
104
		$pool = $_POST['pool'];
105
	}
106

    
107
	// If we have a pool but no interface name, that's not valid. Redirect away.
108
	if (is_numeric($pool) && empty($if)) {
109
		header("Location: services_dhcp.php");
110
		exit;
111
	}
112

    
113
	if (!is_array($config['dhcpd'][$if]['pool'])) {
114
		$config['dhcpd'][$if]['pool'] = array();
115
	}
116

    
117
	$a_pools = &$config['dhcpd'][$if]['pool'];
118

    
119
	if (is_numeric($pool) && $a_pools[$pool]) {
120
		$dhcpdconf = &$a_pools[$pool];
121
	} elseif ($act == "newpool") {
122
		$dhcpdconf = array();
123
	} else {
124
		$dhcpdconf = &$config['dhcpd'][$if];
125
	}
126

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

    
131
	$a_maps = &$config['dhcpd'][$if]['staticmap'];
132
}
133
if (is_array($dhcpdconf)) {
134
	// Global Options
135
	if (!is_numeric($pool) && !($act == "newpool")) {
136
		$pconfig['enable'] = isset($dhcpdconf['enable']);
137
		$pconfig['staticarp'] = isset($dhcpdconf['staticarp']);
138
		// No reason to specify this per-pool, per the dhcpd.conf man page it needs to be in every
139
		//	 pool and should be specified in every pool both nodes share, so we'll treat it as global
140
		$pconfig['failover_peerip'] = $dhcpdconf['failover_peerip'];
141

    
142
		// dhcpleaseinlocaltime is global to all interfaces. So if it is selected on any interface,
143
		// then show it true/checked.
144
		foreach ($config['dhcpd'] as $dhcpdifitem) {
145
			$dhcpleaseinlocaltime = $dhcpdifitem['dhcpleaseinlocaltime'];
146
			if ($dhcpleaseinlocaltime) {
147
				break;
148
			}
149
		}
150

    
151
		$pconfig['dhcpleaseinlocaltime'] = $dhcpleaseinlocaltime;
152
	} else {
153
		// Options that exist only in pools
154
		$pconfig['descr'] = $dhcpdconf['descr'];
155
	}
156

    
157
	// Options that can be global or per-pool.
158
	if (is_array($dhcpdconf['range'])) {
159
		$pconfig['range_from'] = $dhcpdconf['range']['from'];
160
		$pconfig['range_to'] = $dhcpdconf['range']['to'];
161
	}
162

    
163
	$pconfig['deftime'] = $dhcpdconf['defaultleasetime'];
164
	$pconfig['maxtime'] = $dhcpdconf['maxleasetime'];
165
	$pconfig['gateway'] = $dhcpdconf['gateway'];
166
	$pconfig['domain'] = $dhcpdconf['domain'];
167
	$pconfig['domainsearchlist'] = $dhcpdconf['domainsearchlist'];
168
	list($pconfig['wins1'], $pconfig['wins2']) = $dhcpdconf['winsserver'];
169
	list($pconfig['dns1'], $pconfig['dns2'], $pconfig['dns3'], $pconfig['dns4']) = $dhcpdconf['dnsserver'];
170
	$pconfig['ignorebootp'] = isset($dhcpdconf['ignorebootp']);
171
	$pconfig['denyunknown'] = isset($dhcpdconf['denyunknown']);
172
	$pconfig['nonak'] = isset($dhcpdconf['nonak']);
173
	$pconfig['ddnsdomain'] = $dhcpdconf['ddnsdomain'];
174
	$pconfig['ddnsdomainprimary'] = $dhcpdconf['ddnsdomainprimary'];
175
	$pconfig['ddnsdomainkeyname'] = $dhcpdconf['ddnsdomainkeyname'];
176
	$pconfig['ddnsdomainkey'] = $dhcpdconf['ddnsdomainkey'];
177
	$pconfig['ddnsupdate'] = isset($dhcpdconf['ddnsupdate']);
178
	$pconfig['ddnsforcehostname'] = isset($dhcpdconf['ddnsforcehostname']);
179
	$pconfig['mac_allow'] = $dhcpdconf['mac_allow'];
180
	$pconfig['mac_deny'] = $dhcpdconf['mac_deny'];
181
	list($pconfig['ntp1'], $pconfig['ntp2']) = $dhcpdconf['ntpserver'];
182
	$pconfig['tftp'] = $dhcpdconf['tftp'];
183
	$pconfig['ldap'] = $dhcpdconf['ldap'];
184
	$pconfig['netboot'] = isset($dhcpdconf['netboot']);
185
	$pconfig['nextserver'] = $dhcpdconf['nextserver'];
186
	$pconfig['filename'] = $dhcpdconf['filename'];
187
	$pconfig['filename32'] = $dhcpdconf['filename32'];
188
	$pconfig['filename64'] = $dhcpdconf['filename64'];
189
	$pconfig['rootpath'] = $dhcpdconf['rootpath'];
190
	$pconfig['netmask'] = $dhcpdconf['netmask'];
191
	$pconfig['numberoptions'] = $dhcpdconf['numberoptions'];
192
	$pconfig['statsgraph'] = $dhcpdconf['statsgraph'];
193
}
194

    
195
$ifcfgip = $config['interfaces'][$if]['ipaddr'];
196
$ifcfgsn = $config['interfaces'][$if]['subnet'];
197

    
198
$subnet_start = gen_subnetv4($ifcfgip, $ifcfgsn);
199
$subnet_end = gen_subnetv4_max($ifcfgip, $ifcfgsn);
200

    
201
function validate_partial_mac_list($maclist) {
202
	$macs = explode(',', $maclist);
203

    
204
	// Loop through and look for invalid MACs.
205
	foreach ($macs as $mac) {
206
		if (!is_macaddr($mac, true)) {
207
			return false;
208
		}
209
	}
210

    
211
	return true;
212
}
213

    
214
if (isset($_POST['save'])) {
215

    
216
	unset($input_errors);
217

    
218
	$pconfig = $_POST;
219

    
220
	$numberoptions = array();
221
	for ($x = 0; $x < 99; $x++) {
222
		if (isset($_POST["number{$x}"]) && ctype_digit($_POST["number{$x}"])) {
223
			$numbervalue = array();
224
			$numbervalue['number'] = htmlspecialchars($_POST["number{$x}"]);
225
			$numbervalue['type'] = htmlspecialchars($_POST["itemtype{$x}"]);
226
			$numbervalue['value'] = base64_encode($_POST["value{$x}"]);
227
			$numberoptions['item'][] = $numbervalue;
228
		}
229
	}
230

    
231
	// Reload the new pconfig variable that the form uses.
232
	$pconfig['numberoptions'] = $numberoptions;
233

    
234
	/* input validation */
235

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

    
241
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
242
	}
243

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

    
248
	if ($_POST['range_from'] && !is_ipaddrv4($_POST['range_from'])) {
249
		$input_errors[] = gettext("A valid IPv4 address must be specified for range from.");
250
	}
251
	if ($_POST['range_to'] && !is_ipaddrv4($_POST['range_to'])) {
252
		$input_errors[] = gettext("A valid IPv4 address must be specified for range to.");
253
	}
254
	if (($_POST['range_from'] && !$_POST['range_to']) || ($_POST['range_to'] && !$_POST['range_from'])) {
255
		$input_errors[] = gettext("Range From and Range To must both be entered.");
256
	}
257
	if (($_POST['gateway'] && $_POST['gateway'] != "none" && !is_ipaddrv4($_POST['gateway']))) {
258
		$input_errors[] = gettext("A valid IP address must be specified for the gateway.");
259
	}
260
	if (($_POST['wins1'] && !is_ipaddrv4($_POST['wins1'])) || ($_POST['wins2'] && !is_ipaddrv4($_POST['wins2']))) {
261
		$input_errors[] = gettext("A valid IP address must be specified for the primary/secondary WINS servers.");
262
	}
263
	$parent_ip = get_interface_ip($_POST['if']);
264
	if (is_ipaddrv4($parent_ip) && $_POST['gateway'] && $_POST['gateway'] != "none") {
265
		$parent_sn = get_interface_subnet($_POST['if']);
266
		if (!ip_in_subnet($_POST['gateway'], gen_subnet($parent_ip, $parent_sn) . "/" . $parent_sn) && !ip_in_interface_alias_subnet($_POST['if'], $_POST['gateway'])) {
267
			$input_errors[] = sprintf(gettext("The gateway address %s does not lie within the chosen interface's subnet."), $_POST['gateway']);
268
		}
269
	}
270

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

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

    
279
	if (isset($config['captiveportal']) && is_array($config['captiveportal'])) {
280
		$deftime = 7200; // Default value if it's empty
281
		if (is_numeric($_POST['deftime'])) {
282
			$deftime = $_POST['deftime'];
283
		}
284

    
285
		foreach ($config['captiveportal'] as $cpZone => $cpdata) {
286
			if (!isset($cpdata['enable'])) {
287
				continue;
288
			}
289
			if (!isset($cpdata['timeout']) || !is_numeric($cpdata['timeout'])) {
290
				continue;
291
			}
292
			$cp_ifs = explode(',', $cpdata['interface']);
293
			if (!in_array($if, $cp_ifs)) {
294
				continue;
295
			}
296
			if ($cpdata['timeout'] > $deftime) {
297
				$input_errors[] = sprintf(gettext(
298
					'The Captive Portal zone (%1$s) has Hard Timeout parameter set to a value bigger than Default lease time (%2$s).'), $cpZone, $deftime);
299
			}
300
		}
301
	}
302

    
303
	if ($_POST['maxtime'] && (!is_numeric($_POST['maxtime']) || ($_POST['maxtime'] < 60) || ($_POST['maxtime'] <= $_POST['deftime']))) {
304
		$input_errors[] = gettext("The maximum lease time must be at least 60 seconds and higher than the default lease time.");
305
	}
306
	if (($_POST['ddnsdomain'] && !is_domain($_POST['ddnsdomain']))) {
307
		$input_errors[] = gettext("A valid domain name must be specified for the dynamic DNS registration.");
308
	}
309
	if (($_POST['ddnsdomain'] && !is_ipaddrv4($_POST['ddnsdomainprimary']))) {
310
		$input_errors[] = gettext("A valid primary domain name server IP address must be specified for the dynamic domain name.");
311
	}
312
	if (($_POST['ddnsdomainkey'] && !$_POST['ddnsdomainkeyname']) ||
313
		($_POST['ddnsdomainkeyname'] && !$_POST['ddnsdomainkey'])) {
314
		$input_errors[] = gettext("Both a valid domain key and key name must be specified.");
315
	}
316
	if ($_POST['domainsearchlist']) {
317
		$domain_array = preg_split("/[ ;]+/", $_POST['domainsearchlist']);
318
		foreach ($domain_array as $curdomain) {
319
			if (!is_domain($curdomain)) {
320
				$input_errors[] = gettext("A valid domain search list must be specified.");
321
				break;
322
			}
323
		}
324
	}
325

    
326
	// Validate MACs
327
	if (!empty($_POST['mac_allow']) && !validate_partial_mac_list($_POST['mac_allow'])) {
328
		$input_errors[] = gettext("If a mac allow list is specified, it must contain only valid partial MAC addresses.");
329
	}
330
	if (!empty($_POST['mac_deny']) && !validate_partial_mac_list($_POST['mac_deny'])) {
331
		$input_errors[] = gettext("If a mac deny list is specified, it must contain only valid partial MAC addresses.");
332
	}
333

    
334
	if (($_POST['ntp1'] && (!is_ipaddrv4($_POST['ntp1']) && !is_hostname($_POST['ntp1']))) || ($_POST['ntp2'] && (!is_ipaddrv4($_POST['ntp2']) && !is_hostname($_POST['ntp2'])))) {
335
		$input_errors[] = gettext("A valid IP address or hostname must be specified for the primary/secondary NTP servers.");
336
	}
337
	if (($_POST['domain'] && !is_domain($_POST['domain']))) {
338
		$input_errors[] = gettext("A valid domain name must be specified for the DNS domain.");
339
	}
340
	if ($_POST['tftp'] && !is_ipaddrv4($_POST['tftp']) && !is_domain($_POST['tftp']) && !filter_var($_POST['tftp'], FILTER_VALIDATE_URL)) {
341
		$input_errors[] = gettext("A valid IP address, hostname or URL must be specified for the TFTP server.");
342
	}
343
	if (($_POST['nextserver'] && !is_ipaddrv4($_POST['nextserver']))) {
344
		$input_errors[] = gettext("A valid IP address must be specified for the network boot server.");
345
	}
346

    
347
	if (gen_subnet($ifcfgip, $ifcfgsn) == $_POST['range_from']) {
348
		$input_errors[] = gettext("The network address cannot be used in the starting subnet range.");
349
	}
350
	if (gen_subnet_max($ifcfgip, $ifcfgsn) == $_POST['range_to']) {
351
		$input_errors[] = gettext("The broadcast address cannot be used in the ending subnet range.");
352
	}
353

    
354
	// Disallow a range that includes the virtualip
355
	if (is_array($config['virtualip']['vip'])) {
356
		foreach ($config['virtualip']['vip'] as $vip) {
357
			if ($vip['interface'] == $if) {
358
				if ($vip['subnet'] && is_inrange_v4($vip['subnet'], $_POST['range_from'], $_POST['range_to'])) {
359
					$input_errors[] = sprintf(gettext("The subnet range cannot overlap with virtual IP address %s."), $vip['subnet']);
360
				}
361
			}
362
		}
363
	}
364

    
365
	$noip = false;
366
	if (is_array($a_maps)) {
367
		foreach ($a_maps as $map) {
368
			if (empty($map['ipaddr'])) {
369
				$noip = true;
370
			}
371
		}
372
	}
373

    
374
	if ($_POST['staticarp'] && $noip) {
375
		$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.");
376
	}
377

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

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

    
410
	// 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.
411
	if (!$input_errors && $_POST['range_from'] && $_POST['range_to']) {
412
		/* make sure the range lies within the current subnet */
413
		if (ip_greater_than($_POST['range_from'], $_POST['range_to'])) {
414
			$input_errors[] = gettext("The range is invalid (first element higher than second element).");
415
		}
416

    
417
		if (!is_inrange_v4($_POST['range_from'], $subnet_start, $subnet_end) ||
418
			!is_inrange_v4($_POST['range_to'], $subnet_start, $subnet_end)) {
419
			$input_errors[] = gettext("The specified range lies outside of the current subnet.");
420
		}
421

    
422
		if (is_numeric($pool) || ($act == "newpool")) {
423
			if (is_inrange_v4($_POST['range_from'],
424
				$config['dhcpd'][$if]['range']['from'],
425
				$config['dhcpd'][$if]['range']['to']) ||
426
				is_inrange_v4($_POST['range_to'],
427
				$config['dhcpd'][$if]['range']['from'],
428
				$config['dhcpd'][$if]['range']['to'])) {
429
				$input_errors[] = gettext("The specified range must not be within the DHCP range for this interface.");
430
			}
431
		}
432

    
433
		foreach ($a_pools as $id => $p) {
434
			if (is_numeric($pool) && ($id == $pool)) {
435
				continue;
436
			}
437

    
438
			if (is_inrange_v4($_POST['range_from'],
439
				$p['range']['from'], $p['range']['to']) ||
440
				is_inrange_v4($_POST['range_to'],
441
				$p['range']['from'], $p['range']['to'])) {
442
				$input_errors[] = gettext("The specified range must not be within the range configured on a DHCP pool for this interface.");
443
				break;
444
			}
445
		}
446

    
447
		if (is_array($a_maps)) {
448
			foreach ($a_maps as $map) {
449
				if (empty($map['ipaddr'])) {
450
					continue;
451
				}
452
				if (is_inrange_v4($map['ipaddr'], $_POST['range_from'], $_POST['range_to'])) {
453
					$input_errors[] = sprintf(gettext("The DHCP range cannot overlap any static DHCP mappings."));
454
					break;
455
				}
456
			}
457
		}
458
	}
459

    
460
	if (!$input_errors) {
461
		if (!is_numeric($pool)) {
462
			if ($act == "newpool") {
463
				$dhcpdconf = array();
464
			} else {
465
				if (!is_array($config['dhcpd'][$if])) {
466
					$config['dhcpd'][$if] = array();
467
				}
468
				$dhcpdconf = $config['dhcpd'][$if];
469
			}
470
		} else {
471
			if (is_array($a_pools[$pool])) {
472
				$dhcpdconf = $a_pools[$pool];
473
			} else {
474
				// Someone specified a pool but it doesn't exist. Punt.
475
				header("Location: services_dhcp.php");
476
				exit;
477
			}
478
		}
479
		if (!is_array($dhcpdconf['range'])) {
480
			$dhcpdconf['range'] = array();
481
		}
482

    
483
		$dhcpd_enable_changed = false;
484

    
485
		// Global Options
486
		if (!is_numeric($pool) && !($act == "newpool")) {
487
			$old_dhcpd_enable = isset($dhcpdconf['enable']);
488
			$new_dhcpd_enable = ($_POST['enable']) ? true : false;
489
			if ($old_dhcpd_enable != $new_dhcpd_enable) {
490
				/* DHCP has been enabled or disabled. The pf ruleset will need to be rebuilt to allow or disallow DHCP. */
491
				$dhcpd_enable_changed = true;
492
			}
493

    
494
			$dhcpdconf['enable'] = $new_dhcpd_enable;
495
			$dhcpdconf['staticarp'] = ($_POST['staticarp']) ? true : false;
496
			$previous = $dhcpdconf['failover_peerip'];
497
			if ($previous != $_POST['failover_peerip']) {
498
				mwexec("/bin/rm -rf /var/dhcpd/var/db/*");
499
			}
500

    
501
			$dhcpdconf['failover_peerip'] = $_POST['failover_peerip'];
502
			// dhcpleaseinlocaltime is global to all interfaces. So update the setting on all interfaces.
503
			foreach ($config['dhcpd'] as &$dhcpdifitem) {
504
				$dhcpdifitem['dhcpleaseinlocaltime'] = $_POST['dhcpleaseinlocaltime'];
505
			}
506
		} else {
507
			// Options that exist only in pools
508
			$dhcpdconf['descr'] = $_POST['descr'];
509
		}
510

    
511
		// Options that can be global or per-pool.
512
		$dhcpdconf['range']['from'] = $_POST['range_from'];
513
		$dhcpdconf['range']['to'] = $_POST['range_to'];
514
		$dhcpdconf['defaultleasetime'] = $_POST['deftime'];
515
		$dhcpdconf['maxleasetime'] = $_POST['maxtime'];
516
		$dhcpdconf['netmask'] = $_POST['netmask'];
517

    
518
		unset($dhcpdconf['winsserver']);
519
		if ($_POST['wins1']) {
520
			$dhcpdconf['winsserver'][] = $_POST['wins1'];
521
		}
522
		if ($_POST['wins2']) {
523
			$dhcpdconf['winsserver'][] = $_POST['wins2'];
524
		}
525

    
526
		unset($dhcpdconf['dnsserver']);
527
		if ($_POST['dns1']) {
528
			$dhcpdconf['dnsserver'][] = $_POST['dns1'];
529
		}
530
		if ($_POST['dns2']) {
531
			$dhcpdconf['dnsserver'][] = $_POST['dns2'];
532
		}
533
		if ($_POST['dns3']) {
534
			$dhcpdconf['dnsserver'][] = $_POST['dns3'];
535
		}
536
		if ($_POST['dns4']) {
537
			$dhcpdconf['dnsserver'][] = $_POST['dns4'];
538
		}
539

    
540
		$dhcpdconf['gateway'] = $_POST['gateway'];
541
		$dhcpdconf['domain'] = $_POST['domain'];
542
		$dhcpdconf['domainsearchlist'] = $_POST['domainsearchlist'];
543
		$dhcpdconf['ignorebootp'] = ($_POST['ignorebootp']) ? true : false;
544
		$dhcpdconf['denyunknown'] = ($_POST['denyunknown']) ? true : false;
545
		$dhcpdconf['nonak'] = ($_POST['nonak']) ? true : false;
546
		$dhcpdconf['ddnsdomain'] = $_POST['ddnsdomain'];
547
		$dhcpdconf['ddnsdomainprimary'] = $_POST['ddnsdomainprimary'];
548
		$dhcpdconf['ddnsdomainkeyname'] = $_POST['ddnsdomainkeyname'];
549
		$dhcpdconf['ddnsdomainkey'] = $_POST['ddnsdomainkey'];
550
		$dhcpdconf['ddnsupdate'] = ($_POST['ddnsupdate']) ? true : false;
551
		$dhcpdconf['ddnsforcehostname'] = ($_POST['ddnsforcehostname']) ? true : false;
552
		$dhcpdconf['mac_allow'] = $_POST['mac_allow'];
553
		$dhcpdconf['mac_deny'] = $_POST['mac_deny'];
554

    
555
		unset($dhcpdconf['ntpserver']);
556
		if ($_POST['ntp1']) {
557
			$dhcpdconf['ntpserver'][] = $_POST['ntp1'];
558
		}
559
		if ($_POST['ntp2']) {
560
			$dhcpdconf['ntpserver'][] = $_POST['ntp2'];
561
		}
562

    
563
		$dhcpdconf['tftp'] = $_POST['tftp'];
564
		$dhcpdconf['ldap'] = $_POST['ldap'];
565
		$dhcpdconf['netboot'] = ($_POST['netboot']) ? true : false;
566
		$dhcpdconf['nextserver'] = $_POST['nextserver'];
567
		$dhcpdconf['filename'] = $_POST['filename'];
568
		$dhcpdconf['filename32'] = $_POST['filename32'];
569
		$dhcpdconf['filename64'] = $_POST['filename64'];
570
		$dhcpdconf['rootpath'] = $_POST['rootpath'];
571
		unset($dhcpdconf['statsgraph']);
572
		if ($_POST['statsgraph']) {
573
			$dhcpdconf['statsgraph'] = $_POST['statsgraph'];
574
			enable_rrd_graphing();
575
		}
576

    
577
		// Handle the custom options rowhelper
578
		if (isset($dhcpdconf['numberoptions']['item'])) {
579
			unset($dhcpdconf['numberoptions']['item']);
580
		}
581

    
582
		$dhcpdconf['numberoptions'] = $numberoptions;
583

    
584
		if (is_numeric($pool) && is_array($a_pools[$pool])) {
585
			$a_pools[$pool] = $dhcpdconf;
586
		} elseif ($act == "newpool") {
587
			$a_pools[] = $dhcpdconf;
588
		} else {
589
			$config['dhcpd'][$if] = $dhcpdconf;
590
		}
591

    
592
		write_config();
593
	}
594
}
595

    
596
if ((isset($_POST['save']) || isset($_POST['apply'])) && (!$input_errors)) {
597
	$retval = 0;
598
	$retvaldhcp = 0;
599
	$retvaldns = 0;
600
	/* dnsmasq_configure calls dhcpd_configure */
601
	/* no need to restart dhcpd twice */
602
	if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic']))	{
603
		$retvaldns = services_dnsmasq_configure();
604
		if ($retvaldns == 0) {
605
			clear_subsystem_dirty('hosts');
606
			clear_subsystem_dirty('staticmaps');
607
		}
608
	} else if (isset($config['unbound']['enable']) && isset($config['unbound']['regdhcpstatic'])) {
609
		$retvaldns = services_unbound_configure();
610
		if ($retvaldns == 0) {
611
			clear_subsystem_dirty('unbound');
612
			clear_subsystem_dirty('hosts');
613
			clear_subsystem_dirty('staticmaps');
614
		}
615
	} else {
616
		$retvaldhcp = services_dhcpd_configure();
617
		if ($retvaldhcp == 0) {
618
			clear_subsystem_dirty('staticmaps');
619
		}
620
	}
621
	if ($dhcpd_enable_changed) {
622
		$retvalfc = filter_configure();
623
	}
624

    
625
	if ($retvaldhcp == 1 || $retvaldns == 1 || $retvalfc == 1) {
626
		$retval = 1;
627
	}
628

    
629
	$savemsg = get_std_save_message($retval);
630
}
631

    
632
if ($act == "delpool") {
633
	if ($a_pools[$_GET['id']]) {
634
		unset($a_pools[$_GET['id']]);
635
		write_config();
636
		header("Location: services_dhcp.php?if={$if}");
637
		exit;
638
	}
639
}
640

    
641
if ($act == "del") {
642
	if ($a_maps[$_GET['id']]) {
643
		/* Remove static ARP entry, if necessary */
644
		if (isset($a_maps[$_GET['id']]['arp_table_static_entry'])) {
645
			mwexec("/usr/sbin/arp -d " . escapeshellarg($a_maps[$_GET['id']]['ipaddr']));
646
		}
647
		unset($a_maps[$_GET['id']]);
648
		write_config();
649
		if (isset($config['dhcpd'][$if]['enable'])) {
650
			mark_subsystem_dirty('staticmaps');
651
			if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic'])) {
652
				mark_subsystem_dirty('hosts');
653
			}
654
		}
655

    
656
		header("Location: services_dhcp.php?if={$if}");
657
		exit;
658
	}
659
}
660

    
661
// Build an HTML table that can be inserted into a Form_StaticText element
662
function build_pooltable() {
663
	global $a_pools, $if;
664

    
665
	$pooltbl =	'<div class="table-responsive">';
666
	$pooltbl .=		'<table class="table table-striped table-hover table-condensed">';
667
	$pooltbl .=			'<thead>';
668
	$pooltbl .=				'<tr>';
669
	$pooltbl .=					'<th>' . gettext("Pool Start") . '</th>';
670
	$pooltbl .=					'<th>' . gettext("Pool End") . '</th>';
671
	$pooltbl .=					'<th>' . gettext("Description") . '</th>';
672
	$pooltbl .=					'<th>' . gettext("Actions") . '</th>';
673
	$pooltbl .=				'</tr>';
674
	$pooltbl .=			'</thead>';
675
	$pooltbl .=			'<tbody>';
676

    
677
	if (is_array($a_pools)) {
678
		$i = 0;
679
		foreach ($a_pools as $poolent) {
680
			if (!empty($poolent['range']['from']) && !empty($poolent['range']['to'])) {
681
				$pooltbl .= '<tr>';
682
				$pooltbl .= '<td ondblclick="document.location=\'services_dhcp.php?if=' . htmlspecialchars($if) . '&pool=' . $i . '\';">' .
683
							htmlspecialchars($poolent['range']['from']) . '</td>';
684

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

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

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

    
693
				$pooltbl .= ' <a class="fa fa-trash" title="'. gettext("Delete pool") . '" href="services_dhcp.php?if=' . htmlspecialchars($if) . '&act=delpool&id=' . $i . '"></a></td>';
694
				$pooltbl .= '</tr>';
695
			}
696
		$i++;
697
		}
698
	}
699

    
700
	$pooltbl .=			'</tbody>';
701
	$pooltbl .=		'</table>';
702
	$pooltbl .= '</div>';
703

    
704
	return($pooltbl);
705
}
706

    
707
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
708

    
709
if (!empty($if) && isset($iflist[$if])) {
710
	$pgtitle[] = $iflist[$if];
711
}
712
$shortcut_section = "dhcp";
713

    
714
include("head.inc");
715

    
716
if ($input_errors) {
717
	print_input_errors($input_errors);
718
}
719

    
720
if ($savemsg) {
721
	print_info_box($savemsg, 'success');
722
}
723

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

    
728
/* active tabs */
729
$tab_array = array();
730
$tabscounter = 0;
731
$i = 0;
732

    
733
foreach ($iflist as $ifent => $ifname) {
734
	$oc = $config['interfaces'][$ifent];
735
	if ((is_array($config['dhcpd'][$ifent]) && !isset($config['dhcpd'][$ifent]['enable']) && (!is_ipaddrv4($oc['ipaddr']))) ||
736
	    (!is_array($config['dhcpd'][$ifent]) && (!is_ipaddrv4($oc['ipaddr'])))) {
737
		continue;
738
	}
739

    
740
	if ($ifent == $if) {
741
		$active = true;
742
	} else {
743
		$active = false;
744
	}
745

    
746
	$tab_array[] = array($ifname, $active, "services_dhcp.php?if={$ifent}");
747
	$tabscounter++;
748
}
749

    
750
if ($tabscounter == 0) {
751
	print_info_box(gettext("The DHCP Server can only be enabled on interfaces configured with a static IPv4 address. This system has none."));
752
	include("foot.inc");
753
	exit;
754
}
755

    
756
display_top_tabs($tab_array);
757

    
758
$form = new Form();
759

    
760
$section = new Form_Section('General Options');
761

    
762
if (!is_numeric($pool) && !($act == "newpool")) {
763
	if (isset($config['dhcrelay']['enable'])) {
764
		$section->addInput(new Form_Checkbox(
765
			'enable',
766
			'Enable',
767
			gettext("DHCP Relay is currently enabled. DHCP Server canot be enabled while the DHCP Relay is enabled on any interface."),
768
			$pconfig['enable']
769
		))->setAttribute('disabled', true);
770
	} else {
771
		$section->addInput(new Form_Checkbox(
772
			'enable',
773
			'Enable',
774
			sprintf(gettext("Enable DHCP server on %s interface"), htmlspecialchars($iflist[$if])),
775
			$pconfig['enable']
776
		));
777
	}
778
} else {
779
	print_info_box(gettext('Editing pool-specific options. To return to the Interface, click its tab above.'), 'info', false);
780
}
781

    
782
$section->addInput(new Form_Checkbox(
783
	'ignorebootp',
784
	'BOOTP',
785
	'Ignore BOOTP queries',
786
	$pconfig['ignorebootp']
787
));
788

    
789
$section->addInput(new Form_Checkbox(
790
	'denyunknown',
791
	'Deny unknown clients',
792
	'Only the clients defined below will get DHCP leases from this server.',
793
	$pconfig['denyunknown']
794
));
795

    
796
$section->addInput(new Form_Checkbox(
797
	'nonak',
798
	'Ignore denied clients',
799
	'Denied clients will be ignored rather than rejected.',
800
	$pconfig['nonak']
801
))->setHelp("This option is not compatible with failover and cannot be enabled when a Failover Peer IP address is configured.");
802

    
803

    
804
if (is_numeric($pool) || ($act == "newpool")) {
805
	$section->addInput(new Form_Input(
806
		'descr',
807
		'Pool Description',
808
		'text',
809
		$pconfig['descr']
810
	));
811
}
812

    
813
$section->addInput(new Form_StaticText(
814
	'Subnet',
815
	gen_subnet($ifcfgip, $ifcfgsn)
816
));
817

    
818
$section->addInput(new Form_StaticText(
819
	'Subnet mask',
820
	gen_subnet_mask($ifcfgsn)
821
));
822

    
823
// Compose a string to display the required address ranges
824
$rangestr = ip_after($subnet_start) . ' - ' . ip_before($subnet_end);
825

    
826
if (is_numeric($pool) || ($act == "newpool")) {
827
	$rangestr .= '<br />' . gettext('In-use DHCP Pool Ranges:');
828
	if (is_array($config['dhcpd'][$if]['range'])) {
829
		$rangestr .= '<br />' . $config['dhcpd'][$if]['range']['from'] . ' - ' . $config['dhcpd'][$if]['range']['to'];
830
	}
831

    
832
	foreach ($a_pools as $p) {
833
		if (is_array($p['range'])) {
834
			$rangestr .= '<br />' . $p['range']['from'] . ' - ' . $p['range']['to'];
835
		}
836
	}
837
}
838

    
839
$section->addInput(new Form_StaticText(
840
	'Available range',
841
	$rangestr
842
));
843

    
844
if ($is_olsr_enabled) {
845
	$section->addInput(new Form_Select(
846
		'netmask',
847
		'Subnet mask',
848
		$pconfig['netmask'],
849
		array_combine(range(32, 1, -1), range(32, 1, -1))
850
	));
851
}
852

    
853
$group = new Form_Group('Range');
854

    
855
$group->add(new Form_IpAddress(
856
	'range_from',
857
	null,
858
	$pconfig['range_from'],
859
	'V4'
860
))->setHelp('From');
861

    
862
$group->add(new Form_IpAddress(
863
	'range_to',
864
	null,
865
	$pconfig['range_to'],
866
	'V4'
867
))->setHelp('To');
868

    
869
$section->add($group);
870

    
871
$form->add($section);
872

    
873
if (!is_numeric($pool) && !($act == "newpool")) {
874
	$section = new Form_Section('Additional Pools');
875

    
876
	$btnaddpool = new Form_Button(
877
		'btnaddpool',
878
		'Add pool',
879
		'services_dhcp.php?if=' . htmlspecialchars($if) . '&act=newpool',
880
		'fa-plus'
881
	);
882
	$btnaddpool->addClass('btn-success');
883

    
884
	$section->addInput(new Form_StaticText(
885
		'Add',
886
		$btnaddpool
887
	))->setHelp('If additional pools of addresses are needed inside of this subnet outside the above Range, they may be specified here.');
888

    
889
	if (is_array($a_pools)) {
890
		$section->addInput(new Form_StaticText(
891
			null,
892
			build_pooltable()
893
		));
894
	}
895

    
896
	$form->add($section);
897
}
898

    
899
$section = new Form_Section('Servers');
900

    
901
$section->addInput(new Form_IpAddress(
902
	'wins1',
903
	'WINS servers',
904
	$pconfig['wins1'],
905
	'V4'
906
))->setAttribute('placeholder', 'WINS Server 1');
907

    
908
$section->addInput(new Form_IpAddress(
909
	'wins2',
910
	null,
911
	$pconfig['wins2'],
912
	'V4'
913
))->setAttribute('placeholder', 'WINS Server 2');
914

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

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

    
926
$section = new Form_Section('Other Options');
927

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1027
$section->addInput(new Form_Checkbox(
1028
	'ddnsforcehostname',
1029
	'DDNS Hostnames',
1030
	'Force dynamic DNS hostname to be the same as configured hostname for Static Mappings',
1031
	$pconfig['ddnsforcehostname']
1032
))->setHelp('Default registers host name option supplied by DHCP client.');
1033

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1270

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

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

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

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

    
1294
	$counter++;
1295
}
1296

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

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

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

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

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

    
1331
print($form);
1332

    
1333
// DHCP Static Mappings table
1334

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

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

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

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

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

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

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

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

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

    
1449
	// Show advanced MAC options ======================================================================================
1450
	var showadvmac = false;
1451

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

    
1469
		hideInput('mac_allow', !showadvmac);
1470
		hideInput('mac_deny', !showadvmac);
1471

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

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

    
1484
	// Show advanced NTP options ======================================================================================
1485
	var showadvntp = false;
1486

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

    
1504
		hideInput('ntp1', !showadvntp);
1505
		hideInput('ntp2', !showadvntp);
1506

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

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

    
1519
	// Show advanced TFTP options ======================================================================================
1520
	var showadvtftp = false;
1521

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

    
1539
		hideInput('tftp', !showadvtftp);
1540

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

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

    
1553
	// Show advanced LDAP options ======================================================================================
1554
	var showadvldap = false;
1555

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

    
1573
		hideInput('ldap', !showadvldap);
1574

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

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

    
1587
	// Show advanced additional opts options ===========================================================================
1588
	var showadvopts = false;
1589

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

    
1608
		hideClass('adnlopts', !showadvopts);
1609

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

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

    
1622
	// Show advanced Network Booting options ===========================================================================
1623
	var showadvnwkboot = false;
1624

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

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

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

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

    
1661
	// ---------- On initial page load ------------------------------------------------------------
1662

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

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

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