Project

General

Profile

Download (50 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 Electric Sheep Fencing, LLC
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['denyunknown'] = isset($dhcpdconf['denyunknown']);
171
	$pconfig['nonak'] = isset($dhcpdconf['nonak']);
172
	$pconfig['ddnsdomain'] = $dhcpdconf['ddnsdomain'];
173
	$pconfig['ddnsdomainprimary'] = $dhcpdconf['ddnsdomainprimary'];
174
	$pconfig['ddnsdomainkeyname'] = $dhcpdconf['ddnsdomainkeyname'];
175
	$pconfig['ddnsdomainkey'] = $dhcpdconf['ddnsdomainkey'];
176
	$pconfig['ddnsupdate'] = isset($dhcpdconf['ddnsupdate']);
177
	$pconfig['mac_allow'] = $dhcpdconf['mac_allow'];
178
	$pconfig['mac_deny'] = $dhcpdconf['mac_deny'];
179
	list($pconfig['ntp1'], $pconfig['ntp2']) = $dhcpdconf['ntpserver'];
180
	$pconfig['tftp'] = $dhcpdconf['tftp'];
181
	$pconfig['ldap'] = $dhcpdconf['ldap'];
182
	$pconfig['netboot'] = isset($dhcpdconf['netboot']);
183
	$pconfig['nextserver'] = $dhcpdconf['nextserver'];
184
	$pconfig['filename'] = $dhcpdconf['filename'];
185
	$pconfig['filename32'] = $dhcpdconf['filename32'];
186
	$pconfig['filename64'] = $dhcpdconf['filename64'];
187
	$pconfig['rootpath'] = $dhcpdconf['rootpath'];
188
	$pconfig['netmask'] = $dhcpdconf['netmask'];
189
	$pconfig['numberoptions'] = $dhcpdconf['numberoptions'];
190
	$pconfig['statsgraph'] = $dhcpdconf['statsgraph'];
191
}
192

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

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

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

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

    
209
	return true;
210
}
211

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

    
214
	unset($input_errors);
215

    
216
	$pconfig = $_POST;
217

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
475
		$dhcpd_enable_changed = false;
476

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

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

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

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

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

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

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

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

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

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

    
572
		$dhcpdconf['numberoptions'] = $numberoptions;
573

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

    
582
		write_config();
583
	}
584
}
585

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

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

    
619
	$savemsg = get_std_save_message($retval);
620
}
621

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

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

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

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

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

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

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

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

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

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

    
686
	$pooltbl .=			'</tbody>';
687
	$pooltbl .=		'</table>';
688
	$pooltbl .= '</div>';
689

    
690
	return($pooltbl);
691
}
692

    
693
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
694

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

    
700
include("head.inc");
701

    
702
if ($input_errors) {
703
	print_input_errors($input_errors);
704
}
705

    
706
if ($savemsg) {
707
	print_info_box($savemsg, 'success');
708
}
709

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

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

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

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

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

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

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

    
748
display_top_tabs($tab_array);
749

    
750
$form = new Form();
751

    
752
$section = new Form_Section('General Options');
753

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

    
765
$section->addInput(new Form_Checkbox(
766
	'denyunknown',
767
	'Deny unknown clients',
768
	'Only the clients defined below will get DHCP leases from this server.',
769
	$pconfig['denyunknown']
770
));
771

    
772
$section->addInput(new Form_Checkbox(
773
	'nonak',
774
	'Ignore denied clients',
775
	'Denied clients will be ignored rather than rejected.',
776
	$pconfig['nonak']
777
))->setHelp("This option is not compatible with failover and cannot be enabled when a Failover Peer IP address is configured.");
778

    
779

    
780
if (is_numeric($pool) || ($act == "newpool")) {
781
	$section->addInput(new Form_Input(
782
		'descr',
783
		'Pool Description',
784
		'text',
785
		$pconfig['descr']
786
	));
787
}
788

    
789
$section->addInput(new Form_StaticText(
790
	'Subnet',
791
	gen_subnet($ifcfgip, $ifcfgsn)
792
));
793

    
794
$section->addInput(new Form_StaticText(
795
	'Subnet mask',
796
	gen_subnet_mask($ifcfgsn)
797
));
798

    
799
// Compose a string to display the required address ranges
800
$rangestr = ip_after($subnet_start) . ' - ' . ip_before($subnet_end);
801

    
802
if (is_numeric($pool) || ($act == "newpool")) {
803
	$rangestr .= '<br />' . gettext('In-use DHCP Pool Ranges:');
804
	if (is_array($config['dhcpd'][$if]['range'])) {
805
		$rangestr .= '<br />' . $config['dhcpd'][$if]['range']['from'] . ' - ' . $config['dhcpd'][$if]['range']['to'];
806
	}
807

    
808
	foreach ($a_pools as $p) {
809
		if (is_array($p['range'])) {
810
			$rangestr .= '<br />' . $p['range']['from'] . ' - ' . $p['range']['to'];
811
		}
812
	}
813
}
814

    
815
$section->addInput(new Form_StaticText(
816
	'Available range',
817
	$rangestr
818
));
819

    
820
if ($is_olsr_enabled) {
821
	$section->addInput(new Form_Select(
822
		'netmask',
823
		'Subnet mask',
824
		$pconfig['netmask'],
825
		array_combine(range(32, 1, -1), range(32, 1, -1))
826
	));
827
}
828

    
829
$group = new Form_Group('Range');
830

    
831
$group->add(new Form_IpAddress(
832
	'range_from',
833
	null,
834
	$pconfig['range_from']
835
))->setHelp('From');
836

    
837
$group->add(new Form_IpAddress(
838
	'range_to',
839
	null,
840
	$pconfig['range_to']
841
))->setHelp('To');
842

    
843
$section->add($group);
844

    
845
$form->add($section);
846

    
847
if (!is_numeric($pool) && !($act == "newpool")) {
848
	$section = new Form_Section('Additional Pools');
849

    
850
	$btnaddpool = new Form_Button(
851
		'btnaddpool',
852
		'Add pool',
853
		'services_dhcp.php?if=' . htmlspecialchars($if) . '&act=newpool',
854
		'fa-plus'
855
	);
856
	$btnaddpool->addClass('btn-success');
857

    
858
	$section->addInput(new Form_StaticText(
859
		'Add',
860
		$btnaddpool
861
	))->setHelp('If additional pools of addresses are needed inside of this subnet outside the above Range, they may be specified here.');
862

    
863
	if (is_array($a_pools)) {
864
		$section->addInput(new Form_StaticText(
865
			null,
866
			build_pooltable()
867
		));
868
	}
869

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

    
873
$section = new Form_Section('Servers');
874

    
875
$section->addInput(new Form_IpAddress(
876
	'wins1',
877
	'WINS servers',
878
	$pconfig['wins1']
879
))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'WINS Server 1');
880

    
881
$section->addInput(new Form_IpAddress(
882
	'wins2',
883
	null,
884
	$pconfig['wins2']
885
))->setPattern('[.a-zA-Z0-9_]+')->setAttribute('placeholder', 'WINS Server 2');
886

    
887
for ($idx=1; $idx<=4; $idx++) {
888
	$section->addInput(new Form_IpAddress(
889
		'dns' . $idx,
890
		($idx == 1) ? 'DNS servers':null,
891
		$pconfig['dns' . $idx]
892
	))->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.':'');
893
}
894

    
895
$form->add($section);
896

    
897
$section = new Form_Section('Other Options');
898

    
899
$section->addInput(new Form_IpAddress(
900
	'gateway',
901
	'Gateway',
902
	$pconfig['gateway']
903
))->setPattern('[.a-zA-Z0-9_]+')
904
  ->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.');
905

    
906
$section->addInput(new Form_Input(
907
	'domain',
908
	'Domain name',
909
	'text',
910
	$pconfig['domain']
911
))->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.');
912

    
913
$section->addInput(new Form_Input(
914
	'domainsearchlist',
915
	'Domain search list',
916
	'text',
917
	$pconfig['domainsearchlist']
918
))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator.');
919

    
920
$section->addInput(new Form_Input(
921
	'deftime',
922
	'Default lease time',
923
	'number',
924
	$pconfig['deftime']
925
))->setHelp('This is used for clients that do not ask for a specific expiration time. The default is 7200 seconds.');
926

    
927
$section->addInput(new Form_Input(
928
	'maxtime',
929
	'Maximum lease time',
930
	'number',
931
	$pconfig['maxtime']
932
))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds.');
933

    
934
if (!is_numeric($pool) && !($act == "newpool")) {
935
	$section->addInput(new Form_IpAddress(
936
		'failover_peerip',
937
		'Failover peer IP',
938
		$pconfig['failover_peerip']
939
	))->setHelp('Leave blank to disable. Enter the interface IP address of the other machine. Machines must be using CARP. ' .
940
				'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).');
941
}
942

    
943
if (!is_numeric($pool) && !($act == "newpool")) {
944
	$section->addInput(new Form_Checkbox(
945
		'staticarp',
946
		'Static ARP',
947
		'Enable Static ARP entries',
948
		$pconfig['staticarp']
949
	))->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.');
950

    
951
	$section->addInput(new Form_Checkbox(
952
		'dhcpleaseinlocaltime',
953
		'Time format change',
954
		'Change DHCP display lease time from UTC to local time',
955
		$pconfig['dhcpleaseinlocaltime']
956
	))->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.' .
957
				' This will be used for all DHCP interfaces lease time.');
958
	$section->addInput(new Form_Checkbox(
959
		'statsgraph',
960
		'Statistics graphs',
961
		'Enable RRD statistics graphs',
962
		$pconfig['statsgraph']
963
	))->setHelp('Enable this to add DHCP leases statistics to the RRD graphs. Disabled by default.');
964
}
965

    
966
// DDNS
967
$btnadv = new Form_Button(
968
	'btnadvdns',
969
	'Display Advanced',
970
	null,
971
	'fa-cog'
972
);
973

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

    
976
$section->addInput(new Form_StaticText(
977
	'Dynamic DNS',
978
	$btnadv
979
));
980

    
981
$section->addInput(new Form_Checkbox(
982
	'ddnsupdate',
983
	null,
984
	'Enable registration of DHCP client names in DNS',
985
	$pconfig['ddnsupdate']
986
));
987

    
988
$section->addInput(new Form_Input(
989
	'ddnsdomain',
990
	'DDNS Domain',
991
	'text',
992
	$pconfig['ddnsdomain']
993
))->setHelp('Leave blank to disable dynamic DNS registration.' . '<br />' .
994
			'Enter the dynamic DNS domain which will be used to register client names in the DNS server.');
995

    
996
$section->addInput(new Form_IpAddress(
997
	'ddnsdomainprimary',
998
	'Primary DDNS address',
999
	$pconfig['ddnsdomainprimary']
1000
))->setHelp('Primary domain name server IP address for the dynamic domain name.');
1001

    
1002
$section->addInput(new Form_Input(
1003
	'ddnsdomainkeyname',
1004
	'DNS Domain key',
1005
	'text',
1006
	$pconfig['ddnsdomainkeyname']
1007
))->setHelp('Dynamic DNS domain key name which will be used to register client names in the DNS server.');
1008

    
1009
$section->addInput(new Form_Input(
1010
	'ddnsdomainkey',
1011
	'DNS Domain key secret',
1012
	'text',
1013
	$pconfig['ddnsdomainkey']
1014
))->setHelp('Dynamic DNS domain key secret (HMAC-MD5) which will be used to register client names in the DNS server.');
1015

    
1016
// Advanced MAC
1017
$btnadv = new Form_Button(
1018
	'btnadvmac',
1019
	'Display Advanced',
1020
	null,
1021
	'fa-cog'
1022
);
1023

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

    
1026
$section->addInput(new Form_StaticText(
1027
	'MAC address control',
1028
	$btnadv
1029
));
1030

    
1031
$section->addInput(new Form_Input(
1032
	'mac_allow',
1033
	'MAC Allow',
1034
	'text',
1035
	$pconfig['mac_allow']
1036
))->setHelp('List of partial MAC addresses to allow, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1037

    
1038
$section->addInput(new Form_Input(
1039
	'mac_deny',
1040
	'MAC Deny',
1041
	'text',
1042
	$pconfig['mac_deny']
1043
))->setHelp('List of partial MAC addresses to deny access, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1044

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

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

    
1055
$section->addInput(new Form_StaticText(
1056
	'NTP',
1057
	$btnadv
1058
));
1059

    
1060
$section->addInput(new Form_IpAddress(
1061
	'ntp1',
1062
	'NTP Server 1',
1063
	$pconfig['ntp1']
1064
))->setPattern('[.a-zA-Z0-9_]+');
1065

    
1066
$section->addInput(new Form_IpAddress(
1067
	'ntp2',
1068
	'NTP Server 2',
1069
	$pconfig['ntp2']
1070
))->setPattern('[.a-zA-Z0-9_]+');
1071

    
1072
// Advanced TFTP
1073
$btnadv = new Form_Button(
1074
	'btnadvtftp',
1075
	'Display Advanced',
1076
	null,
1077
	'fa-cog'
1078
);
1079

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

    
1082
$section->addInput(new Form_StaticText(
1083
	'TFTP',
1084
	$btnadv
1085
));
1086

    
1087
$section->addInput(new Form_IpAddress(
1088
	'tftp',
1089
	'TFTP Server',
1090
	$pconfig['tftp']
1091
))->setHelp('Leave blank to disable.  Enter a full hostname or IP for the TFTP server.')->setPattern('[.a-zA-Z0-9_-]+');
1092

    
1093
// Advanced LDAP
1094
$btnadv = new Form_Button(
1095
	'btnadvldap',
1096
	'Display Advanced',
1097
	null,
1098
	'fa-cog'
1099
);
1100

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

    
1103
$section->addInput(new Form_StaticText(
1104
	'LDAP',
1105
	$btnadv
1106
));
1107

    
1108
$section->addInput(new Form_Input(
1109
	'ldap',
1110
	'LDAP Server URI',
1111
	'text',
1112
	$pconfig['ldap']
1113
))->setHelp('Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com ');
1114

    
1115
// Advanced Network Booting options
1116
$btnadv = new Form_Button(
1117
	'btnadvnwkboot',
1118
	'Display Advanced',
1119
	null,
1120
	'fa-cog'
1121
);
1122

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

    
1125
$section->addInput(new Form_StaticText(
1126
	'Network Booting',
1127
	$btnadv
1128
));
1129

    
1130
$section->addInput(new Form_Checkbox(
1131
	'netboot',
1132
	'Enable',
1133
	'Enables network booting',
1134
	$pconfig['netboot']
1135
));
1136

    
1137
$section->addInput(new Form_IpAddress(
1138
	'nextserver',
1139
	'Next Server',
1140
	$pconfig['nextserver']
1141
))->setHelp('Enter the IP address of the next server');
1142

    
1143
$section->addInput(new Form_Input(
1144
	'filename',
1145
	'Default BIOS file name',
1146
	'text',
1147
	$pconfig['filename']
1148
));
1149

    
1150
$section->addInput(new Form_Input(
1151
	'filename32',
1152
	'UEFI 32 bit file name',
1153
	'text',
1154
	$pconfig['filename32']
1155
));
1156

    
1157
$section->addInput(new Form_Input(
1158
	'filename64',
1159
	'UEFI 64 bit file name',
1160
	'text',
1161
	$pconfig['filename64']
1162
))->setHelp('Both a filename and a boot server must be configured for this to work! ' .
1163
			'All three filenames and a configured boot server are necessary for UEFI to work! ');
1164

    
1165
$section->addInput(new Form_Input(
1166
	'rootpath',
1167
	'Root path',
1168
	'text',
1169
	$pconfig['rootpath']
1170
))->setHelp('string-format: iscsi:(servername):(protocol):(port):(LUN):targetname ');
1171

    
1172
// Advanced Additional options
1173
$btnadv = new Form_Button(
1174
	'btnadvopts',
1175
	'Display Advanced',
1176
	null,
1177
	'fa-cog'
1178
);
1179

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

    
1182
$section->addInput(new Form_StaticText(
1183
	'Additional BOOTP/DHCP Options',
1184
	$btnadv
1185
));
1186

    
1187
$form->add($section);
1188

    
1189
$section = new Form_Section('Additional BOOTP/DHCP Options');
1190
$section->addClass('adnlopts');
1191

    
1192
$section->addInput(new Form_StaticText(
1193
	null,
1194
	'<div class="alert alert-info"> ' . gettext('Enter the DHCP option number and the value for each item to include in the DHCP lease information.') . ' ' .
1195
	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>')
1196
));
1197

    
1198
if (!$pconfig['numberoptions']) {
1199
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1200
}
1201

    
1202
$customitemtypes = array(
1203
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1204
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1205
	'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')
1206
);
1207

    
1208
$numrows = count($item) -1;
1209
$counter = 0;
1210

    
1211
$numrows = count($pconfig['numberoptions']['item']) -1;
1212

    
1213
foreach ($pconfig['numberoptions']['item'] as $item) {
1214
	$number = $item['number'];
1215
	$itemtype = $item['type'];
1216
	$value = base64_decode($item['value']);
1217

    
1218
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1219
	$group->addClass('repeatable');
1220

    
1221
	$group->add(new Form_Input(
1222
		'number' . $counter,
1223
		null,
1224
		'text',
1225
		$number
1226
	))->setHelp($numrows == $counter ? 'Number':null);
1227

    
1228

    
1229
	$group->add(new Form_Select(
1230
		'itemtype' . $counter,
1231
		null,
1232
		$itemtype,
1233
		$customitemtypes
1234
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1235

    
1236
	$group->add(new Form_Input(
1237
		'value' . $counter,
1238
		null,
1239
		'text',
1240
		$value
1241
	))->setHelp($numrows == $counter ? 'Value':null);
1242

    
1243
	$group->add(new Form_Button(
1244
		'deleterow' . $counter,
1245
		'Delete',
1246
		null,
1247
		'fa-trash'
1248
	))->addClass('btn-warning');
1249

    
1250
	$section->add($group);
1251

    
1252
	$counter++;
1253
}
1254

    
1255
$section->addInput(new Form_Button(
1256
	'addrow',
1257
	'Add',
1258
	null,
1259
	'fa-plus'
1260
))->addClass('btn-success');
1261

    
1262
$form->add($section);
1263

    
1264
if ($act == "newpool") {
1265
	$form->addGlobal(new Form_Input(
1266
		'act',
1267
		null,
1268
		'hidden',
1269
		'newpool'
1270
	));
1271
}
1272

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

    
1282
$form->addGlobal(new Form_Input(
1283
	'if',
1284
	null,
1285
	'hidden',
1286
	$if
1287
));
1288

    
1289
print($form);
1290

    
1291
// DHCP Static Mappings table
1292

    
1293
if (!is_numeric($pool) && !($act == "newpool")) {
1294
?>
1295

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

    
1353
<nav class="action-buttons">
1354
	<a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>" class="btn btn-sm btn-success">
1355
		<i class="fa fa-plus icon-embed-btn"></i>
1356
		<?=gettext("Add")?>
1357
	</a>
1358
</nav>
1359
<?php
1360
}
1361
?>
1362

    
1363
<script type="text/javascript">
1364
//<![CDATA[
1365
events.push(function() {
1366

    
1367
	// Show advanced DNS options ======================================================================================
1368
	var showadvdns = false;
1369

    
1370
	function show_advdns(ispageload) {
1371
		var text;
1372
		// On page load decide the initial state based on the data.
1373
		if (ispageload) {
1374
<?php
1375
			if (!$pconfig['ddnsupdate'] && empty($pconfig['ddnsdomain']) && empty($pconfig['ddnsdomainprimary']) &&
1376
			    empty($pconfig['ddnsdomainkeyname']) && empty($pconfig['ddnsdomainkey'])) {
1377
				$showadv = false;
1378
			} else {
1379
				$showadv = true;
1380
			}
1381
?>
1382
			showadvdns = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1383
		} else {
1384
			// It was a click, swap the state.
1385
			showadvdns = !showadvdns;
1386
		}
1387

    
1388
		hideCheckbox('ddnsupdate', !showadvdns);
1389
		hideInput('ddnsdomain', !showadvdns);
1390
		hideInput('ddnsdomainprimary', !showadvdns);
1391
		hideInput('ddnsdomainkeyname', !showadvdns);
1392
		hideInput('ddnsdomainkey', !showadvdns);
1393

    
1394
		if (showadvdns) {
1395
			text = "<?=gettext('Hide Advanced');?>";
1396
		} else {
1397
			text = "<?=gettext('Display Advanced');?>";
1398
		}
1399
		$('#btnadvdns').html('<i class="fa fa-cog"></i> ' + text);
1400
	}
1401

    
1402
	$('#btnadvdns').click(function(event) {
1403
		show_advdns();
1404
	});
1405

    
1406
	// Show advanced MAC options ======================================================================================
1407
	var showadvmac = false;
1408

    
1409
	function show_advmac(ispageload) {
1410
		var text;
1411
		// On page load decide the initial state based on the data.
1412
		if (ispageload) {
1413
<?php
1414
			if (empty($pconfig['mac_allow']) && empty($pconfig['mac_deny'])) {
1415
				$showadv = false;
1416
			} else {
1417
				$showadv = true;
1418
			}
1419
?>
1420
			showadvmac = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1421
		} else {
1422
			// It was a click, swap the state.
1423
			showadvmac = !showadvmac;
1424
		}
1425

    
1426
		hideInput('mac_allow', !showadvmac);
1427
		hideInput('mac_deny', !showadvmac);
1428

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

    
1437
	$('#btnadvmac').click(function(event) {
1438
		show_advmac();
1439
	});
1440

    
1441
	// Show advanced NTP options ======================================================================================
1442
	var showadvntp = false;
1443

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

    
1461
		hideInput('ntp1', !showadvntp);
1462
		hideInput('ntp2', !showadvntp);
1463

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

    
1472
	$('#btnadvntp').click(function(event) {
1473
		show_advntp();
1474
	});
1475

    
1476
	// Show advanced TFTP options ======================================================================================
1477
	var showadvtftp = false;
1478

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

    
1496
		hideInput('tftp', !showadvtftp);
1497

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

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

    
1510
	// Show advanced LDAP options ======================================================================================
1511
	var showadvldap = false;
1512

    
1513
	function show_advldap(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['ldap'])) {
1519
				$showadv = false;
1520
			} else {
1521
				$showadv = true;
1522
			}
1523
?>
1524
			showadvldap = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1525
		} else {
1526
			// It was a click, swap the state.
1527
			showadvldap = !showadvldap;
1528
		}
1529

    
1530
		hideInput('ldap', !showadvldap);
1531

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

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

    
1544
	// Show advanced additional opts options ===========================================================================
1545
	var showadvopts = false;
1546

    
1547
	function show_advopts(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['numberoptions']) ||
1553
			    (empty($pconfig['numberoptions']['item'][0]['number']) && (empty($pconfig['numberoptions']['item'][0]['value'])))) {
1554
				$showadv = false;
1555
			} else {
1556
				$showadv = true;
1557
			}
1558
?>
1559
			showadvopts = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1560
		} else {
1561
			// It was a click, swap the state.
1562
			showadvopts = !showadvopts;
1563
		}
1564

    
1565
		hideClass('adnlopts', !showadvopts);
1566

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

    
1575
	$('#btnadvopts').click(function(event) {
1576
		show_advopts();
1577
	});
1578

    
1579
	// Show advanced Network Booting options ===========================================================================
1580
	var showadvnwkboot = false;
1581

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

    
1599
		hideCheckbox('netboot', !showadvnwkboot);
1600
		hideInput('nextserver', !showadvnwkboot);
1601
		hideInput('filename', !showadvnwkboot);
1602
		hideInput('filename32', !showadvnwkboot);
1603
		hideInput('filename64', !showadvnwkboot);
1604
		hideInput('rootpath', !showadvnwkboot);
1605

    
1606
		if (showadvnwkboot) {
1607
			text = "<?=gettext('Hide Advanced');?>";
1608
		} else {
1609
			text = "<?=gettext('Display Advanced');?>";
1610
		}
1611
		$('#btnadvnwkboot').html('<i class="fa fa-cog"></i> ' + text);
1612
	}
1613

    
1614
	$('#btnadvnwkboot').click(function(event) {
1615
		show_advnwkboot();
1616
	});
1617

    
1618
	// ---------- On initial page load ------------------------------------------------------------
1619

    
1620
	show_advdns(true);
1621
	show_advmac(true);
1622
	show_advntp(true);
1623
	show_advtftp(true);
1624
	show_advldap(true);
1625
	show_advopts(true);
1626
	show_advnwkboot(true);
1627

    
1628
	// Suppress "Delete row" button if there are fewer than two rows
1629
	checkLastRow();
1630
});
1631
//]]>
1632
</script>
1633

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