Project

General

Profile

Download (50.3 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['mac_allow'] = $dhcpdconf['mac_allow'];
179
	$pconfig['mac_deny'] = $dhcpdconf['mac_deny'];
180
	list($pconfig['ntp1'], $pconfig['ntp2']) = $dhcpdconf['ntpserver'];
181
	$pconfig['tftp'] = $dhcpdconf['tftp'];
182
	$pconfig['ldap'] = $dhcpdconf['ldap'];
183
	$pconfig['netboot'] = isset($dhcpdconf['netboot']);
184
	$pconfig['nextserver'] = $dhcpdconf['nextserver'];
185
	$pconfig['filename'] = $dhcpdconf['filename'];
186
	$pconfig['filename32'] = $dhcpdconf['filename32'];
187
	$pconfig['filename64'] = $dhcpdconf['filename64'];
188
	$pconfig['rootpath'] = $dhcpdconf['rootpath'];
189
	$pconfig['netmask'] = $dhcpdconf['netmask'];
190
	$pconfig['numberoptions'] = $dhcpdconf['numberoptions'];
191
	$pconfig['statsgraph'] = $dhcpdconf['statsgraph'];
192
}
193

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

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

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

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

    
210
	return true;
211
}
212

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

    
215
	unset($input_errors);
216

    
217
	$pconfig = $_POST;
218

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

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

    
233
	/* input validation */
234
	if ($_POST['enable'] || is_numeric($pool) || $act == "newpool") {
235
		$reqdfields = explode(" ", "range_from range_to");
236
		$reqdfieldsn = array(gettext("Range begin"), gettext("Range end"));
237

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
476
		$dhcpd_enable_changed = false;
477

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

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

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

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

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

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

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

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

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

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

    
574
		$dhcpdconf['numberoptions'] = $numberoptions;
575

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

    
584
		write_config();
585
	}
586
}
587

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

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

    
621
	$savemsg = get_std_save_message($retval);
622
}
623

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

    
633
if ($act == "del") {
634
	if ($a_maps[$_GET['id']]) {
635
		unset($a_maps[$_GET['id']]);
636
		write_config();
637
		if (isset($config['dhcpd'][$if]['enable'])) {
638
			mark_subsystem_dirty('staticmaps');
639
			if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic'])) {
640
				mark_subsystem_dirty('hosts');
641
			}
642
		}
643

    
644
		header("Location: services_dhcp.php?if={$if}");
645
		exit;
646
	}
647
}
648

    
649
// Build an HTML table that can be inserted into a Form_StaticText element
650
function build_pooltable() {
651
	global $a_pools, $if;
652

    
653
	$pooltbl =	'<div class="table-responsive">';
654
	$pooltbl .=		'<table class="table table-striped table-hover table-condensed">';
655
	$pooltbl .=			'<thead>';
656
	$pooltbl .=				'<tr>';
657
	$pooltbl .=					'<th>' . gettext("Pool Start") . '</th>';
658
	$pooltbl .=					'<th>' . gettext("Pool End") . '</th>';
659
	$pooltbl .=					'<th>' . gettext("Description") . '</th>';
660
	$pooltbl .=					'<th>' . gettext("Actions") . '</th>';
661
	$pooltbl .=				'</tr>';
662
	$pooltbl .=			'</thead>';
663
	$pooltbl .=			'<tbody>';
664

    
665
	if (is_array($a_pools)) {
666
		$i = 0;
667
		foreach ($a_pools as $poolent) {
668
			if (!empty($poolent['range']['from']) && !empty($poolent['range']['to'])) {
669
				$pooltbl .= '<tr>';
670
				$pooltbl .= '<td ondblclick="document.location=\'services_dhcp.php?if=' . htmlspecialchars($if) . '&pool=' . $i . '\';">' .
671
							htmlspecialchars($poolent['range']['from']) . '</td>';
672

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

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

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

    
681
				$pooltbl .= ' <a class="fa fa-trash" title="'. gettext("Delete pool") . '" href="services_dhcp.php?if=' . htmlspecialchars($if) . '&act=delpool&id=' . $i . '"></a></td>';
682
				$pooltbl .= '</tr>';
683
			}
684
		$i++;
685
		}
686
	}
687

    
688
	$pooltbl .=			'</tbody>';
689
	$pooltbl .=		'</table>';
690
	$pooltbl .= '</div>';
691

    
692
	return($pooltbl);
693
}
694

    
695
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
696

    
697
if (!empty($if) && !isset($config['dhcrelay']['enable']) && isset($iflist[$if])) {
698
	$pgtitle[] = $iflist[$if];
699
}
700
$shortcut_section = "dhcp";
701

    
702
include("head.inc");
703

    
704
if ($input_errors) {
705
	print_input_errors($input_errors);
706
}
707

    
708
if ($savemsg) {
709
	print_info_box($savemsg, 'success');
710
}
711

    
712
if (isset($config['dhcrelay']['enable'])) {
713
	print_info_box(gettext("DHCP Relay is currently enabled. Cannot enable the DHCP Server service while the DHCP Relay is enabled on any interface."));
714
	include("foot.inc");
715
	exit;
716
}
717

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

    
722
/* active tabs */
723
$tab_array = array();
724
$tabscounter = 0;
725
$i = 0;
726

    
727
foreach ($iflist as $ifent => $ifname) {
728
	$oc = $config['interfaces'][$ifent];
729
	if ((is_array($config['dhcpd'][$ifent]) && !isset($config['dhcpd'][$ifent]['enable']) && (!is_ipaddrv4($oc['ipaddr']))) ||
730
	    (!is_array($config['dhcpd'][$ifent]) && (!is_ipaddrv4($oc['ipaddr'])))) {
731
		continue;
732
	}
733

    
734
	if ($ifent == $if) {
735
		$active = true;
736
	} else {
737
		$active = false;
738
	}
739

    
740
	$tab_array[] = array($ifname, $active, "services_dhcp.php?if={$ifent}");
741
	$tabscounter++;
742
}
743

    
744
if ($tabscounter == 0) {
745
	print_info_box(gettext("The DHCP Server can only be enabled on interfaces configured with a static IPv4 address. This system has none."));
746
	include("foot.inc");
747
	exit;
748
}
749

    
750
display_top_tabs($tab_array);
751

    
752
$form = new Form();
753

    
754
$section = new Form_Section('General Options');
755

    
756
if (!is_numeric($pool) && !($act == "newpool")) {
757
	$section->addInput(new Form_Checkbox(
758
		'enable',
759
		'Enable',
760
		sprintf(gettext("Enable DHCP server on %s interface"), htmlspecialchars($iflist[$if])),
761
		$pconfig['enable']
762
	));
763
} else {
764
	print_info_box(gettext('Editing pool-specific options. To return to the Interface, click its tab above.'), 'info', false);
765
}
766

    
767
$section->addInput(new Form_Checkbox(
768
	'ignorebootp',
769
	'BOOTP',
770
	'Ignore BOOTP queries',
771
	$pconfig['ignorebootp']
772
));
773

    
774
$section->addInput(new Form_Checkbox(
775
	'denyunknown',
776
	'Deny unknown clients',
777
	'Only the clients defined below will get DHCP leases from this server.',
778
	$pconfig['denyunknown']
779
));
780

    
781
$section->addInput(new Form_Checkbox(
782
	'nonak',
783
	'Ignore denied clients',
784
	'Denied clients will be ignored rather than rejected.',
785
	$pconfig['nonak']
786
))->setHelp("This option is not compatible with failover and cannot be enabled when a Failover Peer IP address is configured.");
787

    
788

    
789
if (is_numeric($pool) || ($act == "newpool")) {
790
	$section->addInput(new Form_Input(
791
		'descr',
792
		'Pool Description',
793
		'text',
794
		$pconfig['descr']
795
	));
796
}
797

    
798
$section->addInput(new Form_StaticText(
799
	'Subnet',
800
	gen_subnet($ifcfgip, $ifcfgsn)
801
));
802

    
803
$section->addInput(new Form_StaticText(
804
	'Subnet mask',
805
	gen_subnet_mask($ifcfgsn)
806
));
807

    
808
// Compose a string to display the required address ranges
809
$rangestr = ip_after($subnet_start) . ' - ' . ip_before($subnet_end);
810

    
811
if (is_numeric($pool) || ($act == "newpool")) {
812
	$rangestr .= '<br />' . gettext('In-use DHCP Pool Ranges:');
813
	if (is_array($config['dhcpd'][$if]['range'])) {
814
		$rangestr .= '<br />' . $config['dhcpd'][$if]['range']['from'] . ' - ' . $config['dhcpd'][$if]['range']['to'];
815
	}
816

    
817
	foreach ($a_pools as $p) {
818
		if (is_array($p['range'])) {
819
			$rangestr .= '<br />' . $p['range']['from'] . ' - ' . $p['range']['to'];
820
		}
821
	}
822
}
823

    
824
$section->addInput(new Form_StaticText(
825
	'Available range',
826
	$rangestr
827
));
828

    
829
if ($is_olsr_enabled) {
830
	$section->addInput(new Form_Select(
831
		'netmask',
832
		'Subnet mask',
833
		$pconfig['netmask'],
834
		array_combine(range(32, 1, -1), range(32, 1, -1))
835
	));
836
}
837

    
838
$group = new Form_Group('Range');
839

    
840
$group->add(new Form_IpAddress(
841
	'range_from',
842
	null,
843
	$pconfig['range_from']
844
))->setHelp('From');
845

    
846
$group->add(new Form_IpAddress(
847
	'range_to',
848
	null,
849
	$pconfig['range_to']
850
))->setHelp('To');
851

    
852
$section->add($group);
853

    
854
$form->add($section);
855

    
856
if (!is_numeric($pool) && !($act == "newpool")) {
857
	$section = new Form_Section('Additional Pools');
858

    
859
	$btnaddpool = new Form_Button(
860
		'btnaddpool',
861
		'Add pool',
862
		'services_dhcp.php?if=' . htmlspecialchars($if) . '&act=newpool',
863
		'fa-plus'
864
	);
865
	$btnaddpool->addClass('btn-success');
866

    
867
	$section->addInput(new Form_StaticText(
868
		'Add',
869
		$btnaddpool
870
	))->setHelp('If additional pools of addresses are needed inside of this subnet outside the above Range, they may be specified here.');
871

    
872
	if (is_array($a_pools)) {
873
		$section->addInput(new Form_StaticText(
874
			null,
875
			build_pooltable()
876
		));
877
	}
878

    
879
	$form->add($section);
880
}
881

    
882
$section = new Form_Section('Servers');
883

    
884
$section->addInput(new Form_IpAddress(
885
	'wins1',
886
	'WINS servers',
887
	$pconfig['wins1']
888
))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'WINS Server 1');
889

    
890
$section->addInput(new Form_IpAddress(
891
	'wins2',
892
	null,
893
	$pconfig['wins2']
894
))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'WINS Server 2');
895

    
896
for ($idx=1; $idx<=4; $idx++) {
897
	$section->addInput(new Form_IpAddress(
898
		'dns' . $idx,
899
		($idx == 1) ? 'DNS servers':null,
900
		$pconfig['dns' . $idx]
901
	))->setPattern('[.a-zA-Z0-9_]+')->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.':'');
902
}
903

    
904
$form->add($section);
905

    
906
$section = new Form_Section('Other Options');
907

    
908
$section->addInput(new Form_IpAddress(
909
	'gateway',
910
	'Gateway',
911
	$pconfig['gateway']
912
))->setPattern('[.a-zA-Z0-9_]+')
913
  ->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.');
914

    
915
$section->addInput(new Form_Input(
916
	'domain',
917
	'Domain name',
918
	'text',
919
	$pconfig['domain']
920
))->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.');
921

    
922
$section->addInput(new Form_Input(
923
	'domainsearchlist',
924
	'Domain search list',
925
	'text',
926
	$pconfig['domainsearchlist']
927
))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator.');
928

    
929
$section->addInput(new Form_Input(
930
	'deftime',
931
	'Default lease time',
932
	'number',
933
	$pconfig['deftime']
934
))->setHelp('This is used for clients that do not ask for a specific expiration time. The default is 7200 seconds.');
935

    
936
$section->addInput(new Form_Input(
937
	'maxtime',
938
	'Maximum lease time',
939
	'number',
940
	$pconfig['maxtime']
941
))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds.');
942

    
943
if (!is_numeric($pool) && !($act == "newpool")) {
944
	$section->addInput(new Form_IpAddress(
945
		'failover_peerip',
946
		'Failover peer IP',
947
		$pconfig['failover_peerip']
948
	))->setHelp('Leave blank to disable. Enter the interface IP address of the other machine. Machines must be using CARP. ' .
949
				'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).');
950
}
951

    
952
if (!is_numeric($pool) && !($act == "newpool")) {
953
	$section->addInput(new Form_Checkbox(
954
		'staticarp',
955
		'Static ARP',
956
		'Enable Static ARP entries',
957
		$pconfig['staticarp']
958
	))->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.');
959

    
960
	$section->addInput(new Form_Checkbox(
961
		'dhcpleaseinlocaltime',
962
		'Time format change',
963
		'Change DHCP display lease time from UTC to local time',
964
		$pconfig['dhcpleaseinlocaltime']
965
	))->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.' .
966
				' This will be used for all DHCP interfaces lease time.');
967
	$section->addInput(new Form_Checkbox(
968
		'statsgraph',
969
		'Statistics graphs',
970
		'Enable RRD statistics graphs',
971
		$pconfig['statsgraph']
972
	))->setHelp('Enable this to add DHCP leases statistics to the RRD graphs. Disabled by default.');
973
}
974

    
975
// DDNS
976
$btnadv = new Form_Button(
977
	'btnadvdns',
978
	'Display Advanced',
979
	null,
980
	'fa-cog'
981
);
982

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

    
985
$section->addInput(new Form_StaticText(
986
	'Dynamic DNS',
987
	$btnadv
988
));
989

    
990
$section->addInput(new Form_Checkbox(
991
	'ddnsupdate',
992
	null,
993
	'Enable registration of DHCP client names in DNS',
994
	$pconfig['ddnsupdate']
995
));
996

    
997
$section->addInput(new Form_Input(
998
	'ddnsdomain',
999
	'DDNS Domain',
1000
	'text',
1001
	$pconfig['ddnsdomain']
1002
))->setHelp('Leave blank to disable dynamic DNS registration.' . '<br />' .
1003
			'Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
1004

    
1005
$section->addInput(new Form_IpAddress(
1006
	'ddnsdomainprimary',
1007
	'Primary DDNS address',
1008
	$pconfig['ddnsdomainprimary']
1009
))->setHelp('Primary domain name server IP address for the dynamic domain name.');
1010

    
1011
$section->addInput(new Form_Input(
1012
	'ddnsdomainkeyname',
1013
	'DNS Domain key',
1014
	'text',
1015
	$pconfig['ddnsdomainkeyname']
1016
))->setHelp('Dynamic DNS domain key name which will be used to register client names in the DNS server.');
1017

    
1018
$section->addInput(new Form_Input(
1019
	'ddnsdomainkey',
1020
	'DNS Domain key secret',
1021
	'text',
1022
	$pconfig['ddnsdomainkey']
1023
))->setHelp('Dynamic DNS domain key secret (HMAC-MD5) which will be used to register client names in the DNS server.');
1024

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

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

    
1035
$section->addInput(new Form_StaticText(
1036
	'MAC address control',
1037
	$btnadv
1038
));
1039

    
1040
$section->addInput(new Form_Input(
1041
	'mac_allow',
1042
	'MAC Allow',
1043
	'text',
1044
	$pconfig['mac_allow']
1045
))->setHelp('List of partial MAC addresses to allow, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1046

    
1047
$section->addInput(new Form_Input(
1048
	'mac_deny',
1049
	'MAC Deny',
1050
	'text',
1051
	$pconfig['mac_deny']
1052
))->setHelp('List of partial MAC addresses to deny access, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1053

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

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

    
1064
$section->addInput(new Form_StaticText(
1065
	'NTP',
1066
	$btnadv
1067
));
1068

    
1069
$section->addInput(new Form_IpAddress(
1070
	'ntp1',
1071
	'NTP Server 1',
1072
	$pconfig['ntp1']
1073
))->setPattern('[.a-zA-Z0-9_]+');
1074

    
1075
$section->addInput(new Form_IpAddress(
1076
	'ntp2',
1077
	'NTP Server 2',
1078
	$pconfig['ntp2']
1079
))->setPattern('[.a-zA-Z0-9_]+');
1080

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

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

    
1091
$section->addInput(new Form_StaticText(
1092
	'TFTP',
1093
	$btnadv
1094
));
1095

    
1096
$section->addInput(new Form_Input(
1097
	'tftp',
1098
	'TFTP Server',
1099
	$pconfig['tftp']
1100
))->setHelp('Leave blank to disable. Enter a valid IP address, hostname or URL for the TFTP server.');
1101

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

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

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

    
1117
$section->addInput(new Form_Input(
1118
	'ldap',
1119
	'LDAP Server URI',
1120
	'text',
1121
	$pconfig['ldap']
1122
))->setHelp('Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com ');
1123

    
1124
// Advanced Network Booting options
1125
$btnadv = new Form_Button(
1126
	'btnadvnwkboot',
1127
	'Display Advanced',
1128
	null,
1129
	'fa-cog'
1130
);
1131

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

    
1134
$section->addInput(new Form_StaticText(
1135
	'Network Booting',
1136
	$btnadv
1137
));
1138

    
1139
$section->addInput(new Form_Checkbox(
1140
	'netboot',
1141
	'Enable',
1142
	'Enables network booting',
1143
	$pconfig['netboot']
1144
));
1145

    
1146
$section->addInput(new Form_IpAddress(
1147
	'nextserver',
1148
	'Next Server',
1149
	$pconfig['nextserver']
1150
))->setHelp('Enter the IP address of the next server');
1151

    
1152
$section->addInput(new Form_Input(
1153
	'filename',
1154
	'Default BIOS file name',
1155
	'text',
1156
	$pconfig['filename']
1157
));
1158

    
1159
$section->addInput(new Form_Input(
1160
	'filename32',
1161
	'UEFI 32 bit file name',
1162
	'text',
1163
	$pconfig['filename32']
1164
));
1165

    
1166
$section->addInput(new Form_Input(
1167
	'filename64',
1168
	'UEFI 64 bit file name',
1169
	'text',
1170
	$pconfig['filename64']
1171
))->setHelp('Both a filename and a boot server must be configured for this to work! ' .
1172
			'All three filenames and a configured boot server are necessary for UEFI to work! ');
1173

    
1174
$section->addInput(new Form_Input(
1175
	'rootpath',
1176
	'Root path',
1177
	'text',
1178
	$pconfig['rootpath']
1179
))->setHelp('string-format: iscsi:(servername):(protocol):(port):(LUN):targetname ');
1180

    
1181
// Advanced Additional options
1182
$btnadv = new Form_Button(
1183
	'btnadvopts',
1184
	'Display Advanced',
1185
	null,
1186
	'fa-cog'
1187
);
1188

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

    
1191
$section->addInput(new Form_StaticText(
1192
	'Additional BOOTP/DHCP Options',
1193
	$btnadv
1194
));
1195

    
1196
$form->add($section);
1197

    
1198
$section = new Form_Section('Additional BOOTP/DHCP Options');
1199
$section->addClass('adnlopts');
1200

    
1201
$section->addInput(new Form_StaticText(
1202
	null,
1203
	'<div class="alert alert-info"> ' . gettext('Enter the DHCP option number and the value for each item to include in the DHCP lease information.') . ' ' .
1204
	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>')
1205
));
1206

    
1207
if (!$pconfig['numberoptions']) {
1208
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1209
}
1210

    
1211
$customitemtypes = array(
1212
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1213
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1214
	'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')
1215
);
1216

    
1217
$numrows = count($item) -1;
1218
$counter = 0;
1219

    
1220
$numrows = count($pconfig['numberoptions']['item']) -1;
1221

    
1222
foreach ($pconfig['numberoptions']['item'] as $item) {
1223
	$number = $item['number'];
1224
	$itemtype = $item['type'];
1225
	$value = base64_decode($item['value']);
1226

    
1227
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1228
	$group->addClass('repeatable');
1229

    
1230
	$group->add(new Form_Input(
1231
		'number' . $counter,
1232
		null,
1233
		'text',
1234
		$number
1235
	))->setHelp($numrows == $counter ? 'Number':null);
1236

    
1237

    
1238
	$group->add(new Form_Select(
1239
		'itemtype' . $counter,
1240
		null,
1241
		$itemtype,
1242
		$customitemtypes
1243
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1244

    
1245
	$group->add(new Form_Input(
1246
		'value' . $counter,
1247
		null,
1248
		'text',
1249
		$value
1250
	))->setHelp($numrows == $counter ? 'Value':null);
1251

    
1252
	$group->add(new Form_Button(
1253
		'deleterow' . $counter,
1254
		'Delete',
1255
		null,
1256
		'fa-trash'
1257
	))->addClass('btn-warning');
1258

    
1259
	$section->add($group);
1260

    
1261
	$counter++;
1262
}
1263

    
1264
$section->addInput(new Form_Button(
1265
	'addrow',
1266
	'Add',
1267
	null,
1268
	'fa-plus'
1269
))->addClass('btn-success');
1270

    
1271
$form->add($section);
1272

    
1273
if ($act == "newpool") {
1274
	$form->addGlobal(new Form_Input(
1275
		'act',
1276
		null,
1277
		'hidden',
1278
		'newpool'
1279
	));
1280
}
1281

    
1282
if (is_numeric($pool)) {
1283
	$form->addGlobal(new Form_Input(
1284
		'pool',
1285
		null,
1286
		'hidden',
1287
		$pool
1288
	));
1289
}
1290

    
1291
$form->addGlobal(new Form_Input(
1292
	'if',
1293
	null,
1294
	'hidden',
1295
	$if
1296
));
1297

    
1298
print($form);
1299

    
1300
// DHCP Static Mappings table
1301

    
1302
if (!is_numeric($pool) && !($act == "newpool")) {
1303
?>
1304

    
1305
<div class="panel panel-default">
1306
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("DHCP Static Mappings for this Interface")?></h2></div>
1307
	<div class="table-responsive">
1308
			<table class="table table-striped table-hover table-condensed sortable-theme-bootstrap" data-sortable>
1309
				<thead>
1310
					<tr>
1311
						<th><?=gettext("Static ARP")?></th>
1312
						<th><?=gettext("MAC address")?></th>
1313
						<th><?=gettext("IP address")?></th>
1314
						<th><?=gettext("Hostname")?></th>
1315
						<th><?=gettext("Description")?></th>
1316
						<th></th>
1317
					</tr>
1318
				</thead>
1319
<?php
1320
	if (is_array($a_maps)) {
1321
		$i = 0;
1322
?>
1323
				<tbody>
1324
<?php
1325
		foreach ($a_maps as $mapent) {
1326
?>
1327
					<tr>
1328
						<td class="text-center" ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1329
							<?php if (isset($mapent['arp_table_static_entry'])): ?>
1330
								<i class="fa fa-check"></i>
1331
							<?php endif; ?>
1332
						</td>
1333
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1334
							<?=htmlspecialchars($mapent['mac'])?>
1335
						</td>
1336
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1337
							<?=htmlspecialchars($mapent['ipaddr'])?>
1338
						</td>
1339
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1340
							<?=htmlspecialchars($mapent['hostname'])?>
1341
						</td>
1342
						<td ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>';">
1343
							<?=htmlspecialchars($mapent['descr'])?>
1344
						</td>
1345
						<td>
1346
							<a class="fa fa-pencil"	title="<?=gettext('Edit static mapping')?>"	href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>&amp;id=<?=$i?>"></a>
1347
							<a class="fa fa-trash"	title="<?=gettext('Delete static mapping')?>"	href="services_dhcp.php?if=<?=htmlspecialchars($if)?>&amp;act=del&amp;id=<?=$i?>"></a>
1348
						</td>
1349
					</tr>
1350
<?php
1351
		$i++;
1352
		}
1353
?>
1354
				</tbody>
1355
<?php
1356
	}
1357
?>
1358
		</table>
1359
	</div>
1360
</div>
1361

    
1362
<nav class="action-buttons">
1363
	<a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>" class="btn btn-sm btn-success">
1364
		<i class="fa fa-plus icon-embed-btn"></i>
1365
		<?=gettext("Add")?>
1366
	</a>
1367
</nav>
1368
<?php
1369
}
1370
?>
1371

    
1372
<script type="text/javascript">
1373
//<![CDATA[
1374
events.push(function() {
1375

    
1376
	// Show advanced DNS options ======================================================================================
1377
	var showadvdns = false;
1378

    
1379
	function show_advdns(ispageload) {
1380
		var text;
1381
		// On page load decide the initial state based on the data.
1382
		if (ispageload) {
1383
<?php
1384
			if (!$pconfig['ddnsupdate'] && empty($pconfig['ddnsdomain']) && empty($pconfig['ddnsdomainprimary']) &&
1385
			    empty($pconfig['ddnsdomainkeyname']) && empty($pconfig['ddnsdomainkey'])) {
1386
				$showadv = false;
1387
			} else {
1388
				$showadv = true;
1389
			}
1390
?>
1391
			showadvdns = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1392
		} else {
1393
			// It was a click, swap the state.
1394
			showadvdns = !showadvdns;
1395
		}
1396

    
1397
		hideCheckbox('ddnsupdate', !showadvdns);
1398
		hideInput('ddnsdomain', !showadvdns);
1399
		hideInput('ddnsdomainprimary', !showadvdns);
1400
		hideInput('ddnsdomainkeyname', !showadvdns);
1401
		hideInput('ddnsdomainkey', !showadvdns);
1402

    
1403
		if (showadvdns) {
1404
			text = "<?=gettext('Hide Advanced');?>";
1405
		} else {
1406
			text = "<?=gettext('Display Advanced');?>";
1407
		}
1408
		$('#btnadvdns').html('<i class="fa fa-cog"></i> ' + text);
1409
	}
1410

    
1411
	$('#btnadvdns').click(function(event) {
1412
		show_advdns();
1413
	});
1414

    
1415
	// Show advanced MAC options ======================================================================================
1416
	var showadvmac = false;
1417

    
1418
	function show_advmac(ispageload) {
1419
		var text;
1420
		// On page load decide the initial state based on the data.
1421
		if (ispageload) {
1422
<?php
1423
			if (empty($pconfig['mac_allow']) && empty($pconfig['mac_deny'])) {
1424
				$showadv = false;
1425
			} else {
1426
				$showadv = true;
1427
			}
1428
?>
1429
			showadvmac = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1430
		} else {
1431
			// It was a click, swap the state.
1432
			showadvmac = !showadvmac;
1433
		}
1434

    
1435
		hideInput('mac_allow', !showadvmac);
1436
		hideInput('mac_deny', !showadvmac);
1437

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

    
1446
	$('#btnadvmac').click(function(event) {
1447
		show_advmac();
1448
	});
1449

    
1450
	// Show advanced NTP options ======================================================================================
1451
	var showadvntp = false;
1452

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

    
1470
		hideInput('ntp1', !showadvntp);
1471
		hideInput('ntp2', !showadvntp);
1472

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

    
1481
	$('#btnadvntp').click(function(event) {
1482
		show_advntp();
1483
	});
1484

    
1485
	// Show advanced TFTP options ======================================================================================
1486
	var showadvtftp = false;
1487

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

    
1505
		hideInput('tftp', !showadvtftp);
1506

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

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

    
1519
	// Show advanced LDAP options ======================================================================================
1520
	var showadvldap = false;
1521

    
1522
	function show_advldap(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['ldap'])) {
1528
				$showadv = false;
1529
			} else {
1530
				$showadv = true;
1531
			}
1532
?>
1533
			showadvldap = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1534
		} else {
1535
			// It was a click, swap the state.
1536
			showadvldap = !showadvldap;
1537
		}
1538

    
1539
		hideInput('ldap', !showadvldap);
1540

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

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

    
1553
	// Show advanced additional opts options ===========================================================================
1554
	var showadvopts = false;
1555

    
1556
	function show_advopts(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['numberoptions']) ||
1562
			    (empty($pconfig['numberoptions']['item'][0]['number']) && (empty($pconfig['numberoptions']['item'][0]['value'])))) {
1563
				$showadv = false;
1564
			} else {
1565
				$showadv = true;
1566
			}
1567
?>
1568
			showadvopts = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1569
		} else {
1570
			// It was a click, swap the state.
1571
			showadvopts = !showadvopts;
1572
		}
1573

    
1574
		hideClass('adnlopts', !showadvopts);
1575

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

    
1584
	$('#btnadvopts').click(function(event) {
1585
		show_advopts();
1586
	});
1587

    
1588
	// Show advanced Network Booting options ===========================================================================
1589
	var showadvnwkboot = false;
1590

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

    
1608
		hideCheckbox('netboot', !showadvnwkboot);
1609
		hideInput('nextserver', !showadvnwkboot);
1610
		hideInput('filename', !showadvnwkboot);
1611
		hideInput('filename32', !showadvnwkboot);
1612
		hideInput('filename64', !showadvnwkboot);
1613
		hideInput('rootpath', !showadvnwkboot);
1614

    
1615
		if (showadvnwkboot) {
1616
			text = "<?=gettext('Hide Advanced');?>";
1617
		} else {
1618
			text = "<?=gettext('Display Advanced');?>";
1619
		}
1620
		$('#btnadvnwkboot').html('<i class="fa fa-cog"></i> ' + text);
1621
	}
1622

    
1623
	$('#btnadvnwkboot').click(function(event) {
1624
		show_advnwkboot();
1625
	});
1626

    
1627
	// ---------- On initial page load ------------------------------------------------------------
1628

    
1629
	show_advdns(true);
1630
	show_advmac(true);
1631
	show_advntp(true);
1632
	show_advtftp(true);
1633
	show_advldap(true);
1634
	show_advopts(true);
1635
	show_advnwkboot(true);
1636

    
1637
	// Suppress "Delete row" button if there are fewer than two rows
1638
	checkLastRow();
1639
});
1640
//]]>
1641
</script>
1642

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