Project

General

Profile

Download (50.9 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * services_dhcp.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2016 Rubicon Communications, LLC (Netgate)
7
 * All rights reserved.
8
 *
9
 * originally based on m0n0wall (http://m0n0.ch/wall)
10
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
11
 * All rights reserved.
12
 *
13
 * Licensed under the Apache License, Version 2.0 (the "License");
14
 * you may not use this file except in compliance with the License.
15
 * You may obtain a copy of the License at
16
 *
17
 * http://www.apache.org/licenses/LICENSE-2.0
18
 *
19
 * Unless required by applicable law or agreed to in writing, software
20
 * distributed under the License is distributed on an "AS IS" BASIS,
21
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
 * See the License for the specific language governing permissions and
23
 * limitations under the License.
24
 */
25

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

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

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

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

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

    
58
$iflist = get_configured_interface_with_descr();
59

    
60
/* set the starting interface */
61
if (!$if || !isset($iflist[$if])) {
62
	$found_starting_if = false;
63
	// First look for an interface with DHCP already enabled.
64
	foreach ($iflist as $ifent => $ifname) {
65
		$oc = $config['interfaces'][$ifent];
66
		if (is_array($config['dhcpd'][$ifent]) && 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
	if ($_POST['enable'] || is_numeric($pool) || $act == "newpool") {
236
		$reqdfields = explode(" ", "range_from range_to");
237
		$reqdfieldsn = array(gettext("Range begin"), gettext("Range end"));
238

    
239
		do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
240

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

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

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

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

    
273
		if (isset($config['captiveportal']) && is_array($config['captiveportal'])) {
274
			$deftime = 7200; // Default value if it's empty
275
			if (is_numeric($_POST['deftime'])) {
276
				$deftime = $_POST['deftime'];
277
			}
278

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

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

    
320
		// Validate MACs
321
		if (!empty($_POST['mac_allow']) && !validate_partial_mac_list($_POST['mac_allow'])) {
322
			$input_errors[] = gettext("If a mac allow list is specified, it must contain only valid partial MAC addresses.");
323
		}
324
		if (!empty($_POST['mac_deny']) && !validate_partial_mac_list($_POST['mac_deny'])) {
325
			$input_errors[] = gettext("If a mac deny list is specified, it must contain only valid partial MAC addresses.");
326
		}
327

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

    
341
		if (gen_subnet($ifcfgip, $ifcfgsn) == $_POST['range_from']) {
342
			$input_errors[] = gettext("The network address cannot be used in the starting subnet range.");
343
		}
344
		if (gen_subnet_max($ifcfgip, $ifcfgsn) == $_POST['range_to']) {
345
			$input_errors[] = gettext("The broadcast address cannot be used in the ending subnet range.");
346
		}
347

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

    
359
		$noip = false;
360
		if (is_array($a_maps)) {
361
			foreach ($a_maps as $map) {
362
				if (empty($map['ipaddr'])) {
363
					$noip = true;
364
				}
365
			}
366
		}
367

    
368
		if ($_POST['staticarp'] && $noip) {
369
			$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.");
370
		}
371

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

    
399
		if (!$input_errors) {
400
			/* make sure the range lies within the current subnet */
401
			if (ip_greater_than($_POST['range_from'], $_POST['range_to'])) {
402
				$input_errors[] = gettext("The range is invalid (first element higher than second element).");
403
			}
404

    
405
			if (!is_inrange_v4($_POST['range_from'], $subnet_start, $subnet_end) ||
406
			    !is_inrange_v4($_POST['range_to'], $subnet_start, $subnet_end)) {
407
				$input_errors[] = gettext("The specified range lies outside of the current subnet.");
408
			}
409

    
410
			if (is_numeric($pool) || ($act == "newpool")) {
411
				if (is_inrange_v4($_POST['range_from'],
412
				    $config['dhcpd'][$if]['range']['from'],
413
				    $config['dhcpd'][$if]['range']['to']) ||
414
				    is_inrange_v4($_POST['range_to'],
415
				    $config['dhcpd'][$if]['range']['from'],
416
				    $config['dhcpd'][$if]['range']['to'])) {
417
					$input_errors[] = gettext("The specified range must not be within the DHCP range for this interface.");
418
				}
419
			}
420

    
421
			foreach ($a_pools as $id => $p) {
422
				if (is_numeric($pool) && ($id == $pool)) {
423
					continue;
424
				}
425

    
426
				if (is_inrange_v4($_POST['range_from'],
427
				    $p['range']['from'], $p['range']['to']) ||
428
				    is_inrange_v4($_POST['range_to'],
429
				    $p['range']['from'], $p['range']['to'])) {
430
					$input_errors[] = gettext("The specified range must not be within the range configured on a DHCP pool for this interface.");
431
					break;
432
				}
433
			}
434

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

    
440
			if (is_array($a_maps)) {
441
				foreach ($a_maps as $map) {
442
					if (empty($map['ipaddr'])) {
443
						continue;
444
					}
445
					if (is_inrange_v4($map['ipaddr'], $_POST['range_from'], $_POST['range_to'])) {
446
						$input_errors[] = sprintf(gettext("The DHCP range cannot overlap any static DHCP mappings."));
447
						break;
448
					}
449
				}
450
			}
451
		}
452
	}
453

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

    
477
		$dhcpd_enable_changed = false;
478

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

    
488
			$dhcpdconf['enable'] = $new_dhcpd_enable;
489
			$dhcpdconf['staticarp'] = ($_POST['staticarp']) ? true : false;
490
			$previous = $dhcpdconf['failover_peerip'];
491
			if ($previous != $_POST['failover_peerip']) {
492
				mwexec("/bin/rm -rf /var/dhcpd/var/db/*");
493
			}
494

    
495
			$dhcpdconf['failover_peerip'] = $_POST['failover_peerip'];
496
			// dhcpleaseinlocaltime is global to all interfaces. So update the setting on all interfaces.
497
			foreach ($config['dhcpd'] as &$dhcpdifitem) {
498
				$dhcpdifitem['dhcpleaseinlocaltime'] = $_POST['dhcpleaseinlocaltime'];
499
			}
500
		} else {
501
			// Options that exist only in pools
502
			$dhcpdconf['descr'] = $_POST['descr'];
503
		}
504

    
505
		// Options that can be global or per-pool.
506
		$dhcpdconf['range']['from'] = $_POST['range_from'];
507
		$dhcpdconf['range']['to'] = $_POST['range_to'];
508
		$dhcpdconf['defaultleasetime'] = $_POST['deftime'];
509
		$dhcpdconf['maxleasetime'] = $_POST['maxtime'];
510
		$dhcpdconf['netmask'] = $_POST['netmask'];
511

    
512
		unset($dhcpdconf['winsserver']);
513
		if ($_POST['wins1']) {
514
			$dhcpdconf['winsserver'][] = $_POST['wins1'];
515
		}
516
		if ($_POST['wins2']) {
517
			$dhcpdconf['winsserver'][] = $_POST['wins2'];
518
		}
519

    
520
		unset($dhcpdconf['dnsserver']);
521
		if ($_POST['dns1']) {
522
			$dhcpdconf['dnsserver'][] = $_POST['dns1'];
523
		}
524
		if ($_POST['dns2']) {
525
			$dhcpdconf['dnsserver'][] = $_POST['dns2'];
526
		}
527
		if ($_POST['dns3']) {
528
			$dhcpdconf['dnsserver'][] = $_POST['dns3'];
529
		}
530
		if ($_POST['dns4']) {
531
			$dhcpdconf['dnsserver'][] = $_POST['dns4'];
532
		}
533

    
534
		$dhcpdconf['gateway'] = $_POST['gateway'];
535
		$dhcpdconf['domain'] = $_POST['domain'];
536
		$dhcpdconf['domainsearchlist'] = $_POST['domainsearchlist'];
537
		$dhcpdconf['ignorebootp'] = ($_POST['ignorebootp']) ? true : false;
538
		$dhcpdconf['denyunknown'] = ($_POST['denyunknown']) ? true : false;
539
		$dhcpdconf['nonak'] = ($_POST['nonak']) ? true : false;
540
		$dhcpdconf['ddnsdomain'] = $_POST['ddnsdomain'];
541
		$dhcpdconf['ddnsdomainprimary'] = $_POST['ddnsdomainprimary'];
542
		$dhcpdconf['ddnsdomainkeyname'] = $_POST['ddnsdomainkeyname'];
543
		$dhcpdconf['ddnsdomainkey'] = $_POST['ddnsdomainkey'];
544
		$dhcpdconf['ddnsupdate'] = ($_POST['ddnsupdate']) ? true : false;
545
		$dhcpdconf['ddnsforcehostname'] = ($_POST['ddnsforcehostname']) ? true : false;
546
		$dhcpdconf['mac_allow'] = $_POST['mac_allow'];
547
		$dhcpdconf['mac_deny'] = $_POST['mac_deny'];
548

    
549
		unset($dhcpdconf['ntpserver']);
550
		if ($_POST['ntp1']) {
551
			$dhcpdconf['ntpserver'][] = $_POST['ntp1'];
552
		}
553
		if ($_POST['ntp2']) {
554
			$dhcpdconf['ntpserver'][] = $_POST['ntp2'];
555
		}
556

    
557
		$dhcpdconf['tftp'] = $_POST['tftp'];
558
		$dhcpdconf['ldap'] = $_POST['ldap'];
559
		$dhcpdconf['netboot'] = ($_POST['netboot']) ? true : false;
560
		$dhcpdconf['nextserver'] = $_POST['nextserver'];
561
		$dhcpdconf['filename'] = $_POST['filename'];
562
		$dhcpdconf['filename32'] = $_POST['filename32'];
563
		$dhcpdconf['filename64'] = $_POST['filename64'];
564
		$dhcpdconf['rootpath'] = $_POST['rootpath'];
565
		unset($dhcpdconf['statsgraph']);
566
		if ($_POST['statsgraph']) {
567
			$dhcpdconf['statsgraph'] = $_POST['statsgraph'];
568
			enable_rrd_graphing();
569
		}
570

    
571
		// Handle the custom options rowhelper
572
		if (isset($dhcpdconf['numberoptions']['item'])) {
573
			unset($dhcpdconf['numberoptions']['item']);
574
		}
575

    
576
		$dhcpdconf['numberoptions'] = $numberoptions;
577

    
578
		if (is_numeric($pool) && is_array($a_pools[$pool])) {
579
			$a_pools[$pool] = $dhcpdconf;
580
		} elseif ($act == "newpool") {
581
			$a_pools[] = $dhcpdconf;
582
		} else {
583
			$config['dhcpd'][$if] = $dhcpdconf;
584
		}
585

    
586
		write_config();
587
	}
588
}
589

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

    
619
	if ($retvaldhcp == 1 || $retvaldns == 1 || $retvalfc == 1) {
620
		$retval = 1;
621
	}
622

    
623
	$savemsg = get_std_save_message($retval);
624
}
625

    
626
if ($act == "delpool") {
627
	if ($a_pools[$_GET['id']]) {
628
		unset($a_pools[$_GET['id']]);
629
		write_config();
630
		header("Location: services_dhcp.php?if={$if}");
631
		exit;
632
	}
633
}
634

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

    
650
		header("Location: services_dhcp.php?if={$if}");
651
		exit;
652
	}
653
}
654

    
655
// Build an HTML table that can be inserted into a Form_StaticText element
656
function build_pooltable() {
657
	global $a_pools, $if;
658

    
659
	$pooltbl =	'<div class="table-responsive">';
660
	$pooltbl .=		'<table class="table table-striped table-hover table-condensed">';
661
	$pooltbl .=			'<thead>';
662
	$pooltbl .=				'<tr>';
663
	$pooltbl .=					'<th>' . gettext("Pool Start") . '</th>';
664
	$pooltbl .=					'<th>' . gettext("Pool End") . '</th>';
665
	$pooltbl .=					'<th>' . gettext("Description") . '</th>';
666
	$pooltbl .=					'<th>' . gettext("Actions") . '</th>';
667
	$pooltbl .=				'</tr>';
668
	$pooltbl .=			'</thead>';
669
	$pooltbl .=			'<tbody>';
670

    
671
	if (is_array($a_pools)) {
672
		$i = 0;
673
		foreach ($a_pools as $poolent) {
674
			if (!empty($poolent['range']['from']) && !empty($poolent['range']['to'])) {
675
				$pooltbl .= '<tr>';
676
				$pooltbl .= '<td ondblclick="document.location=\'services_dhcp.php?if=' . htmlspecialchars($if) . '&pool=' . $i . '\';">' .
677
							htmlspecialchars($poolent['range']['from']) . '</td>';
678

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

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

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

    
687
				$pooltbl .= ' <a class="fa fa-trash" title="'. gettext("Delete pool") . '" href="services_dhcp.php?if=' . htmlspecialchars($if) . '&act=delpool&id=' . $i . '"></a></td>';
688
				$pooltbl .= '</tr>';
689
			}
690
		$i++;
691
		}
692
	}
693

    
694
	$pooltbl .=			'</tbody>';
695
	$pooltbl .=		'</table>';
696
	$pooltbl .= '</div>';
697

    
698
	return($pooltbl);
699
}
700

    
701
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
702

    
703
if (!empty($if) && !isset($config['dhcrelay']['enable']) && isset($iflist[$if])) {
704
	$pgtitle[] = $iflist[$if];
705
}
706
$shortcut_section = "dhcp";
707

    
708
include("head.inc");
709

    
710
if ($input_errors) {
711
	print_input_errors($input_errors);
712
}
713

    
714
if ($savemsg) {
715
	print_info_box($savemsg, 'success');
716
}
717

    
718
if (isset($config['dhcrelay']['enable'])) {
719
	print_info_box(gettext("DHCP Relay is currently enabled. Cannot enable the DHCP Server service while the DHCP Relay is enabled on any interface."));
720
	include("foot.inc");
721
	exit;
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
	$section->addInput(new Form_Checkbox(
764
		'enable',
765
		'Enable',
766
		sprintf(gettext("Enable DHCP server on %s interface"), htmlspecialchars($iflist[$if])),
767
		$pconfig['enable']
768
	));
769
} else {
770
	print_info_box(gettext('Editing pool-specific options. To return to the Interface, click its tab above.'), 'info', false);
771
}
772

    
773
$section->addInput(new Form_Checkbox(
774
	'ignorebootp',
775
	'BOOTP',
776
	'Ignore BOOTP queries',
777
	$pconfig['ignorebootp']
778
));
779

    
780
$section->addInput(new Form_Checkbox(
781
	'denyunknown',
782
	'Deny unknown clients',
783
	'Only the clients defined below will get DHCP leases from this server.',
784
	$pconfig['denyunknown']
785
));
786

    
787
$section->addInput(new Form_Checkbox(
788
	'nonak',
789
	'Ignore denied clients',
790
	'Denied clients will be ignored rather than rejected.',
791
	$pconfig['nonak']
792
))->setHelp("This option is not compatible with failover and cannot be enabled when a Failover Peer IP address is configured.");
793

    
794

    
795
if (is_numeric($pool) || ($act == "newpool")) {
796
	$section->addInput(new Form_Input(
797
		'descr',
798
		'Pool Description',
799
		'text',
800
		$pconfig['descr']
801
	));
802
}
803

    
804
$section->addInput(new Form_StaticText(
805
	'Subnet',
806
	gen_subnet($ifcfgip, $ifcfgsn)
807
));
808

    
809
$section->addInput(new Form_StaticText(
810
	'Subnet mask',
811
	gen_subnet_mask($ifcfgsn)
812
));
813

    
814
// Compose a string to display the required address ranges
815
$rangestr = ip_after($subnet_start) . ' - ' . ip_before($subnet_end);
816

    
817
if (is_numeric($pool) || ($act == "newpool")) {
818
	$rangestr .= '<br />' . gettext('In-use DHCP Pool Ranges:');
819
	if (is_array($config['dhcpd'][$if]['range'])) {
820
		$rangestr .= '<br />' . $config['dhcpd'][$if]['range']['from'] . ' - ' . $config['dhcpd'][$if]['range']['to'];
821
	}
822

    
823
	foreach ($a_pools as $p) {
824
		if (is_array($p['range'])) {
825
			$rangestr .= '<br />' . $p['range']['from'] . ' - ' . $p['range']['to'];
826
		}
827
	}
828
}
829

    
830
$section->addInput(new Form_StaticText(
831
	'Available range',
832
	$rangestr
833
));
834

    
835
if ($is_olsr_enabled) {
836
	$section->addInput(new Form_Select(
837
		'netmask',
838
		'Subnet mask',
839
		$pconfig['netmask'],
840
		array_combine(range(32, 1, -1), range(32, 1, -1))
841
	));
842
}
843

    
844
$group = new Form_Group('Range');
845

    
846
$group->add(new Form_IpAddress(
847
	'range_from',
848
	null,
849
	$pconfig['range_from'],
850
	'V4'
851
))->setHelp('From');
852

    
853
$group->add(new Form_IpAddress(
854
	'range_to',
855
	null,
856
	$pconfig['range_to'],
857
	'V4'
858
))->setHelp('To');
859

    
860
$section->add($group);
861

    
862
$form->add($section);
863

    
864
if (!is_numeric($pool) && !($act == "newpool")) {
865
	$section = new Form_Section('Additional Pools');
866

    
867
	$btnaddpool = new Form_Button(
868
		'btnaddpool',
869
		'Add pool',
870
		'services_dhcp.php?if=' . htmlspecialchars($if) . '&act=newpool',
871
		'fa-plus'
872
	);
873
	$btnaddpool->addClass('btn-success');
874

    
875
	$section->addInput(new Form_StaticText(
876
		'Add',
877
		$btnaddpool
878
	))->setHelp('If additional pools of addresses are needed inside of this subnet outside the above Range, they may be specified here.');
879

    
880
	if (is_array($a_pools)) {
881
		$section->addInput(new Form_StaticText(
882
			null,
883
			build_pooltable()
884
		));
885
	}
886

    
887
	$form->add($section);
888
}
889

    
890
$section = new Form_Section('Servers');
891

    
892
$section->addInput(new Form_IpAddress(
893
	'wins1',
894
	'WINS servers',
895
	$pconfig['wins1'],
896
	'V4'
897
))->setAttribute('placeholder', 'WINS Server 1');
898

    
899
$section->addInput(new Form_IpAddress(
900
	'wins2',
901
	null,
902
	$pconfig['wins2'],
903
	'V4'
904
))->setAttribute('placeholder', 'WINS Server 2');
905

    
906
for ($idx=1; $idx<=4; $idx++) {
907
	$section->addInput(new Form_IpAddress(
908
		'dns' . $idx,
909
		($idx == 1) ? 'DNS servers':null,
910
		$pconfig['dns' . $idx],
911
		'V4'
912
	))->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.':'');
913
}
914

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

    
917
$section = new Form_Section('Other Options');
918

    
919
$section->addInput(new Form_IpAddress(
920
	'gateway',
921
	'Gateway',
922
	$pconfig['gateway'],
923
	'V4'
924
))->setPattern('[.a-zA-Z0-9_]+')
925
  ->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.');
926

    
927
$section->addInput(new Form_Input(
928
	'domain',
929
	'Domain name',
930
	'text',
931
	$pconfig['domain']
932
))->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.');
933

    
934
$section->addInput(new Form_Input(
935
	'domainsearchlist',
936
	'Domain search list',
937
	'text',
938
	$pconfig['domainsearchlist']
939
))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator.');
940

    
941
$section->addInput(new Form_Input(
942
	'deftime',
943
	'Default lease time',
944
	'number',
945
	$pconfig['deftime']
946
))->setHelp('This is used for clients that do not ask for a specific expiration time. The default is 7200 seconds.');
947

    
948
$section->addInput(new Form_Input(
949
	'maxtime',
950
	'Maximum lease time',
951
	'number',
952
	$pconfig['maxtime']
953
))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds.');
954

    
955
if (!is_numeric($pool) && !($act == "newpool")) {
956
	$section->addInput(new Form_IpAddress(
957
		'failover_peerip',
958
		'Failover peer IP',
959
		$pconfig['failover_peerip'],
960
		'V4'
961
	))->setHelp('Leave blank to disable. Enter the interface IP address of the other machine. Machines must be using CARP. ' .
962
				'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).');
963
}
964

    
965
if (!is_numeric($pool) && !($act == "newpool")) {
966
	$section->addInput(new Form_Checkbox(
967
		'staticarp',
968
		'Static ARP',
969
		'Enable Static ARP entries',
970
		$pconfig['staticarp']
971
	))->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.');
972

    
973
	$section->addInput(new Form_Checkbox(
974
		'dhcpleaseinlocaltime',
975
		'Time format change',
976
		'Change DHCP display lease time from UTC to local time',
977
		$pconfig['dhcpleaseinlocaltime']
978
	))->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.' .
979
				' This will be used for all DHCP interfaces lease time.');
980
	$section->addInput(new Form_Checkbox(
981
		'statsgraph',
982
		'Statistics graphs',
983
		'Enable RRD statistics graphs',
984
		$pconfig['statsgraph']
985
	))->setHelp('Enable this to add DHCP leases statistics to the RRD graphs. Disabled by default.');
986
}
987

    
988
// DDNS
989
$btnadv = new Form_Button(
990
	'btnadvdns',
991
	'Display Advanced',
992
	null,
993
	'fa-cog'
994
);
995

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

    
998
$section->addInput(new Form_StaticText(
999
	'Dynamic DNS',
1000
	$btnadv
1001
));
1002

    
1003
$section->addInput(new Form_Checkbox(
1004
	'ddnsupdate',
1005
	null,
1006
	'Enable registration of DHCP client names in DNS',
1007
	$pconfig['ddnsupdate']
1008
));
1009

    
1010
$section->addInput(new Form_Input(
1011
	'ddnsdomain',
1012
	'DDNS Domain',
1013
	'text',
1014
	$pconfig['ddnsdomain']
1015
))->setHelp('Leave blank to disable dynamic DNS registration.' . '<br />' .
1016
			'Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
1017

    
1018
$section->addInput(new Form_Checkbox(
1019
	'ddnsforcehostname',
1020
	'DDNS Hostnames',
1021
	'Force dynamic DNS hostname to be the same as configured hostname for Static Mappings',
1022
	$pconfig['ddnsforcehostname']
1023
))->setHelp('Default registers host name option supplied by DHCP client.');
1024

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

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

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

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

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

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

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

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

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

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

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

    
1090
$section->addInput(new Form_IpAddress(
1091
	'ntp1',
1092
	'NTP Server 1',
1093
	$pconfig['ntp1'],
1094
	'HOSTV4'
1095
));
1096

    
1097
$section->addInput(new Form_IpAddress(
1098
	'ntp2',
1099
	'NTP Server 2',
1100
	$pconfig['ntp2'],
1101
	'HOSTV4'
1102
));
1103

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

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

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

    
1119
$section->addInput(new Form_Input(
1120
	'tftp',
1121
	'TFTP Server',
1122
	$pconfig['tftp']
1123
))->setHelp('Leave blank to disable. Enter a valid IP address, hostname or URL for the TFTP server.');
1124

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1225
$section->addInput(new Form_StaticText(
1226
	null,
1227
	'<div class="alert alert-info"> ' . gettext('Enter the DHCP option number and the value for each item to include in the DHCP lease information.') . ' ' .
1228
	sprintf(gettext('For a list of available options please visit this %1$s URL%2$s'), '<a href="http://www.iana.org/assignments/bootp-dhcp-parameters/" target="_blank">', '</a>.</div>')
1229
));
1230

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

    
1235
$customitemtypes = array(
1236
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1237
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1238
	'signed integer 8' => gettext('Signed 8-bit integer'), 'signed integer 16' => gettext('Signed 16-bit integer'), 'signed integer 32' => gettext('Signed 32-bit integer'), 'ip-address' => gettext('IP address or host')
1239
);
1240

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

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

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

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

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

    
1261

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

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

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

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

    
1285
	$counter++;
1286
}
1287

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

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

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

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

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

    
1322
print($form);
1323

    
1324
// DHCP Static Mappings table
1325

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1652
	// ---------- On initial page load ------------------------------------------------------------
1653

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

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

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