Project

General

Profile

Download (50.5 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
		/* Remove static ARP entry, if necessary */
636
		if (isset($a_maps[$_GET['id']]['arp_table_static_entry'])) {
637
			mwexec("/usr/sbin/arp -d " . escapeshellarg($a_maps[$_GET['id']]['ipaddr']));
638
		}
639
		unset($a_maps[$_GET['id']]);
640
		write_config();
641
		if (isset($config['dhcpd'][$if]['enable'])) {
642
			mark_subsystem_dirty('staticmaps');
643
			if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic'])) {
644
				mark_subsystem_dirty('hosts');
645
			}
646
		}
647

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

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

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

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

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

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

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

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

    
692
	$pooltbl .=			'</tbody>';
693
	$pooltbl .=		'</table>';
694
	$pooltbl .= '</div>';
695

    
696
	return($pooltbl);
697
}
698

    
699
$pgtitle = array(gettext("Services"), gettext("DHCP Server"));
700

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

    
706
include("head.inc");
707

    
708
if ($input_errors) {
709
	print_input_errors($input_errors);
710
}
711

    
712
if ($savemsg) {
713
	print_info_box($savemsg, 'success');
714
}
715

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

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

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

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

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

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

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

    
754
display_top_tabs($tab_array);
755

    
756
$form = new Form();
757

    
758
$section = new Form_Section('General Options');
759

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

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

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

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

    
792

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

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

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

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

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

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

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

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

    
842
$group = new Form_Group('Range');
843

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

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

    
858
$section->add($group);
859

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

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

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

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

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

    
885
	$form->add($section);
886
}
887

    
888
$section = new Form_Section('Servers');
889

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

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

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

    
913
$form->add($section);
914

    
915
$section = new Form_Section('Other Options');
916

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1016
$section->addInput(new Form_IpAddress(
1017
	'ddnsdomainprimary',
1018
	'Primary DDNS address',
1019
	$pconfig['ddnsdomainprimary'],
1020
	'V4'
1021
))->setHelp('Primary domain name server IP address for the dynamic domain name.');
1022

    
1023
$section->addInput(new Form_Input(
1024
	'ddnsdomainkeyname',
1025
	'DNS Domain key',
1026
	'text',
1027
	$pconfig['ddnsdomainkeyname']
1028
))->setHelp('Dynamic DNS domain key name which will be used to register client names in the DNS server.');
1029

    
1030
$section->addInput(new Form_Input(
1031
	'ddnsdomainkey',
1032
	'DNS Domain key secret',
1033
	'text',
1034
	$pconfig['ddnsdomainkey']
1035
))->setHelp('Dynamic DNS domain key secret (HMAC-MD5) which will be used to register client names in the DNS server.');
1036

    
1037
// Advanced MAC
1038
$btnadv = new Form_Button(
1039
	'btnadvmac',
1040
	'Display Advanced',
1041
	null,
1042
	'fa-cog'
1043
);
1044

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

    
1047
$section->addInput(new Form_StaticText(
1048
	'MAC address control',
1049
	$btnadv
1050
));
1051

    
1052
$section->addInput(new Form_Input(
1053
	'mac_allow',
1054
	'MAC Allow',
1055
	'text',
1056
	$pconfig['mac_allow']
1057
))->setHelp('List of partial MAC addresses to allow, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1058

    
1059
$section->addInput(new Form_Input(
1060
	'mac_deny',
1061
	'MAC Deny',
1062
	'text',
1063
	$pconfig['mac_deny']
1064
))->setHelp('List of partial MAC addresses to deny access, comma separated, no spaces, e.g.: 00:00:00,01:E5:FF');
1065

    
1066
// Advanced NTP
1067
$btnadv = new Form_Button(
1068
	'btnadvntp',
1069
	'Display Advanced',
1070
	null,
1071
	'fa-cog'
1072
);
1073

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

    
1076
$section->addInput(new Form_StaticText(
1077
	'NTP',
1078
	$btnadv
1079
));
1080

    
1081
$section->addInput(new Form_IpAddress(
1082
	'ntp1',
1083
	'NTP Server 1',
1084
	$pconfig['ntp1'],
1085
	'V4'
1086
))->setPattern('[.a-zA-Z0-9-]+');
1087

    
1088
$section->addInput(new Form_IpAddress(
1089
	'ntp2',
1090
	'NTP Server 2',
1091
	$pconfig['ntp2'],
1092
	'V4'
1093
))->setPattern('[.a-zA-Z0-9-]+');
1094

    
1095
// Advanced TFTP
1096
$btnadv = new Form_Button(
1097
	'btnadvtftp',
1098
	'Display Advanced',
1099
	null,
1100
	'fa-cog'
1101
);
1102

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

    
1105
$section->addInput(new Form_StaticText(
1106
	'TFTP',
1107
	$btnadv
1108
));
1109

    
1110
$section->addInput(new Form_Input(
1111
	'tftp',
1112
	'TFTP Server',
1113
	$pconfig['tftp']
1114
))->setHelp('Leave blank to disable. Enter a valid IP address, hostname or URL for the TFTP server.');
1115

    
1116
// Advanced LDAP
1117
$btnadv = new Form_Button(
1118
	'btnadvldap',
1119
	'Display Advanced',
1120
	null,
1121
	'fa-cog'
1122
);
1123

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

    
1126
$section->addInput(new Form_StaticText(
1127
	'LDAP',
1128
	$btnadv
1129
));
1130

    
1131
$section->addInput(new Form_Input(
1132
	'ldap',
1133
	'LDAP Server URI',
1134
	'text',
1135
	$pconfig['ldap']
1136
))->setHelp('Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com ');
1137

    
1138
// Advanced Network Booting options
1139
$btnadv = new Form_Button(
1140
	'btnadvnwkboot',
1141
	'Display Advanced',
1142
	null,
1143
	'fa-cog'
1144
);
1145

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

    
1148
$section->addInput(new Form_StaticText(
1149
	'Network Booting',
1150
	$btnadv
1151
));
1152

    
1153
$section->addInput(new Form_Checkbox(
1154
	'netboot',
1155
	'Enable',
1156
	'Enables network booting',
1157
	$pconfig['netboot']
1158
));
1159

    
1160
$section->addInput(new Form_IpAddress(
1161
	'nextserver',
1162
	'Next Server',
1163
	$pconfig['nextserver'],
1164
	'V4'
1165
))->setHelp('Enter the IP address of the next server');
1166

    
1167
$section->addInput(new Form_Input(
1168
	'filename',
1169
	'Default BIOS file name',
1170
	'text',
1171
	$pconfig['filename']
1172
));
1173

    
1174
$section->addInput(new Form_Input(
1175
	'filename32',
1176
	'UEFI 32 bit file name',
1177
	'text',
1178
	$pconfig['filename32']
1179
));
1180

    
1181
$section->addInput(new Form_Input(
1182
	'filename64',
1183
	'UEFI 64 bit file name',
1184
	'text',
1185
	$pconfig['filename64']
1186
))->setHelp('Both a filename and a boot server must be configured for this to work! ' .
1187
			'All three filenames and a configured boot server are necessary for UEFI to work! ');
1188

    
1189
$section->addInput(new Form_Input(
1190
	'rootpath',
1191
	'Root path',
1192
	'text',
1193
	$pconfig['rootpath']
1194
))->setHelp('string-format: iscsi:(servername):(protocol):(port):(LUN):targetname ');
1195

    
1196
// Advanced Additional options
1197
$btnadv = new Form_Button(
1198
	'btnadvopts',
1199
	'Display Advanced',
1200
	null,
1201
	'fa-cog'
1202
);
1203

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

    
1206
$section->addInput(new Form_StaticText(
1207
	'Additional BOOTP/DHCP Options',
1208
	$btnadv
1209
));
1210

    
1211
$form->add($section);
1212

    
1213
$section = new Form_Section('Additional BOOTP/DHCP Options');
1214
$section->addClass('adnlopts');
1215

    
1216
$section->addInput(new Form_StaticText(
1217
	null,
1218
	'<div class="alert alert-info"> ' . gettext('Enter the DHCP option number and the value for each item to include in the DHCP lease information.') . ' ' .
1219
	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>')
1220
));
1221

    
1222
if (!$pconfig['numberoptions']) {
1223
	$pconfig['numberoptions']['item']  = array(array('number' => '', 'type' => 'text', 'value' => ''));
1224
}
1225

    
1226
$customitemtypes = array(
1227
	'text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'),
1228
	'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'),
1229
	'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')
1230
);
1231

    
1232
$numrows = count($item) -1;
1233
$counter = 0;
1234

    
1235
$numrows = count($pconfig['numberoptions']['item']) -1;
1236

    
1237
foreach ($pconfig['numberoptions']['item'] as $item) {
1238
	$number = $item['number'];
1239
	$itemtype = $item['type'];
1240
	$value = base64_decode($item['value']);
1241

    
1242
	$group = new Form_Group(($counter == 0) ? 'Option':null);
1243
	$group->addClass('repeatable');
1244

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

    
1252

    
1253
	$group->add(new Form_Select(
1254
		'itemtype' . $counter,
1255
		null,
1256
		$itemtype,
1257
		$customitemtypes
1258
	))->setWidth(3)->setHelp($numrows == $counter ? 'Type':null);
1259

    
1260
	$group->add(new Form_Input(
1261
		'value' . $counter,
1262
		null,
1263
		'text',
1264
		$value
1265
	))->setHelp($numrows == $counter ? 'Value':null);
1266

    
1267
	$group->add(new Form_Button(
1268
		'deleterow' . $counter,
1269
		'Delete',
1270
		null,
1271
		'fa-trash'
1272
	))->addClass('btn-warning');
1273

    
1274
	$section->add($group);
1275

    
1276
	$counter++;
1277
}
1278

    
1279
$section->addInput(new Form_Button(
1280
	'addrow',
1281
	'Add',
1282
	null,
1283
	'fa-plus'
1284
))->addClass('btn-success');
1285

    
1286
$form->add($section);
1287

    
1288
if ($act == "newpool") {
1289
	$form->addGlobal(new Form_Input(
1290
		'act',
1291
		null,
1292
		'hidden',
1293
		'newpool'
1294
	));
1295
}
1296

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

    
1306
$form->addGlobal(new Form_Input(
1307
	'if',
1308
	null,
1309
	'hidden',
1310
	$if
1311
));
1312

    
1313
print($form);
1314

    
1315
// DHCP Static Mappings table
1316

    
1317
if (!is_numeric($pool) && !($act == "newpool")) {
1318
?>
1319

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

    
1377
<nav class="action-buttons">
1378
	<a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if)?>" class="btn btn-sm btn-success">
1379
		<i class="fa fa-plus icon-embed-btn"></i>
1380
		<?=gettext("Add")?>
1381
	</a>
1382
</nav>
1383
<?php
1384
}
1385
?>
1386

    
1387
<script type="text/javascript">
1388
//<![CDATA[
1389
events.push(function() {
1390

    
1391
	// Show advanced DNS options ======================================================================================
1392
	var showadvdns = false;
1393

    
1394
	function show_advdns(ispageload) {
1395
		var text;
1396
		// On page load decide the initial state based on the data.
1397
		if (ispageload) {
1398
<?php
1399
			if (!$pconfig['ddnsupdate'] && empty($pconfig['ddnsdomain']) && empty($pconfig['ddnsdomainprimary']) &&
1400
			    empty($pconfig['ddnsdomainkeyname']) && empty($pconfig['ddnsdomainkey'])) {
1401
				$showadv = false;
1402
			} else {
1403
				$showadv = true;
1404
			}
1405
?>
1406
			showadvdns = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1407
		} else {
1408
			// It was a click, swap the state.
1409
			showadvdns = !showadvdns;
1410
		}
1411

    
1412
		hideCheckbox('ddnsupdate', !showadvdns);
1413
		hideInput('ddnsdomain', !showadvdns);
1414
		hideInput('ddnsdomainprimary', !showadvdns);
1415
		hideInput('ddnsdomainkeyname', !showadvdns);
1416
		hideInput('ddnsdomainkey', !showadvdns);
1417

    
1418
		if (showadvdns) {
1419
			text = "<?=gettext('Hide Advanced');?>";
1420
		} else {
1421
			text = "<?=gettext('Display Advanced');?>";
1422
		}
1423
		$('#btnadvdns').html('<i class="fa fa-cog"></i> ' + text);
1424
	}
1425

    
1426
	$('#btnadvdns').click(function(event) {
1427
		show_advdns();
1428
	});
1429

    
1430
	// Show advanced MAC options ======================================================================================
1431
	var showadvmac = false;
1432

    
1433
	function show_advmac(ispageload) {
1434
		var text;
1435
		// On page load decide the initial state based on the data.
1436
		if (ispageload) {
1437
<?php
1438
			if (empty($pconfig['mac_allow']) && empty($pconfig['mac_deny'])) {
1439
				$showadv = false;
1440
			} else {
1441
				$showadv = true;
1442
			}
1443
?>
1444
			showadvmac = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1445
		} else {
1446
			// It was a click, swap the state.
1447
			showadvmac = !showadvmac;
1448
		}
1449

    
1450
		hideInput('mac_allow', !showadvmac);
1451
		hideInput('mac_deny', !showadvmac);
1452

    
1453
		if (showadvmac) {
1454
			text = "<?=gettext('Hide Advanced');?>";
1455
		} else {
1456
			text = "<?=gettext('Display Advanced');?>";
1457
		}
1458
		$('#btnadvmac').html('<i class="fa fa-cog"></i> ' + text);
1459
	}
1460

    
1461
	$('#btnadvmac').click(function(event) {
1462
		show_advmac();
1463
	});
1464

    
1465
	// Show advanced NTP options ======================================================================================
1466
	var showadvntp = false;
1467

    
1468
	function show_advntp(ispageload) {
1469
		var text;
1470
		// On page load decide the initial state based on the data.
1471
		if (ispageload) {
1472
<?php
1473
			if (empty($pconfig['ntp1']) && empty($pconfig['ntp2'])) {
1474
				$showadv = false;
1475
			} else {
1476
				$showadv = true;
1477
			}
1478
?>
1479
			showadvntp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1480
		} else {
1481
			// It was a click, swap the state.
1482
			showadvntp = !showadvntp;
1483
		}
1484

    
1485
		hideInput('ntp1', !showadvntp);
1486
		hideInput('ntp2', !showadvntp);
1487

    
1488
		if (showadvntp) {
1489
			text = "<?=gettext('Hide Advanced');?>";
1490
		} else {
1491
			text = "<?=gettext('Display Advanced');?>";
1492
		}
1493
		$('#btnadvntp').html('<i class="fa fa-cog"></i> ' + text);
1494
	}
1495

    
1496
	$('#btnadvntp').click(function(event) {
1497
		show_advntp();
1498
	});
1499

    
1500
	// Show advanced TFTP options ======================================================================================
1501
	var showadvtftp = false;
1502

    
1503
	function show_advtftp(ispageload) {
1504
		var text;
1505
		// On page load decide the initial state based on the data.
1506
		if (ispageload) {
1507
<?php
1508
			if (empty($pconfig['tftp'])) {
1509
				$showadv = false;
1510
			} else {
1511
				$showadv = true;
1512
			}
1513
?>
1514
			showadvtftp = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1515
		} else {
1516
			// It was a click, swap the state.
1517
			showadvtftp = !showadvtftp;
1518
		}
1519

    
1520
		hideInput('tftp', !showadvtftp);
1521

    
1522
		if (showadvtftp) {
1523
			text = "<?=gettext('Hide Advanced');?>";
1524
		} else {
1525
			text = "<?=gettext('Display Advanced');?>";
1526
		}
1527
		$('#btnadvtftp').html('<i class="fa fa-cog"></i> ' + text);
1528
	}
1529

    
1530
	$('#btnadvtftp').click(function(event) {
1531
		show_advtftp();
1532
	});
1533

    
1534
	// Show advanced LDAP options ======================================================================================
1535
	var showadvldap = false;
1536

    
1537
	function show_advldap(ispageload) {
1538
		var text;
1539
		// On page load decide the initial state based on the data.
1540
		if (ispageload) {
1541
<?php
1542
			if (empty($pconfig['ldap'])) {
1543
				$showadv = false;
1544
			} else {
1545
				$showadv = true;
1546
			}
1547
?>
1548
			showadvldap = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1549
		} else {
1550
			// It was a click, swap the state.
1551
			showadvldap = !showadvldap;
1552
		}
1553

    
1554
		hideInput('ldap', !showadvldap);
1555

    
1556
		if (showadvldap) {
1557
			text = "<?=gettext('Hide Advanced');?>";
1558
		} else {
1559
			text = "<?=gettext('Display Advanced');?>";
1560
		}
1561
		$('#btnadvldap').html('<i class="fa fa-cog"></i> ' + text);
1562
	}
1563

    
1564
	$('#btnadvldap').click(function(event) {
1565
		show_advldap();
1566
	});
1567

    
1568
	// Show advanced additional opts options ===========================================================================
1569
	var showadvopts = false;
1570

    
1571
	function show_advopts(ispageload) {
1572
		var text;
1573
		// On page load decide the initial state based on the data.
1574
		if (ispageload) {
1575
<?php
1576
			if (empty($pconfig['numberoptions']) ||
1577
			    (empty($pconfig['numberoptions']['item'][0]['number']) && (empty($pconfig['numberoptions']['item'][0]['value'])))) {
1578
				$showadv = false;
1579
			} else {
1580
				$showadv = true;
1581
			}
1582
?>
1583
			showadvopts = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1584
		} else {
1585
			// It was a click, swap the state.
1586
			showadvopts = !showadvopts;
1587
		}
1588

    
1589
		hideClass('adnlopts', !showadvopts);
1590

    
1591
		if (showadvopts) {
1592
			text = "<?=gettext('Hide Advanced');?>";
1593
		} else {
1594
			text = "<?=gettext('Display Advanced');?>";
1595
		}
1596
		$('#btnadvopts').html('<i class="fa fa-cog"></i> ' + text);
1597
	}
1598

    
1599
	$('#btnadvopts').click(function(event) {
1600
		show_advopts();
1601
	});
1602

    
1603
	// Show advanced Network Booting options ===========================================================================
1604
	var showadvnwkboot = false;
1605

    
1606
	function show_advnwkboot(ispageload) {
1607
		var text;
1608
		// On page load decide the initial state based on the data.
1609
		if (ispageload) {
1610
<?php
1611
			if (empty($pconfig['netboot'])) {
1612
				$showadv = false;
1613
			} else {
1614
				$showadv = true;
1615
			}
1616
?>
1617
			showadvnwkboot = <?php if ($showadv) {echo 'true';} else {echo 'false';} ?>;
1618
		} else {
1619
			// It was a click, swap the state.
1620
			showadvnwkboot = !showadvnwkboot;
1621
		}
1622

    
1623
		hideCheckbox('netboot', !showadvnwkboot);
1624
		hideInput('nextserver', !showadvnwkboot);
1625
		hideInput('filename', !showadvnwkboot);
1626
		hideInput('filename32', !showadvnwkboot);
1627
		hideInput('filename64', !showadvnwkboot);
1628
		hideInput('rootpath', !showadvnwkboot);
1629

    
1630
		if (showadvnwkboot) {
1631
			text = "<?=gettext('Hide Advanced');?>";
1632
		} else {
1633
			text = "<?=gettext('Display Advanced');?>";
1634
		}
1635
		$('#btnadvnwkboot').html('<i class="fa fa-cog"></i> ' + text);
1636
	}
1637

    
1638
	$('#btnadvnwkboot').click(function(event) {
1639
		show_advnwkboot();
1640
	});
1641

    
1642
	// ---------- On initial page load ------------------------------------------------------------
1643

    
1644
	show_advdns(true);
1645
	show_advmac(true);
1646
	show_advntp(true);
1647
	show_advtftp(true);
1648
	show_advldap(true);
1649
	show_advopts(true);
1650
	show_advnwkboot(true);
1651

    
1652
	// Suppress "Delete row" button if there are fewer than two rows
1653
	checkLastRow();
1654
});
1655
//]]>
1656
</script>
1657

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