Project

General

Profile

Download (81.1 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	services.inc
4
	part of the pfSense project (https://www.pfsense.org)
5

    
6
	originally part of m0n0wall (http://m0n0.ch/wall)
7
	Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
8
	Copyright (C) 2010	Ermal Luçi
9
	All rights reserved.
10

    
11
	Redistribution and use in source and binary forms, with or without
12
	modification, are permitted provided that the following conditions are met:
13

    
14
	1. Redistributions of source code must retain the above copyright notice,
15
	   this list of conditions and the following disclaimer.
16

    
17
	2. Redistributions in binary form must reproduce the above copyright
18
	   notice, this list of conditions and the following disclaimer in the
19
	   documentation and/or other materials provided with the distribution.
20

    
21
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
22
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
23
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
25
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
	POSSIBILITY OF SUCH DAMAGE.
31
*/
32

    
33
/*
34
	pfSense_BUILDER_BINARIES:	/usr/bin/killall	/bin/pgrep	/bin/sh	/usr/local/sbin/dhcpd	/usr/local/sbin/igmpproxy
35
	pfSense_BUILDER_BINARIES:	/sbin/ifconfig		/usr/local/sbin/dnsmasq
36
	pfSense_BUILDER_BINARIES:	/usr/local/sbin/miniupnpd	/usr/sbin/radvd
37
	pfSense_BUILDER_BINARIES:	/usr/local/sbin/dhcleases6	/usr/sbin/bsnmpd
38
	pfSense_MODULE:	utils
39
*/
40

    
41
define('DYNDNS_PROVIDER_VALUES', 'citynetwork cloudflare custom custom-v6 dhs dnsexit dnsimple dnsmadeeasy dnsomatic dyndns dyndns-custom dyndns-static dyns easydns eurodns freedns glesys googledomains gratisdns he-net he-net-v6 he-net-tunnelbroker loopia namecheap noip noip-free ods opendns ovh-dynhost route53 selfhost zoneedit');
42
define('DYNDNS_PROVIDER_DESCRIPTIONS', 'City Network,CloudFlare,Custom,Custom (v6),DHS,DNSexit,DNSimple,DNS Made Easy,DNS-O-Matic,DynDNS (dynamic),DynDNS (custom),DynDNS (static),DyNS,easyDNS,Euro Dns,freeDNS,GleSYS,Google Domains,GratisDNS,HE.net,HE.net (v6),HE.net Tunnelbroker,Loopia,Namecheap,No-IP,No-IP (free),ODS.org,OpenDNS,OVH DynHOST,Route 53,SelfHost,ZoneEdit');
43

    
44
/* implement ipv6 route advertising daemon */
45
function services_radvd_configure($blacklist = array()) {
46
	global $config, $g;
47

    
48
	if ($g['platform'] == 'jail') {
49
		return;
50
	}
51

    
52
	if (isset($config['system']['developerspew'])) {
53
		$mt = microtime();
54
		echo "services_radvd_configure() being called $mt\n";
55
	}
56

    
57
	if (!is_array($config['dhcpdv6'])) {
58
		$config['dhcpdv6'] = array();
59
	}
60

    
61
	$Iflist = get_configured_interface_list();
62
	$Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces());
63
	$carplist = get_configured_carp_interface_list();
64

    
65
	$radvdconf = "# Automatically Generated, do not edit\n";
66

    
67
	/* Process all links which need the router advertise daemon */
68
	$radvdifs = array();
69

    
70
	/* handle manually configured DHCP6 server settings first */
71
	foreach ($config['dhcpdv6'] as $dhcpv6if => $dhcpv6ifconf) {
72
		if (!is_array($config['interfaces'][$dhcpv6if])) {
73
			continue;
74
		}
75
		if (!isset($config['interfaces'][$dhcpv6if]['enable'])) {
76
			continue;
77
		}
78

    
79
		/* Do not put in the config an interface which is down */
80
		if (isset($blacklist[$dhcpv6if])) {
81
			continue;
82
		}
83
		if (!isset($dhcpv6ifconf['ramode'])) {
84
			$dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode'];
85
		}
86

    
87
		/* are router advertisements enabled? */
88
		if ($dhcpv6ifconf['ramode'] == "disabled") {
89
			continue;
90
		}
91

    
92
		if (!isset($dhcpv6ifconf['rapriority'])) {
93
			$dhcpv6ifconf['rapriority'] = "medium";
94
		}
95

    
96
		/* always start with the real parent, we override with the carp if later */
97
		$carpif = false;
98
		/* check if we need to listen on a CARP interface */
99
		if (!empty($dhcpv6ifconf['rainterface'])) {
100
			if (!empty($carplist[$dhcpv6ifconf['rainterface']])) {
101
				$dhcpv6if = $dhcpv6ifconf['rainterface'];
102
				$carpif = true;
103
			}
104
		}
105

    
106
		if (strstr($dhcpv6if, "_vip")) {
107
			// CARP IP, check if it's enabled and find parent
108
			if (!get_carp_status() || get_carp_interface_status($dhcpv6if) != "MASTER") {
109
				continue;
110
			}
111
			$ifparent = link_carp_interface_to_parent($dhcpv6if);
112
			$realif = convert_friendly_interface_to_real_interface_name($ifparent);
113
		} else {
114
			$realif = get_real_interface($dhcpv6if, "inet6");
115
		}
116

    
117
		if (isset($radvdifs[$realif])) {
118
			continue;
119
		}
120

    
121
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
122
		if (!is_ipaddrv6($ifcfgipv6)) {
123
			continue;
124
		}
125

    
126
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
127
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
128
		$radvdifs[$realif] = $realif;
129

    
130
		$radvdconf .= "# Generated for DHCPv6 Server $dhcpv6if\n";
131
		$radvdconf .= "interface {$realif} {\n";
132
		if (strstr($realif, "ovpn")) {
133
			$radvdconf .= "\tUnicastOnly on;\n";
134
		}
135
		$radvdconf .= "\tAdvSendAdvert on;\n";
136
		$radvdconf .= "\tMinRtrAdvInterval 5;\n";
137
		$radvdconf .= "\tMaxRtrAdvInterval 20;\n";
138
		$mtu = get_interface_mtu($realif);
139
		if (is_numeric($mtu)) {
140
			$radvdconf .= "\tAdvLinkMTU {$mtu};\n";
141
		} else {
142
			$radvdconf .= "\tAdvLinkMTU 1280;\n";
143
		}
144
		// $radvdconf .= "\tDeprecatePrefix on;\n";
145
		switch ($dhcpv6ifconf['rapriority']) {
146
			case "low":
147
				$radvdconf .= "\tAdvDefaultPreference low;\n";
148
				break;
149
			case "high":
150
				$radvdconf .= "\tAdvDefaultPreference high;\n";
151
				break;
152
			default:
153
				$radvdconf .= "\tAdvDefaultPreference medium;\n";
154
				break;
155
		}
156
		switch ($dhcpv6ifconf['ramode']) {
157
			case "managed":
158
			case "assist":
159
				$radvdconf .= "\tAdvManagedFlag on;\n";
160
				$radvdconf .= "\tAdvOtherConfigFlag on;\n";
161
				break;
162
			case "stateless_dhcp":
163
				$radvdconf .= "\tAdvManagedFlag off;\n";
164
				$radvdconf .= "\tAdvOtherConfigFlag on;\n";
165
				break;
166
		}
167
		$radvdconf .= "\tprefix {$subnetv6}/{$ifcfgsnv6} {\n";
168
		if ($carpif == true) {
169
			$radvdconf .= "\t\tDeprecatePrefix off;\n";
170
		} else {
171
			$radvdconf .= "\t\tDeprecatePrefix on;\n";
172
		}
173
		switch ($dhcpv6ifconf['ramode']) {
174
			case "managed":
175
				$radvdconf .= "\t\tAdvOnLink on;\n";
176
				$radvdconf .= "\t\tAdvAutonomous off;\n";
177
				$radvdconf .= "\t\tAdvRouterAddr on;\n";
178
				break;
179
			case "router":
180
				$radvdconf .= "\t\tAdvOnLink off;\n";
181
				$radvdconf .= "\t\tAdvAutonomous off;\n";
182
				$radvdconf .= "\t\tAdvRouterAddr on;\n";
183
				break;
184
			case "stateless_dhcp":
185
			case "assist":
186
				$radvdconf .= "\t\tAdvOnLink on;\n";
187
				$radvdconf .= "\t\tAdvAutonomous on;\n";
188
				$radvdconf .= "\t\tAdvRouterAddr on;\n";
189
				break;
190
			case "unmanaged":
191
				$radvdconf .= "\t\tAdvOnLink on;\n";
192
				$radvdconf .= "\t\tAdvAutonomous on;\n";
193
				$radvdconf .= "\t\tAdvRouterAddr on;\n";
194
				break;
195
		}
196
		$radvdconf .= "\t};\n";
197

    
198
		if (is_array($dhcpv6ifconf['subnets']['item'])) {
199
			foreach ($dhcpv6ifconf['subnets']['item'] as $subnet) {
200
				if (is_subnetv6($subnet)) {
201
					$radvdconf .= "\tprefix {$subnet} {\n";
202
					if ($carpif == true) {
203
						$radvdconf .= "\t\tDeprecatePrefix off;\n";
204
					} else {
205
						$radvdconf .= "\t\tDeprecatePrefix on;\n";
206
					}
207
					switch ($dhcpv6ifconf['ramode']) {
208
						case "managed":
209
							$radvdconf .= "\t\tAdvOnLink on;\n";
210
							$radvdconf .= "\t\tAdvAutonomous off;\n";
211
							$radvdconf .= "\t\tAdvRouterAddr on;\n";
212
							break;
213
						case "router":
214
							$radvdconf .= "\t\tAdvOnLink off;\n";
215
							$radvdconf .= "\t\tAdvAutonomous off;\n";
216
							$radvdconf .= "\t\tAdvRouterAddr on;\n";
217
							break;
218
						case "assist":
219
							$radvdconf .= "\t\tAdvOnLink on;\n";
220
							$radvdconf .= "\t\tAdvAutonomous on;\n";
221
							$radvdconf .= "\t\tAdvRouterAddr on;\n";
222
							break;
223
						case "unmanaged":
224
							$radvdconf .= "\t\tAdvOnLink on;\n";
225
							$radvdconf .= "\t\tAdvAutonomous on;\n";
226
							$radvdconf .= "\t\tAdvRouterAddr on;\n";
227
							break;
228
					}
229
					$radvdconf .= "\t};\n";
230
				}
231
			}
232
		}
233
		if ($carpif === true) {
234
			$radvdconf .= "\troute ::/0 {\n";
235
			$radvdconf .= "\t\tRemoveRoute off;\n";
236
			$radvdconf .= "\t};\n";
237
		} else {
238
			$radvdconf .= "\troute ::/0 {\n";
239
			$radvdconf .= "\t\tRemoveRoute on;\n";
240
			$radvdconf .= "\t};\n";
241
		}
242

    
243
		/* add DNS servers */
244
		$dnslist = array();
245
		if (isset($dhcpv6ifconf['rasamednsasdhcp6']) && is_array($dhcpv6ifconf['dnsserver']) && !empty($dhcpv6ifconf['dnsserver'])) {
246
			foreach ($dhcpv6ifconf['dnsserver'] as $server) {
247
				if (is_ipaddrv6($server)) {
248
					$dnslist[] = $server;
249
				}
250
			}
251
		} elseif (!isset($dhcpv6ifconf['rasamednsasdhcp6']) && isset($dhcpv6ifconf['radnsserver']) && is_array($dhcpv6ifconf['radnsserver'])) {
252
			foreach ($dhcpv6ifconf['radnsserver'] as $server) {
253
				if (is_ipaddrv6($server)) {
254
					$dnslist[] = $server;
255
				}
256
			}
257
		} elseif (isset($config['dnsmasq']['enable']) || isset($config['unbound']['enable'])) {
258
			$dnslist[] = get_interface_ipv6($realif);
259
		} elseif (is_array($config['system']['dnsserver']) && !empty($config['system']['dnsserver'])) {
260
			foreach ($config['system']['dnsserver'] as $server) {
261
				if (is_ipaddrv6($server)) {
262
					$dnslist[] = $server;
263
				}
264
			}
265
		}
266
		if (count($dnslist) > 0) {
267
			$dnsstring = implode(" ", $dnslist);
268
			if ($dnsstring <> "") {
269
				$radvdconf .= "\tRDNSS {$dnsstring} { };\n";
270
			}
271
		}
272
		if (!empty($dhcpv6ifconf['domain'])) {
273
			$radvdconf .= "\tDNSSL {$dhcpv6ifconf['domain']} { };\n";
274
		} elseif (!empty($config['system']['domain'])) {
275
			$radvdconf .= "\tDNSSL {$config['system']['domain']} { };\n";
276
		}
277
		$radvdconf .= "};\n";
278
	}
279

    
280
	/* handle DHCP-PD prefixes and 6RD dynamic interfaces */
281
	foreach ($Iflist as $if => $ifdescr) {
282
		if (!isset($config['interfaces'][$if]['track6-interface'])) {
283
			continue;
284
		}
285
		if (!isset($config['interfaces'][$if]['enable'])) {
286
			continue;
287
		}
288
		/* Do not put in the config an interface which is down */
289
		if (isset($blacklist[$if])) {
290
			continue;
291
		}
292
		$trackif = $config['interfaces'][$if]['track6-interface'];
293
		if (empty($config['interfaces'][$trackif])) {
294
			continue;
295
		}
296

    
297
		if (strstr($if, "_vip")) {
298
			// CARP IP, find parent
299
			$ifparent = link_carp_interface_to_parent($if);
300
			$realif = convert_friendly_interface_to_real_interface_name($ifparent);
301
		} else {
302
			$realif = get_real_interface($if, "inet6");
303
		}
304

    
305
		/* prevent duplicate entries, manual overrides */
306
		if (isset($radvdifs[$realif])) {
307
			continue;
308
		}
309

    
310
		$ifcfgipv6 = get_interface_ipv6($if);
311
		if (!is_ipaddrv6($ifcfgipv6)) {
312
			$subnetv6 = "::";
313
			$ifcfgsnv6 = "64";
314
		} else {
315
			$ifcfgsnv6 = get_interface_subnetv6($if);
316
			$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
317
		}
318
		$radvdifs[$realif] = $realif;
319

    
320
		$autotype = $config['interfaces'][$trackif]['ipaddrv6'];
321

    
322
		if ($g['debug']) {
323
			log_error("configuring RA on {$if} for type {$autotype} radvd subnet {$subnetv6}/{$ifcfgsnv6}");
324
		}
325

    
326
		$radvdconf .= "# Generated config for {$autotype} delegation from {$trackif} on {$if}\n";
327
		$radvdconf .= "interface {$realif} {\n";
328
		$radvdconf .= "\tAdvSendAdvert on;\n";
329
		$radvdconf .= "\tMinRtrAdvInterval 3;\n";
330
		$radvdconf .= "\tMaxRtrAdvInterval 10;\n";
331
		$mtu = get_interface_mtu($realif);
332
		if (is_numeric($mtu)) {
333
			$radvdconf .= "\tAdvLinkMTU {$mtu};\n";
334
		} else {
335
			$radvdconf .= "\tAdvLinkMTU 1280;\n";
336
		}
337
		$radvdconf .= "\tAdvOtherConfigFlag on;\n";
338
		$radvdconf .= "\t\tprefix {$subnetv6}/{$ifcfgsnv6} {\n";
339
		$radvdconf .= "\t\tAdvOnLink on;\n";
340
		$radvdconf .= "\t\tAdvAutonomous on;\n";
341
		$radvdconf .= "\t\tAdvRouterAddr on;\n";
342
		$radvdconf .= "\t};\n";
343

    
344
		/* add DNS servers */
345
		$dnslist = array();
346
		if (isset($config['dnsmasq']['enable']) || isset($config['unbound']['enable'])) {
347
			$dnslist[] = $ifcfgipv6;
348
		} elseif (is_array($config['system']['dnsserver']) && !empty($config['system']['dnsserver'])) {
349
			foreach ($config['system']['dnsserver'] as $server) {
350
				if (is_ipaddrv6($server)) {
351
					$dnslist[] = $server;
352
				}
353
			}
354
		}
355
		if (count($dnslist) > 0) {
356
			$dnsstring = implode(" ", $dnslist);
357
			if (!empty($dnsstring)) {
358
				$radvdconf .= "\tRDNSS {$dnsstring} { };\n";
359
			}
360
		}
361
		if (!empty($config['system']['domain'])) {
362
			$radvdconf .= "\tDNSSL {$config['system']['domain']} { };\n";
363
		}
364
		$radvdconf .= "};\n";
365
	}
366

    
367
	/* write radvd.conf */
368
	if (!@file_put_contents("{$g['varetc_path']}/radvd.conf", $radvdconf)) {
369
		log_error("Error: cannot open radvd.conf in services_radvd_configure().\n");
370
		if (platform_booting()) {
371
			printf("Error: cannot open radvd.conf in services_radvd_configure().\n");
372
		}
373
	}
374
	unset($radvdconf);
375

    
376
	if (count($radvdifs) > 0) {
377
		if (isvalidpid("{$g['varrun_path']}/radvd.pid")) {
378
			sigkillbypid("{$g['varrun_path']}/radvd.pid", "HUP");
379
		} else {
380
			mwexec("/usr/local/sbin/radvd -p {$g['varrun_path']}/radvd.pid -C {$g['varetc_path']}/radvd.conf -m syslog");
381
		}
382
	} else {
383
		/* we need to shut down the radvd cleanly, it will send out the prefix
384
		 * information with a lifetime of 0 to notify clients of a (possible) new prefix */
385
		if (isvalidpid("{$g['varrun_path']}/radvd.pid")) {
386
			log_error("Shutting down Router Advertisment daemon cleanly");
387
			killbypid("{$g['varrun_path']}/radvd.pid");
388
			@unlink("{$g['varrun_path']}/radvd.pid");
389
		}
390
	}
391
	return 0;
392
}
393

    
394
function services_dhcpd_configure($family = "all", $blacklist = array()) {
395
	global $config, $g;
396

    
397
	/* configure DHCPD chroot once */
398
	$fd = fopen("{$g['tmp_path']}/dhcpd.sh", "w");
399
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}\n");
400
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/dev\n");
401
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/etc\n");
402
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/usr/local/sbin\n");
403
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/var/db\n");
404
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/var/run\n");
405
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/usr\n");
406
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/lib\n");
407
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/run\n");
408
	fwrite($fd, "/usr/sbin/chown -R dhcpd:_dhcp {$g['dhcpd_chroot_path']}/*\n");
409
	fwrite($fd, "/bin/cp -n /lib/libc.so.* {$g['dhcpd_chroot_path']}/lib/\n");
410
	fwrite($fd, "/bin/cp -n /usr/local/sbin/dhcpd {$g['dhcpd_chroot_path']}/usr/local/sbin/\n");
411
	fwrite($fd, "/bin/chmod a+rx {$g['dhcpd_chroot_path']}/usr/local/sbin/dhcpd\n");
412

    
413
	$status = `/sbin/mount | /usr/bin/grep -v grep  | /usr/bin/grep  "{$g['dhcpd_chroot_path']}/dev"`;
414
	if (!trim($status)) {
415
		fwrite($fd, "/sbin/mount -t devfs devfs {$g['dhcpd_chroot_path']}/dev\n");
416
	}
417
	fclose($fd);
418
	mwexec("/bin/sh {$g['tmp_path']}/dhcpd.sh");
419

    
420
	if ($family == "all" || $family == "inet") {
421
		services_dhcpdv4_configure();
422
	}
423
	if ($family == "all" || $family == "inet6") {
424
		services_dhcpdv6_configure($blacklist);
425
		services_radvd_configure($blacklist);
426
	}
427
}
428

    
429
function services_dhcpdv4_configure() {
430
	global $config, $g;
431
	$need_ddns_updates = false;
432
	$ddns_zones = array();
433

    
434
	if ($g['services_dhcp_server_enable'] == false) {
435
		return;
436
	}
437

    
438
	if (isset($config['system']['developerspew'])) {
439
		$mt = microtime();
440
		echo "services_dhcpdv4_configure($if) being called $mt\n";
441
	}
442

    
443
	/* kill any running dhcpd */
444
	if (isvalidpid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid")) {
445
		killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid");
446
	}
447

    
448
	/* DHCP enabled on any interfaces? */
449
	if (!is_dhcp_server_enabled()) {
450
		return 0;
451
	}
452

    
453
	/* if OLSRD is enabled, allow WAN to house DHCP. */
454
	if (!function_exists('is_package_installed')) {
455
		require_once('pkg-utils.inc');
456
	}
457
	if (is_package_installed('olsrd') && isset($config['installedpackages']['olsrd'])) {
458
		foreach ($config['installedpackages']['olsrd']['config'] as $olsrd) {
459
			if (isset($olsrd['enable']) && $olsrd['enable'] == "on") {
460
				$is_olsr_enabled = true;
461
				break;
462
			}
463
		}
464
	}
465

    
466
	if (platform_booting()) {
467
		/* restore the leases, if we have them */
468
		if (file_exists("{$g['cf_conf_path']}/dhcpleases.tgz")) {
469
			$dhcprestore = "";
470
			$dhcpreturn = "";
471
			exec("cd /;LANG=C /usr/bin/tar -xzf {$g['cf_conf_path']}/dhcpleases.tgz 2>&1", $dhcprestore, $dhcpreturn);
472
			$dhcprestore = implode(" ", $dhcprestore);
473
			if ($dhcpreturn <> 0) {
474
				log_error(sprintf(gettext('DHCP leases restore failed exited with %1$s, the error is: %2$s%3$s'), $dhcpreturn, $dhcprestore, "\n"));
475
			}
476
		}
477
		/* If this backup is still there on a full install, but we aren't going to use ram disks, remove the archive since this is a transition. */
478
		if (($g['platform'] == "pfSense") && !isset($config['system']['use_mfs_tmpvar'])) {
479
			unlink_if_exists("{$g['cf_conf_path']}/dhcpleases.tgz");
480
		}
481
	}
482

    
483
	$syscfg = $config['system'];
484
	if (!is_array($config['dhcpd'])) {
485
		$config['dhcpd'] = array();
486
	}
487
	$dhcpdcfg = $config['dhcpd'];
488
	$Iflist = get_configured_interface_list();
489

    
490
	/* Only consider DNS servers with IPv4 addresses for the IPv4 DHCP server. */
491
	$dns_arrv4 = array();
492
	if (is_array($syscfg['dnsserver'])) {
493
		foreach ($syscfg['dnsserver'] as $dnsserver) {
494
			if (is_ipaddrv4($dnsserver)) {
495
				$dns_arrv4[] = $dnsserver;
496
			}
497
		}
498
	}
499

    
500
	if (platform_booting()) {
501
		echo gettext("Starting DHCP service...");
502
	} else {
503
		sleep(1);
504
	}
505

    
506
	$custoptions = "";
507
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
508
		if (is_array($dhcpifconf['numberoptions']) && is_array($dhcpifconf['numberoptions']['item'])) {
509
			foreach ($dhcpifconf['numberoptions']['item'] as $itemidx => $item) {
510
				if (!empty($item['type'])) {
511
					$itemtype = $item['type'];
512
				} else {
513
					$itemtype = "text";
514
				}
515
				$custoptions .= "option custom-{$dhcpif}-{$itemidx} code {$item['number']} = {$itemtype};\n";
516
			}
517
		}
518
	}
519

    
520
	$dhcpdconf = <<<EOD
521

    
522
option domain-name "{$syscfg['domain']}";
523
option ldap-server code 95 = text;
524
option domain-search-list code 119 = text;
525
option arch code 93 = unsigned integer 16; # RFC4578
526
{$custoptions}
527
default-lease-time 7200;
528
max-lease-time 86400;
529
log-facility local7;
530
one-lease-per-client true;
531
deny duplicates;
532
ping-check true;
533
update-conflict-detection false;
534

    
535
EOD;
536

    
537
	if (!isset($dhcpifconf['disableauthoritative'])) {
538
		$dhcpdconf .= "authoritative;\n";
539
	}
540

    
541
	if (isset($dhcpifconf['alwaysbroadcast'])) {
542
		$dhcpdconf .= "always-broadcast on\n";
543
	}
544

    
545
	$dhcpdifs = array();
546
	$enable_add_routers = false;
547
	$gateways_arr = return_gateways_array();
548
	/* only add a routers line if the system has any IPv4 gateway at all */
549
	/* a static route has a gateway, manually overriding this field always works */
550
	foreach ($gateways_arr as $gwitem) {
551
		if ($gwitem['ipprotocol'] == "inet") {
552
			$enable_add_routers = true;
553
			break;
554
		}
555
	}
556

    
557
	/*    loop through and determine if we need to setup
558
	 *    failover peer "bleh" entries
559
	 */
560
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
561

    
562
		if (!isset($config['interfaces'][$dhcpif]['enable'])) {
563
			continue;
564
		}
565

    
566
		interfaces_staticarp_configure($dhcpif);
567

    
568
		if (!isset($dhcpifconf['enable'])) {
569
			continue;
570
		}
571

    
572
		if ($dhcpifconf['failover_peerip'] <> "") {
573
			$intip = get_interface_ip($dhcpif);
574
			/*
575
			 *    yep, failover peer is defined.
576
			 *    does it match up to a defined vip?
577
			 */
578
			$skew = 110;
579
			if (is_array($config['virtualip']['vip'])) {
580
				foreach ($config['virtualip']['vip'] as $vipent) {
581
					if ($vipent['interface'] == $dhcpif) {
582
						$carp_nw = gen_subnet($vipent['subnet'], $vipent['subnet_bits']);
583
						if (ip_in_subnet($dhcpifconf['failover_peerip'], "{$carp_nw}/{$vipent['subnet_bits']}")) {
584
							/* this is the interface! */
585
							if (is_numeric($vipent['advskew']) && (intval($vipent['advskew']) < 20)) {
586
								$skew = 0;
587
								break;
588
							}
589
						}
590
					}
591
				}
592
			} else {
593
				log_error(gettext("Warning!  DHCP Failover setup and no CARP virtual IPs defined!"));
594
			}
595
			if ($skew > 10) {
596
				$type = "secondary";
597
				$my_port = "520";
598
				$peer_port = "519";
599
			} else {
600
				$my_port = "519";
601
				$peer_port = "520";
602
				$type = "primary";
603
				$dhcpdconf_pri  = "split 128;\n";
604
				$dhcpdconf_pri .= "  mclt 600;\n";
605
			}
606

    
607
			if (is_ipaddrv4($intip)) {
608
				$dhcpdconf .= <<<EOPP
609
failover peer "dhcp_{$dhcpif}" {
610
  {$type};
611
  address {$intip};
612
  port {$my_port};
613
  peer address {$dhcpifconf['failover_peerip']};
614
  peer port {$peer_port};
615
  max-response-delay 10;
616
  max-unacked-updates 10;
617
  {$dhcpdconf_pri}
618
  load balance max seconds 3;
619
}
620
\n
621
EOPP;
622
			}
623
		}
624
	}
625

    
626
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
627

    
628
		$newzone = array();
629
		$ifcfg = $config['interfaces'][$dhcpif];
630

    
631
		if (!isset($dhcpifconf['enable']) || !isset($Iflist[$dhcpif])) {
632
			continue;
633
		}
634
		$ifcfgip = get_interface_ip($dhcpif);
635
		$ifcfgsn = get_interface_subnet($dhcpif);
636
		$subnet = gen_subnet($ifcfgip, $ifcfgsn);
637
		$subnetmask = gen_subnet_mask($ifcfgsn);
638

    
639
		if (!is_ipaddr($subnet)) {
640
			continue;
641
		}
642

    
643
		if ($is_olsr_enabled == true) {
644
			if ($dhcpifconf['netmask']) {
645
				$subnetmask = gen_subnet_mask($dhcpifconf['netmask']);
646
			}
647
		}
648

    
649
		$all_pools = array();
650
		$all_pools[] = $dhcpifconf;
651
		if (is_array($dhcpifconf['pool'])) {
652
			$all_pools = array_merge($all_pools, $dhcpifconf['pool']);
653
		}
654

    
655
		$dnscfg = "";
656

    
657
		if ($dhcpifconf['domain']) {
658
			$dnscfg .= "	option domain-name \"{$dhcpifconf['domain']}\";\n";
659
		}
660

    
661
		if ($dhcpifconf['domainsearchlist'] <> "") {
662
			$dnscfg .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpifconf['domainsearchlist'])) . "\";\n";
663
		}
664

    
665
		if (isset($dhcpifconf['ddnsupdate'])) {
666
			$need_ddns_updates = true;
667
			$newzone = array();
668
			if ($dhcpifconf['ddnsdomain'] <> "") {
669
				$newzone['domain-name'] = $dhcpifconf['ddnsdomain'];
670
				$dnscfg .= "	ddns-domainname \"{$dhcpifconf['ddnsdomain']}\";\n";
671
			} else {
672
				$newzone['domain-name'] = $config['system']['domain'];
673
			}
674
			$revsubnet = explode(".", $subnet);
675
			$revsubnet = array_reverse($revsubnet);
676
			foreach ($revsubnet as $octet) {
677
				if ($octet != "0") {
678
					break;
679
				}
680
				array_shift($revsubnet);
681
			}
682
			$newzone['ptr-domain'] = implode(".", $revsubnet) . ".in-addr.arpa";
683
		}
684

    
685
		if (is_array($dhcpifconf['dnsserver']) && ($dhcpifconf['dnsserver'][0])) {
686
			$dnscfg .= "	option domain-name-servers " . join(",", $dhcpifconf['dnsserver']) . ";";
687
			if ($newzone['domain-name']) {
688
				$newzone['dns-servers'] = $dhcpifconf['dnsserver'];
689
			}
690
		} else if (isset($config['dnsmasq']['enable'])) {
691
			$dnscfg .= "	option domain-name-servers {$ifcfgip};";
692
			if ($newzone['domain-name'] && is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
693
				$newzone['dns-servers'] = $syscfg['dnsserver'];
694
			}
695
		} else if (isset($config['unbound']['enable'])) {
696
			$dnscfg .= "	option domain-name-servers {$ifcfgip};";
697
		} else if (!empty($dns_arrv4)) {
698
			$dnscfg .= "	option domain-name-servers " . join(",", $dns_arrv4) . ";";
699
			if ($newzone['domain-name']) {
700
				$newzone['dns-servers'] = $dns_arrv4;
701
			}
702
		}
703

    
704
		/* Create classes - These all contain comma separated lists. Join them into one
705
		   big comma separated string then split them all up. */
706
		$all_mac_strings = array();
707
		if (is_array($dhcpifconf['pool'])) {
708
			foreach ($all_pools as $poolconf) {
709
				$all_mac_strings[] = $poolconf['mac_allow'];
710
				$all_mac_strings[] = $poolconf['mac_deny'];
711
			}
712
		}
713
		$all_mac_strings[] = $dhcpifconf['mac_allow'];
714
		$all_mac_strings[] = $dhcpifconf['mac_deny'];
715
		if (!empty($all_mac_strings)) {
716
			$all_mac_list = array_unique(explode(',', implode(',', $all_mac_strings)));
717
			foreach ($all_mac_list as $mac) {
718
				if (empty($mac)) {
719
					continue;
720
				}
721
				$dhcpdconf .= 'class "' . str_replace(':', '', $mac) . '" {' . "\n";
722
				// Skip the first octet of the MAC address - for media type, typically Ethernet ("01") and match the rest.
723
				$dhcpdconf .= '	match if substring (hardware, 1, ' . (substr_count($mac, ':') + 1) . ') = ' . $mac . ';' . "\n";
724
				$dhcpdconf .= '}' . "\n";
725
			}
726
		}
727

    
728
		$dhcpdconf .= "subnet {$subnet} netmask {$subnetmask} {\n";
729

    
730
		// Setup pool options
731
		foreach ($all_pools as $poolconf) {
732
			$dhcpdconf .= "	pool {\n";
733
			/* is failover dns setup? */
734
			if (is_array($poolconf['dnsserver']) && $poolconf['dnsserver'][0] <> "") {
735
				$dhcpdconf .= "		option domain-name-servers {$poolconf['dnsserver'][0]}";
736
				if ($poolconf['dnsserver'][1] <> "") {
737
					$dhcpdconf .= ",{$poolconf['dnsserver'][1]}";
738
				}
739
				if ($poolconf['dnsserver'][2] <> "") {
740
					$dhcpdconf .= ",{$poolconf['dnsserver'][2]}";
741
				}
742
				if ($poolconf['dnsserver'][3] <> "") {
743
					$dhcpdconf .= ",{$poolconf['dnsserver'][3]}";
744
				}
745
				$dhcpdconf .= ";\n";
746
			}
747

    
748
			/* allow/deny MACs */
749
			$mac_allow_list = array_unique(explode(',', $poolconf['mac_allow']));
750
			foreach ($mac_allow_list as $mac) {
751
				if (empty($mac)) {
752
					continue;
753
				}
754
				$dhcpdconf .= "		allow members of \"" . str_replace(':', '', $mac) . "\";\n";
755
			}
756
			$mac_deny_list = array_unique(explode(',', $poolconf['mac_deny']));
757
			foreach ($mac_deny_list as $mac) {
758
				if (empty($mac)) {
759
					continue;
760
				}
761
				$dhcpdconf .= "		deny members of \"" . str_replace(':', '', $mac) . "\";\n";
762
			}
763

    
764
			if ($poolconf['failover_peerip'] <> "") {
765
				$dhcpdconf .= "		deny dynamic bootp clients;\n";
766
			}
767

    
768
			if (isset($poolconf['denyunknown'])) {
769
			   $dhcpdconf .= "		deny unknown-clients;\n";
770
			}
771

    
772
			if ($poolconf['gateway'] && $poolconf['gateway'] != "none" && ($poolconf['gateway'] != $dhcpifconf['gateway'])) {
773
				$dhcpdconf .= "		option routers {$poolconf['gateway']};\n";
774
			}
775

    
776
			if ($dhcpifconf['failover_peerip'] <> "") {
777
				$dhcpdconf .= "		failover peer \"dhcp_{$dhcpif}\";\n";
778
			}
779

    
780
			$pdnscfg = "";
781

    
782
			if ($poolconf['domain'] && ($poolconf['domain'] != $dhcpifconf['domain'])) {
783
				$pdnscfg .= "		option domain-name \"{$poolconf['domain']}\";\n";
784
			}
785

    
786
			if (!empty($poolconf['domainsearchlist']) && ($poolconf['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
787
				$pdnscfg .= "		option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $poolconf['domainsearchlist'])) . "\";\n";
788
			}
789

    
790
			if (isset($poolconf['ddnsupdate'])) {
791
				if (($poolconf['ddnsdomain'] <> "") && ($poolconf['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
792
					$pdnscfg .= "		ddns-domainname \"{$poolconf['ddnsdomain']}\";\n";
793
				}
794
				$pdnscfg .= "		ddns-update-style interim;\n";
795
			}
796

    
797
			if (is_array($poolconf['dnsserver']) && ($poolconf['dnsserver'][0]) && ($poolconf['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
798
				$pdnscfg .= "		option domain-name-servers " . join(",", $poolconf['dnsserver']) . ";\n";
799
			}
800
			$dhcpdconf .= "{$pdnscfg}";
801

    
802
			// default-lease-time
803
			if ($poolconf['defaultleasetime'] && ($poolconf['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) {
804
				$dhcpdconf .= "		default-lease-time {$poolconf['defaultleasetime']};\n";
805
			}
806

    
807
			// max-lease-time
808
			if ($poolconf['maxleasetime'] && ($poolconf['maxleasetime'] != $dhcpifconf['maxleasetime'])) {
809
				$dhcpdconf .= "		max-lease-time {$poolconf['maxleasetime']};\n";
810
			}
811

    
812
			// netbios-name*
813
			if (is_array($poolconf['winsserver']) && $poolconf['winsserver'][0] && ($poolconf['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
814
				$dhcpdconf .= "		option netbios-name-servers " . join(",", $poolconf['winsserver']) . ";\n";
815
				$dhcpdconf .= "		option netbios-node-type 8;\n";
816
			}
817

    
818
			// ntp-servers
819
			if (is_array($poolconf['ntpserver']) && $poolconf['ntpserver'][0] && ($poolconf['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
820
				$dhcpdconf .= "		option ntp-servers " . join(",", $poolconf['ntpserver']) . ";\n";
821
			}
822

    
823
			// tftp-server-name
824
			if (!empty($poolconf['tftp']) && ($poolconf['tftp'] != $dhcpifconf['tftp'])) {
825
				$dhcpdconf .= "		option tftp-server-name \"{$poolconf['tftp']}\";\n";
826
			}
827

    
828
			// ldap-server
829
			if (!empty($poolconf['ldap']) && ($poolconf['ldap'] != $dhcpifconf['ldap'])) {
830
				$dhcpdconf .= "		option ldap-server \"{$poolconf['ldap']}\";\n";
831
			}
832

    
833
			// net boot information
834
			if (isset($poolconf['netboot'])) {
835
				if (!empty($poolconf['nextserver']) && ($poolconf['nextserver'] != $dhcpifconf['nextserver'])) {
836
					$dhcpdconf .= "		next-server {$poolconf['nextserver']};\n";
837
				}
838
				if (!empty($poolconf['filename']) && ($poolconf['filename'] != $dhcpifconf['filename'])) {
839
					$dhcpdconf .= "		filename \"{$poolconf['filename']}\";\n";
840
				}
841
				if (!empty($poolconf['rootpath']) && ($poolconf['rootpath'] != $dhcpifconf['rootpath'])) {
842
					$dhcpdconf .= "		option root-path \"{$poolconf['rootpath']}\";\n";
843
				}
844
			}
845
			$dhcpdconf .= "		range {$poolconf['range']['from']} {$poolconf['range']['to']};\n";
846
			$dhcpdconf .= "	}\n\n";
847
		}
848
// End of settings inside pools
849

    
850
		if ($dhcpifconf['gateway'] && $dhcpifconf['gateway'] != "none") {
851
			$routers = $dhcpifconf['gateway'];
852
			$add_routers = true;
853
		} elseif ($dhcpifconf['gateway'] == "none") {
854
			$add_routers = false;
855
		} else {
856
			$add_routers = $enable_add_routers;
857
			$routers = $ifcfgip;
858
		}
859
		if ($add_routers) {
860
			$dhcpdconf .= "	option routers {$routers};\n";
861
		}
862

    
863
		$dhcpdconf .= <<<EOD
864
$dnscfg
865

    
866
EOD;
867
		// default-lease-time
868
		if ($dhcpifconf['defaultleasetime']) {
869
			$dhcpdconf .= "	default-lease-time {$dhcpifconf['defaultleasetime']};\n";
870
		}
871

    
872
		// max-lease-time
873
		if ($dhcpifconf['maxleasetime']) {
874
			$dhcpdconf .= "	max-lease-time {$dhcpifconf['maxleasetime']};\n";
875
		}
876

    
877
		// netbios-name*
878
		if (is_array($dhcpifconf['winsserver']) && $dhcpifconf['winsserver'][0]) {
879
			$dhcpdconf .= "	option netbios-name-servers " . join(",", $dhcpifconf['winsserver']) . ";\n";
880
			$dhcpdconf .= "	option netbios-node-type 8;\n";
881
		}
882

    
883
		// ntp-servers
884
		if (is_array($dhcpifconf['ntpserver']) && $dhcpifconf['ntpserver'][0]) {
885
			$dhcpdconf .= "	option ntp-servers " . join(",", $dhcpifconf['ntpserver']) . ";\n";
886
		}
887

    
888
		// tftp-server-name
889
		if ($dhcpifconf['tftp'] <> "") {
890
			$dhcpdconf .= "	option tftp-server-name \"{$dhcpifconf['tftp']}\";\n";
891
		}
892

    
893
		// Handle option, number rowhelper values
894
		$dhcpdconf .= "\n";
895
		if ($dhcpifconf['numberoptions']['item']) {
896
			foreach ($dhcpifconf['numberoptions']['item'] as $itemidx => $item) {
897
				if (empty($item['type']) || $item['type'] == "text") {
898
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} \"{$item['value']}\";\n";
899
				} else {
900
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} {$item['value']};\n";
901
				}
902
			}
903
		}
904

    
905
		// ldap-server
906
		if ($dhcpifconf['ldap'] <> "") {
907
			$dhcpdconf .= "	option ldap-server \"{$dhcpifconf['ldap']}\";\n";
908
		}
909

    
910
		// net boot information
911
		if (isset($dhcpifconf['netboot'])) {
912
			if ($dhcpifconf['nextserver'] <> "") {
913
				$dhcpdconf .= "	next-server {$dhcpifconf['nextserver']};\n";
914
			}
915
			if (!empty($dhcpifconf['filename']) && !empty($dhcpifconf['filename32']) && !empty($dhcpifconf['filename64'])) {
916
				$dhcpdconf .= "	if option arch = 00:06 {\n";
917
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename32']}\";\n";
918
				$dhcpdconf .= "	} else if option arch = 00:07 {\n";
919
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename64']}\";\n";
920
				$dhcpdconf .= "	} else {\n";
921
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename']}\";\n";
922
				$dhcpdconf .= "	}\n\n";
923
			} elseif (!empty($dhcpifconf['filename'])) {
924
				$dhcpdconf .= "	filename \"{$dhcpifconf['filename']}\";\n";
925
			}
926
			if (!empty($dhcpifconf['rootpath'])) {
927
				$dhcpdconf .= "	option root-path \"{$dhcpifconf['rootpath']}\";\n";
928
			}
929
		}
930

    
931
		$dhcpdconf .= <<<EOD
932
}
933

    
934
EOD;
935

    
936
		/* add static mappings */
937
		if (is_array($dhcpifconf['staticmap'])) {
938

    
939
			$i = 0;
940
			foreach ($dhcpifconf['staticmap'] as $sm) {
941
				$dhcpdconf .= "host s_{$dhcpif}_{$i} {\n";
942

    
943
				if ($sm['mac']) {
944
					$dhcpdconf .= "        hardware ethernet {$sm['mac']};\n";
945
				}
946

    
947
				if ($sm['cid']) {
948
					$dhcpdconf .= "        option dhcp-client-identifier \"{$sm['cid']}\";\n";
949
				}
950

    
951
				if ($sm['ipaddr']) {
952
					$dhcpdconf .= "	fixed-address {$sm['ipaddr']};\n";
953
				}
954

    
955
				if ($sm['hostname']) {
956
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
957
					$dhhostname = str_replace(".", "_", $dhhostname);
958
					$dhcpdconf .= "	option host-name \"{$dhhostname}\";\n";
959
				}
960
				if ($sm['filename']) {
961
					$dhcpdconf .= "	filename \"{$sm['filename']}\";\n";
962
				}
963

    
964
				if ($sm['rootpath']) {
965
					$dhcpdconf .= "	option root-path \"{$sm['rootpath']}\";\n";
966
				}
967

    
968
				if ($sm['gateway'] && ($sm['gateway'] != $dhcpifconf['gateway'])) {
969
					$dhcpdconf .= "	option routers {$sm['gateway']};\n";
970
				}
971

    
972
				$smdnscfg = "";
973

    
974
				if ($sm['domain'] && ($sm['domain'] != $dhcpifconf['domain'])) {
975
					$smdnscfg .= "	option domain-name \"{$sm['domain']}\";\n";
976
				}
977

    
978
				if (!empty($sm['domainsearchlist']) && ($sm['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
979
					$smdnscfg .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $sm['domainsearchlist'])) . "\";\n";
980
				}
981

    
982
				if (isset($sm['ddnsupdate'])) {
983
					if (($sm['ddnsdomain'] <> "") && ($sm['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
984
						$pdnscfg .= "		ddns-domainname \"{$sm['ddnsdomain']}\";\n";
985
					}
986
					$pdnscfg .= "		ddns-update-style interim;\n";
987
				}
988

    
989
				if (is_array($sm['dnsserver']) && ($sm['dnsserver'][0]) && ($sm['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
990
					$smdnscfg .= "	option domain-name-servers " . join(",", $sm['dnsserver']) . ";\n";
991
				}
992
				$dhcpdconf .= "{$smdnscfg}";
993

    
994
				// default-lease-time
995
				if ($sm['defaultleasetime'] && ($sm['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) {
996
					$dhcpdconf .= "	default-lease-time {$sm['defaultleasetime']};\n";
997
				}
998

    
999
				// max-lease-time
1000
				if ($sm['maxleasetime'] && ($sm['maxleasetime'] != $dhcpifconf['maxleasetime'])) {
1001
					$dhcpdconf .= "	max-lease-time {$sm['maxleasetime']};\n";
1002
				}
1003

    
1004
				// netbios-name*
1005
				if (is_array($sm['winsserver']) && $sm['winsserver'][0] && ($sm['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
1006
					$dhcpdconf .= "	option netbios-name-servers " . join(",", $sm['winsserver']) . ";\n";
1007
					$dhcpdconf .= "	option netbios-node-type 8;\n";
1008
				}
1009

    
1010
				// ntp-servers
1011
				if (is_array($sm['ntpserver']) && $sm['ntpserver'][0] && ($sm['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
1012
					$dhcpdconf .= "	option ntp-servers " . join(",", $sm['ntpserver']) . ";\n";
1013
				}
1014

    
1015
				// tftp-server-name
1016
				if (!empty($sm['tftp']) && ($sm['tftp'] != $dhcpifconf['tftp'])) {
1017
					$dhcpdconf .= "	option tftp-server-name \"{$sm['tftp']}\";\n";
1018
				}
1019

    
1020
				$dhcpdconf .= "}\n";
1021
				$i++;
1022
			}
1023
		}
1024

    
1025
		$dhcpdifs[] = get_real_interface($dhcpif);
1026
		if ($newzone['domain-name']) {
1027
			if ($need_ddns_updates) {
1028
				$newzone['dns-servers'] = array($dhcpifconf['ddnsdomainprimary']);
1029
			}
1030
			$ddns_zones[] = $newzone;
1031
		}
1032
	}
1033

    
1034
	if ($need_ddns_updates) {
1035
		$dhcpdconf .= "ddns-update-style interim;\n";
1036
		$dhcpdconf .= "update-static-leases on;\n";
1037

    
1038
		$dhcpdconf .= dhcpdkey($dhcpifconf);
1039
		$dhcpdconf .= dhcpdzones($ddns_zones, $dhcpifconf);
1040
	}
1041

    
1042
	/* write dhcpd.conf */
1043
	if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpd.conf", $dhcpdconf)) {
1044
		printf(gettext("Error: cannot open dhcpd.conf in services_dhcpdv4_configure().%s"), "\n");
1045
		unset($dhcpdconf);
1046
		return 1;
1047
	}
1048
	unset($dhcpdconf);
1049

    
1050
	/* create an empty leases database */
1051
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases")) {
1052
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases");
1053
	}
1054

    
1055
	/* make sure there isn't a stale dhcpd.pid file, which can make dhcpd fail to start.   */
1056
	/* if we get here, dhcpd has been killed and is not started yet                        */
1057
	unlink_if_exists("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid");
1058

    
1059
	/* fire up dhcpd in a chroot */
1060
	if (count($dhcpdifs) > 0) {
1061
		mwexec("/usr/local/sbin/dhcpd -user dhcpd -group _dhcp -chroot {$g['dhcpd_chroot_path']} -cf /etc/dhcpd.conf -pf {$g['varrun_path']}/dhcpd.pid " .
1062
			join(" ", $dhcpdifs));
1063
	}
1064

    
1065
	if (platform_booting()) {
1066
		print "done.\n";
1067
	}
1068

    
1069
	return 0;
1070
}
1071

    
1072
function dhcpdkey($dhcpifconf) {
1073
	$dhcpdconf = "";
1074
	if ($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "") {
1075
		$dhcpdconf .= "key {$dhcpifconf['ddnsdomainkeyname']} {\n";
1076
		$dhcpdconf .= "	algorithm hmac-md5;\n";
1077
		$dhcpdconf .= "	secret {$dhcpifconf['ddnsdomainkey']};\n";
1078
		$dhcpdconf .= "}\n";
1079
	}
1080

    
1081
	return $dhcpdconf;
1082
}
1083

    
1084
function dhcpdzones($ddns_zones, $dhcpifconf) {
1085
	$dhcpdconf = "";
1086

    
1087
	if (is_array($ddns_zones)) {
1088
		$added_zones = array();
1089
		foreach ($ddns_zones as $zone) {
1090
			if (!is_array($zone) || empty($zone) || !is_array($zone['dns-servers'])) {
1091
				continue;
1092
			}
1093
			$primary = $zone['dns-servers'][0];
1094
			$secondary = empty($zone['dns-servers'][1]) ? "" : $zone['dns-servers'][1];
1095

    
1096
			// Make sure we aren't using any invalid or IPv6 DNS servers.
1097
			if (!is_ipaddrv4($primary)) {
1098
				if (is_ipaddrv4($secondary)) {
1099
					$primary = $secondary;
1100
					$secondary = "";
1101
				} else {
1102
					continue;
1103
				}
1104
			}
1105

    
1106
			// We don't need to add zones multiple times.
1107
			if ($zone['domain-name'] && !in_array($zone['domain-name'], $added_zones)) {
1108
				$dhcpdconf .= "zone {$zone['domain-name']}. {\n";
1109
				$dhcpdconf .= "	primary {$primary};\n";
1110
				if (is_ipaddrv4($secondary)) {
1111
					$dhcpdconf .= "	secondary {$secondary};\n";
1112
				}
1113
				if ($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "") {
1114
					$dhcpdconf .= "	key {$dhcpifconf['ddnsdomainkeyname']};\n";
1115
				}
1116
				$dhcpdconf .= "}\n";
1117
				$added_zones[] = $zone['domain-name'];
1118
			}
1119
			if ($zone['ptr-domain'] && !in_array($zone['ptr-domain'], $added_zones)) {
1120
				$dhcpdconf .= "zone {$zone['ptr-domain']} {\n";
1121
				$dhcpdconf .= "	primary {$primary};\n";
1122
				if (is_ipaddrv4($secondary)) {
1123
					$dhcpdconf .= "	secondary {$secondary};\n";
1124
				}
1125
				if ($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "") {
1126
					$dhcpdconf .= "	key {$dhcpifconf['ddnsdomainkeyname']};\n";
1127
				}
1128
				$dhcpdconf .= "}\n";
1129
				$added_zones[] = $zone['ptr-domain'];
1130
			}
1131
		}
1132
	}
1133

    
1134
	return $dhcpdconf;
1135
}
1136

    
1137
function services_dhcpdv6_configure($blacklist = array()) {
1138
	global $config, $g;
1139

    
1140
	if ($g['services_dhcp_server_enable'] == false) {
1141
		return;
1142
	}
1143

    
1144
	if (isset($config['system']['developerspew'])) {
1145
		$mt = microtime();
1146
		echo "services_dhcpd_configure($if) being called $mt\n";
1147
	}
1148

    
1149
	/* kill any running dhcpd */
1150
	if (isvalidpid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid")) {
1151
		killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid");
1152
	}
1153
	if (isvalidpid("{$g['varrun_path']}/dhcpleases6.pid")) {
1154
		killbypid("{$g['varrun_path']}/dhcpleases6.pid");
1155
	}
1156

    
1157
	/* DHCP enabled on any interfaces? */
1158
	if (!is_dhcpv6_server_enabled()) {
1159
		return 0;
1160
	}
1161

    
1162
	if (platform_booting()) {
1163
		if ($g['platform'] != "pfSense") {
1164
			/* restore the leases, if we have them */
1165
			if (file_exists("{$g['cf_conf_path']}/dhcp6leases.tgz")) {
1166
				$dhcprestore = "";
1167
				$dhcpreturn = "";
1168
				exec("cd /;LANG=C /usr/bin/tar -xzf {$g['cf_conf_path']}/dhcp6leases.tgz 2>&1", $dhcprestore, $dhcpreturn);
1169
				$dhcprestore = implode(" ", $dhcprestore);
1170
				if ($dhcpreturn <> 0) {
1171
					log_error("DHCP leases v6 restore failed exited with $dhcpreturn, the error is: $dhcprestore\n");
1172
				}
1173
			}
1174
		}
1175
	}
1176

    
1177
	$syscfg = $config['system'];
1178
	if (!is_array($config['dhcpdv6'])) {
1179
		$config['dhcpdv6'] = array();
1180
	}
1181
	$dhcpdv6cfg = $config['dhcpdv6'];
1182
	$Iflist = get_configured_interface_list();
1183
	$Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces());
1184

    
1185

    
1186
	if (platform_booting()) {
1187
		echo "Starting DHCPv6 service...";
1188
	} else {
1189
		sleep(1);
1190
	}
1191

    
1192
	/* we add a fake entry for interfaces that are set to track6 another WAN */
1193
	foreach ($Iflist as $ifname) {
1194
		/* Do not put in the config an interface which is down */
1195
		if (isset($blacklist[$ifname])) {
1196
			continue;
1197
		}
1198
		if (!empty($config['interfaces'][$ifname]['track6-interface'])) {
1199
			$realif = get_real_interface($ifname, "inet6");
1200
			$ifcfgipv6 = get_interface_ipv6($ifname);
1201
			if (!is_ipaddrv6($ifcfgipv6)) {
1202
				continue;
1203
			}
1204
			$ifcfgipv6 = Net_IPv6::getNetmask($ifcfgipv6, 64);
1205
			$trackifname = $config['interfaces'][$ifname]['track6-interface'];
1206
			$trackcfg = $config['interfaces'][$trackifname];
1207
			$pdlen = calculate_ipv6_delegation_length($trackifname);
1208
			$ifcfgipv6arr =explode(":", $ifcfgipv6);
1209
			$dhcpdv6cfg[$ifname] = array();
1210
			$dhcpdv6cfg[$ifname]['enable'] = true;
1211
			/* range */
1212
			$ifcfgipv6arr[7] = "1000";
1213
			$dhcpdv6cfg[$ifname]['range'] = array();
1214
			$dhcpdv6cfg[$ifname]['range']['from'] = Net_IPv6::compress(implode(":", $ifcfgipv6arr));
1215
			$ifcfgipv6arr[7] = "2000";
1216
			$dhcpdv6cfg[$ifname]['range']['to'] = Net_IPv6::compress(implode(":", $ifcfgipv6arr));
1217
			/* prefix length > 0? We can add dhcp6 prefix delegation server */
1218
			if ($pdlen > 2) {
1219
				$pdlenmax = $pdlen;
1220
				$pdlenhalf = $pdlenmax -1;
1221
				$pdlenmin = (64 - ceil($pdlenhalf / 4));
1222
				$dhcpdv6cfg[$ifname]['prefixrange'] = array();
1223
				$dhcpdv6cfg[$ifname]['prefixrange']['prefixlength'] = $pdlenmin;
1224

    
1225
				/* set the delegation start to half the current address block */
1226
				$range = Net_IPv6::parseAddress($ifcfgipv6, (64 - $pdlenmax));
1227
				$range['start'] = Net_IPv6::getNetmask($range['end'], (64 - $pdlenhalf));
1228

    
1229
				/* set the end range to a multiple of the prefix delegation size, required by dhcpd */
1230
				$range = Net_IPv6::parseAddress($range['end'], (64 - $pdlenhalf));
1231
				$range['end'] = Net_IPv6::getNetmask($range['end'], (64 - round($pdlen / 2)));
1232

    
1233
				$dhcpdv6cfg[$ifname]['prefixrange']['from'] = Net_IPv6::compress($range['start']);
1234
				$dhcpdv6cfg[$ifname]['prefixrange']['to'] = Net_IPv6::compress($range['end']);
1235
			}
1236
			$dhcpdv6cfg[$ifname]['dns6ip'] = get_interface_ipv6($ifname);
1237
		}
1238
	}
1239

    
1240
	$custoptionsv6 = "";
1241
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1242
		if (is_array($dhcpv6ifconf['numberoptions']) && is_array($dhcpv6ifconf['numberoptions']['item'])) {
1243
			foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
1244
				$custoptionsv6 .= "option custom-{$dhcpv6if}-{$itemv6idx} code {$itemv6['number']} = text;\n";
1245
			}
1246
		}
1247
	}
1248

    
1249
	if (isset($dhcpv6ifconf['netboot']) && !empty($dhcpv6ifconf['bootfile_url'])) {
1250
		$custoptionsv6 .= "option dhcp6.bootfile-url code 59 = string;\n";
1251
	}
1252

    
1253
	$dhcpdv6conf = <<<EOD
1254

    
1255
option domain-name "{$syscfg['domain']}";
1256
option ldap-server code 95 = text;
1257
option domain-search-list code 119 = text;
1258
{$custoptionsv6}
1259
default-lease-time 7200;
1260
max-lease-time 86400;
1261
log-facility local7;
1262
one-lease-per-client true;
1263
deny duplicates;
1264
ping-check true;
1265
update-conflict-detection false;
1266

    
1267
EOD;
1268

    
1269
	if (!isset($dhcpv6ifconf['disableauthoritative'])) {
1270
		$dhcpdv6conf .= "authoritative;\n";
1271
	}
1272

    
1273
	if (isset($dhcpv6ifconf['alwaysbroadcast'])) {
1274
		$dhcpdv6conf .= "always-broadcast on\n";
1275
	}
1276

    
1277
	$dhcpdv6ifs = array();
1278

    
1279
	$dhcpv6num = 0;
1280
	$nsupdate = false;
1281

    
1282
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1283

    
1284
		$ddns_zones = array();
1285

    
1286
		$ifcfgv6 = $config['interfaces'][$dhcpv6if];
1287

    
1288
		if (!isset($dhcpv6ifconf['enable']) || !isset($Iflist[$dhcpv6if]) || !isset($ifcfgv6['enable'])) {
1289
			continue;
1290
		}
1291
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1292
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
1293
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1294

    
1295
		if ($is_olsr_enabled == true) {
1296
			if ($dhcpv6ifconf['netmask']) {
1297
				$subnetmask = gen_subnet_maskv6($dhcpv6ifconf['netmask']);
1298
			}
1299
		}
1300

    
1301
		$dnscfgv6 = "";
1302

    
1303
		if ($dhcpv6ifconf['domain']) {
1304
			$dnscfgv6 .= "	option domain-name \"{$dhcpv6ifconf['domain']}\";\n";
1305
		}
1306

    
1307
		if ($dhcpv6ifconf['domainsearchlist'] <> "") {
1308
			$dnscfgv6 .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpv6ifconf['domainsearchlist'])) . "\";\n";
1309
		}
1310

    
1311
		if (isset($dhcpv6ifconf['ddnsupdate'])) {
1312
			if ($dhcpv6ifconf['ddnsdomain'] <> "") {
1313
				$dnscfgv6 .= "	ddns-domainname \"{$dhcpv6ifconf['ddnsdomain']}\";\n";
1314
			}
1315
			$dnscfgv6 .= "	ddns-update-style interim;\n";
1316
			$nsupdate = true;
1317
		}
1318

    
1319
		if (is_array($dhcpv6ifconf['dnsserver']) && ($dhcpv6ifconf['dnsserver'][0])) {
1320
			$dnscfgv6 .= "	option dhcp6.name-servers " . join(",", $dhcpv6ifconf['dnsserver']) . ";";
1321
		} else if (((isset($config['dnsmasq']['enable'])) || isset($config['unbound']['enable'])) && (is_ipaddrv6($ifcfgipv6))) {
1322
			$dnscfgv6 .= "	option dhcp6.name-servers {$ifcfgipv6};";
1323
		} else if (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
1324
			$dns_arrv6 = array();
1325
			foreach ($syscfg['dnsserver'] as $dnsserver) {
1326
				if (is_ipaddrv6($dnsserver)) {
1327
					$dns_arrv6[] = $dnsserver;
1328
				}
1329
			}
1330
			if (!empty($dns_arrv6)) {
1331
				$dnscfgv6 .= "	option dhcp6.name-servers " . join(",", $dns_arrv6) . ";";
1332
			}
1333
		}
1334

    
1335
		if ($dhcpv6ifconf['domain']) {
1336
			$newzone = array();
1337
			$newzone['domain-name'] = $dhcpv6ifconf['domain'];
1338
			$newzone['dns-servers'][] = $dhcpv6ifconf['ddnsdomainprimary'];
1339
			$ddns_zones[] = $newzone;
1340
		}
1341

    
1342
		if (is_ipaddrv6($ifcfgipv6)) {
1343
			$dhcpdv6conf .= "subnet6 {$subnetv6}/{$ifcfgsnv6}";
1344
		} else {
1345
			$subnet6 = gen_subnetv6($dhcpv6ifconf['range']['from'], "64");
1346
			$dhcpdv6conf .= "subnet6 {$subnet6}/64";
1347
		}
1348
		$dhcpdv6conf .= " {\n";
1349

    
1350
		$dhcpdv6conf .= <<<EOD
1351
	range6 {$dhcpv6ifconf['range']['from']} {$dhcpv6ifconf['range']['to']};
1352
$dnscfgv6
1353

    
1354
EOD;
1355

    
1356
		if (is_ipaddrv6($dhcpv6ifconf['prefixrange']['from']) && is_ipaddrv6($dhcpv6ifconf['prefixrange']['to'])) {
1357
			$dhcpdv6conf .= "	prefix6 {$dhcpv6ifconf['prefixrange']['from']} {$dhcpv6ifconf['prefixrange']['to']} /{$dhcpv6ifconf['prefixrange']['prefixlength']};\n";
1358
		}
1359
		if (is_ipaddrv6($dhcpv6ifconf['dns6ip'])) {
1360
			$dhcpdv6conf .= "	option dhcp6.name-servers {$dhcpv6ifconf['dns6ip']};\n";
1361
		}
1362
		// default-lease-time
1363
		if ($dhcpv6ifconf['defaultleasetime']) {
1364
			$dhcpdv6conf .= "	default-lease-time {$dhcpv6ifconf['defaultleasetime']};\n";
1365
		}
1366

    
1367
		// max-lease-time
1368
		if ($dhcpv6ifconf['maxleasetime']) {
1369
			$dhcpdv6conf .= "	max-lease-time {$dhcpv6ifconf['maxleasetime']};\n";
1370
		}
1371

    
1372
		// ntp-servers
1373
		if (is_array($dhcpv6ifconf['ntpserver']) && $dhcpv6ifconf['ntpserver'][0]) {
1374
			$ntpservers = array();
1375
			foreach ($dhcpv6ifconf['ntpserver'] as $ntpserver) {
1376
				if (is_ipaddrv6($ntpserver)) {
1377
					$ntpservers[] = $ntpserver;
1378
				}
1379
			}
1380
			if (count($ntpservers) > 0) {
1381
				$dhcpdv6conf .= "       option dhcp6.sntp-servers " . join(",", $dhcpv6ifconf['ntpserver']) . ";\n";
1382
			}
1383
		}
1384
		// tftp-server-name
1385
		/* Needs ISC DHCPD support
1386
		 if ($dhcpv6ifconf['tftp'] <> "") {
1387
			$dhcpdv6conf .= "	option tftp-server-name \"{$dhcpv6ifconf['tftp']}\";\n";
1388
		 }
1389
		*/
1390

    
1391
		// Handle option, number rowhelper values
1392
		$dhcpdv6conf .= "\n";
1393
		if ($dhcpv6ifconf['numberoptions']['item']) {
1394
			foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
1395
				$dhcpdv6conf .= "	option custom-{$dhcpv6if}-{$itemv6idx} \"{$itemv6['value']}\";\n";
1396
			}
1397
		}
1398

    
1399
		// ldap-server
1400
		if ($dhcpv6ifconf['ldap'] <> "") {
1401
			$dhcpdv6conf .= "	option ldap-server \"{$dhcpv6ifconf['ldap']}\";\n";
1402
		}
1403

    
1404
		// net boot information
1405
		if (isset($dhcpv6ifconf['netboot'])) {
1406
			if (!empty($dhcpv6ifconf['bootfile_url'])) {
1407
				$dhcpdv6conf .= "	option dhcp6.bootfile-url \"{$dhcpv6ifconf['bootfile_url']}\";\n";
1408
			}
1409
		}
1410

    
1411
		$dhcpdv6conf .= "}\n";
1412

    
1413
		/* add static mappings */
1414
		/* Needs to use DUID */
1415
		if (is_array($dhcpv6ifconf['staticmap'])) {
1416
			$i = 0;
1417
			foreach ($dhcpv6ifconf['staticmap'] as $sm) {
1418
				$dhcpdv6conf .= <<<EOD
1419
host s_{$dhcpv6if}_{$i} {
1420
	host-identifier option dhcp6.client-id {$sm['duid']};
1421

    
1422
EOD;
1423
				if ($sm['ipaddrv6']) {
1424
					$dhcpdv6conf .= "	fixed-address6 {$sm['ipaddrv6']};\n";
1425
				}
1426

    
1427
				if ($sm['hostname']) {
1428
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
1429
					$dhhostname = str_replace(".", "_", $dhhostname);
1430
					$dhcpdv6conf .= "	option host-name {$dhhostname};\n";
1431
				}
1432
				if ($sm['filename']) {
1433
					$dhcpdv6conf .= "	filename \"{$sm['filename']}\";\n";
1434
				}
1435

    
1436
				if ($sm['rootpath']) {
1437
					$dhcpdv6conf .= "	option root-path \"{$sm['rootpath']}\";\n";
1438
				}
1439

    
1440
				$dhcpdv6conf .= "}\n";
1441
				$i++;
1442
			}
1443
		}
1444

    
1445
		if ($dhcpv6ifconf['domain']) {
1446
			$dhcpdv6conf .= dhcpdkey($dhcpv6ifconf);
1447
			$dhcpdv6conf .= dhcpdzones($ddns_zones, $dhcpv6ifconf);
1448
		}
1449

    
1450
		if ($config['dhcpdv6'][$dhcpv6if]['ramode'] <> "unmanaged" && isset($config['interfaces'][$dhcpv6if]['enable'])) {
1451
			if (preg_match("/poes/si", $dhcpv6if)) {
1452
				/* magic here */
1453
				$dhcpdv6ifs = array_merge($dhcpdv6ifs, get_pppoes_child_interfaces($dhcpv6if));
1454
			} else {
1455
				$realif = get_real_interface($dhcpv6if, "inet6");
1456
				if (stristr("$realif", "bridge")) {
1457
					$mac = get_interface_mac($realif);
1458
					$v6address = generate_ipv6_from_mac($mac);
1459
					/* Create link local address for bridges */
1460
					mwexec("/sbin/ifconfig {$realif} inet6 {$v6address}");
1461
				}
1462
				$realif = escapeshellcmd($realif);
1463
				$dhcpdv6ifs[] = $realif;
1464
			}
1465
		}
1466
	}
1467

    
1468
	if ($nsupdate) {
1469
		$dhcpdv6conf .= "ddns-update-style interim;\n";
1470
	} else {
1471
		$dhcpdv6conf .= "ddns-update-style none;\n";
1472
	}
1473

    
1474
	/* write dhcpdv6.conf */
1475
	if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf", $dhcpdv6conf)) {
1476
		log_error("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n");
1477
		if (platform_booting()) {
1478
			printf("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n");
1479
		}
1480
		unset($dhcpdv6conf);
1481
		return 1;
1482
	}
1483
	unset($dhcpdv6conf);
1484

    
1485
	/* create an empty leases v6 database */
1486
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases")) {
1487
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases");
1488
	}
1489

    
1490
	/* make sure there isn't a stale dhcpdv6.pid file, which may make dhcpdv6 fail to start.  */
1491
	/* if we get here, dhcpdv6 has been killed and is not started yet                         */
1492
	unlink_if_exists("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid");
1493

    
1494
	/* fire up dhcpd in a chroot */
1495
	if (count($dhcpdv6ifs) > 0) {
1496
		mwexec("/usr/local/sbin/dhcpd -6 -user dhcpd -group _dhcp -chroot {$g['dhcpd_chroot_path']} -cf /etc/dhcpdv6.conf -pf {$g['varrun_path']}/dhcpdv6.pid " .
1497
			join(" ", $dhcpdv6ifs));
1498
		mwexec("/usr/local/sbin/dhcpleases6 -c \"/usr/local/bin/php -f /usr/local/sbin/prefixes.php|/bin/sh\" -l {$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases");
1499
	}
1500
	if (platform_booting()) {
1501
		print gettext("done.") . "\n";
1502
	}
1503

    
1504
	return 0;
1505
}
1506

    
1507
function services_igmpproxy_configure() {
1508
	global $config, $g;
1509

    
1510
	/* kill any running igmpproxy */
1511
	killbyname("igmpproxy");
1512

    
1513
	if (!is_array($config['igmpproxy']['igmpentry']) || (count($config['igmpproxy']['igmpentry']) == 0)) {
1514
		return 1;
1515
	}
1516

    
1517
	$iflist = get_configured_interface_list();
1518

    
1519
	$igmpconf = <<<EOD
1520

    
1521
##------------------------------------------------------
1522
## Enable Quickleave mode (Sends Leave instantly)
1523
##------------------------------------------------------
1524
quickleave
1525

    
1526
EOD;
1527

    
1528
	foreach ($config['igmpproxy']['igmpentry'] as $igmpcf) {
1529
		unset($iflist[$igmpcf['ifname']]);
1530
		$realif = get_real_interface($igmpcf['ifname']);
1531
		if (empty($igmpcf['threshold'])) {
1532
			$threshld = 1;
1533
		} else {
1534
			$threshld = $igmpcf['threshold'];
1535
		}
1536
		$igmpconf .= "phyint {$realif} {$igmpcf['type']} ratelimit 0 threshold {$threshld}\n";
1537

    
1538
		if ($igmpcf['address'] <> "") {
1539
			$item = explode(" ", $igmpcf['address']);
1540
			foreach ($item as $iww) {
1541
				$igmpconf .= "altnet {$iww}\n";
1542
			}
1543
		}
1544
		$igmpconf .= "\n";
1545
	}
1546
	foreach ($iflist as $ifn) {
1547
		$realif = get_real_interface($ifn);
1548
		$igmpconf .= "phyint {$realif} disabled\n";
1549
	}
1550
	$igmpconf .= "\n";
1551

    
1552
	$igmpfl = fopen($g['tmp_path'] . "/igmpproxy.conf", "w");
1553
	if (!$igmpfl) {
1554
		log_error(gettext("Could not write Igmpproxy configuration file!"));
1555
		return;
1556
	}
1557
	fwrite($igmpfl, $igmpconf);
1558
	fclose($igmpfl);
1559
	unset($igmpconf);
1560

    
1561
	/* NOTE: -d4 means everything LOG_WARNING and smaller */
1562
	mwexec("/usr/local/sbin/igmpproxy -d4 -c {$g['tmp_path']}/igmpproxy.conf");
1563
	log_error(gettext("Started IGMP proxy service."));
1564

    
1565
	return 0;
1566
}
1567

    
1568
function services_dhcrelay_configure() {
1569
	global $config, $g;
1570
	if ($g['platform'] == 'jail') {
1571
		return;
1572
	}
1573
	if (isset($config['system']['developerspew'])) {
1574
		$mt = microtime();
1575
		echo "services_dhcrelay_configure() being called $mt\n";
1576
	}
1577

    
1578
	/* kill any running dhcrelay */
1579
	killbypid("{$g['varrun_path']}/dhcrelay.pid");
1580

    
1581
	$dhcrelaycfg =& $config['dhcrelay'];
1582

    
1583
	/* DHCPRelay enabled on any interfaces? */
1584
	if (!isset($dhcrelaycfg['enable'])) {
1585
		return 0;
1586
	}
1587

    
1588
	if (platform_booting()) {
1589
		echo gettext("Starting DHCP relay service...");
1590
	} else {
1591
		sleep(1);
1592
	}
1593

    
1594
	$iflist = get_configured_interface_list();
1595

    
1596
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1597
	foreach ($dhcifaces as $dhcrelayif) {
1598
		if (!isset($iflist[$dhcrelayif]) ||
1599
		    link_interface_to_bridge($dhcrelayif)) {
1600
			continue;
1601
		}
1602

    
1603
		if (is_ipaddr(get_interface_ip($dhcrelayif))) {
1604
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1605
		}
1606
	}
1607

    
1608
	/*
1609
	 * In order for the relay to work, it needs to be active
1610
	 * on the interface in which the destination server sits.
1611
	 */
1612
	$srvips = explode(",", $dhcrelaycfg['server']);
1613
	if (!is_array($srvips)) {
1614
		log_error("No destination ip has been configured!");
1615
		return;
1616
	}
1617

    
1618
	foreach ($srvips as $srcidx => $srvip) {
1619
		unset($destif);
1620
		foreach ($iflist as $ifname) {
1621
			$subnet = get_interface_ip($ifname);
1622
			if (!is_ipaddr($subnet)) {
1623
				continue;
1624
			}
1625
			$subnet .=  "/" . get_interface_subnet($ifname);
1626
			if (ip_in_subnet($srvip, $subnet)) {
1627
				$destif = get_real_interface($ifname);
1628
				break;
1629
			}
1630
		}
1631
		if (!isset($destif)) {
1632
			foreach (get_staticroutes() as $rtent) {
1633
				if (ip_in_subnet($srvip, $rtent['network'])) {
1634
					$a_gateways = return_gateways_array(true);
1635
					$destif = $a_gateways[$rtent['gateway']]['interface'];
1636
					break;
1637
				}
1638
			}
1639
		}
1640

    
1641
		if (!isset($destif)) {
1642
			/* Create a array from the existing route table */
1643
			exec("/usr/bin/netstat -rnWf inet", $route_str);
1644
			array_shift($route_str);
1645
			array_shift($route_str);
1646
			array_shift($route_str);
1647
			array_shift($route_str);
1648
			$route_arr = array();
1649
			foreach ($route_str as $routeline) {
1650
				$items = preg_split("/[ ]+/i", $routeline);
1651
				if (is_subnetv4($items[0])) {
1652
					$subnet = $items[0];
1653
				} elseif (is_ipaddrv4($items[0])) {
1654
					$subnet = "{$items[0]}/32";
1655
				} else {
1656
					// Not a subnet or IP address, skip to the next line.
1657
					continue;
1658
				}
1659
				if (ip_in_subnet($srvip, $subnet)) {
1660
					$destif = trim($items[6]);
1661
					break;
1662
				}
1663
			}
1664
		}
1665

    
1666
		if (!isset($destif)) {
1667
			if (is_array($config['gateways']['gateway_item'])) {
1668
				foreach ($config['gateways']['gateway_item'] as $gateway) {
1669
					if (isset($gateway['defaultgw'])) {
1670
						$destif = get_real_interface($gateway['interface']);
1671
						break;
1672
					}
1673
				}
1674
			} else {
1675
				$destif = get_real_interface("wan");
1676
			}
1677
		}
1678

    
1679
		if (!empty($destif)) {
1680
			$dhcrelayifs[] = $destif;
1681
		}
1682
	}
1683
	$dhcrelayifs = array_unique($dhcrelayifs);
1684

    
1685
	/* fire up dhcrelay */
1686
	if (empty($dhcrelayifs)) {
1687
		log_error("No suitable interface found for running dhcrelay!");
1688
		return; /* XXX */
1689
	}
1690

    
1691
	$cmd = "/usr/local/sbin/dhcrelay -i " .  implode(" -i ", $dhcrelayifs);
1692

    
1693
	if (isset($dhcrelaycfg['agentoption'])) {
1694
		$cmd .=  " -a -m replace";
1695
	}
1696

    
1697
	$cmd .= " " . implode(" ", $srvips);
1698
	mwexec($cmd);
1699
	unset($cmd);
1700

    
1701
	return 0;
1702
}
1703

    
1704
function services_dhcrelay6_configure() {
1705
	global $config, $g;
1706
	if ($g['platform'] == 'jail') {
1707
		return;
1708
	}
1709
	if (isset($config['system']['developerspew'])) {
1710
		$mt = microtime();
1711
		echo "services_dhcrelay6_configure() being called $mt\n";
1712
	}
1713

    
1714
	/* kill any running dhcrelay */
1715
	killbypid("{$g['varrun_path']}/dhcrelay6.pid");
1716

    
1717
	$dhcrelaycfg =& $config['dhcrelay6'];
1718

    
1719
	/* DHCPv6 Relay enabled on any interfaces? */
1720
	if (!isset($dhcrelaycfg['enable'])) {
1721
		return 0;
1722
	}
1723

    
1724
	if (platform_booting()) {
1725
		echo gettext("Starting DHCPv6 relay service...");
1726
	} else {
1727
		sleep(1);
1728
	}
1729

    
1730
	$iflist = get_configured_interface_list();
1731

    
1732
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1733
	foreach ($dhcifaces as $dhcrelayif) {
1734
		if (!isset($iflist[$dhcrelayif]) ||
1735
		    link_interface_to_bridge($dhcrelayif)) {
1736
			continue;
1737
		}
1738

    
1739
		if (is_ipaddrv6(get_interface_ipv6($dhcrelayif))) {
1740
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1741
		}
1742
	}
1743
	$dhcrelayifs = array_unique($dhcrelayifs);
1744

    
1745
	/*
1746
	 * In order for the relay to work, it needs to be active
1747
	 * on the interface in which the destination server sits.
1748
	 */
1749
	$srvips = explode(",", $dhcrelaycfg['server']);
1750
	$srvifaces = array();
1751
	foreach ($srvips as $srcidx => $srvip) {
1752
		unset($destif);
1753
		foreach ($iflist as $ifname) {
1754
			$subnet = get_interface_ipv6($ifname);
1755
			if (!is_ipaddrv6($subnet)) {
1756
				continue;
1757
			}
1758
			$subnet .=  "/" . get_interface_subnetv6($ifname);
1759
			if (ip_in_subnet($srvip, $subnet)) {
1760
				$destif = get_real_interface($ifname);
1761
				break;
1762
			}
1763
		}
1764
		if (!isset($destif)) {
1765
			if (is_array($config['staticroutes']['route'])) {
1766
				foreach ($config['staticroutes']['route'] as $rtent) {
1767
					if (ip_in_subnet($srvip, $rtent['network'])) {
1768
						$a_gateways = return_gateways_array(true);
1769
						$destif = $a_gateways[$rtent['gateway']]['interface'];
1770
						break;
1771
					}
1772
				}
1773
			}
1774
		}
1775

    
1776
		if (!isset($destif)) {
1777
			/* Create a array from the existing route table */
1778
			exec("/usr/bin/netstat -rnWf inet6", $route_str);
1779
			array_shift($route_str);
1780
			array_shift($route_str);
1781
			array_shift($route_str);
1782
			array_shift($route_str);
1783
			$route_arr = array();
1784
			foreach ($route_str as $routeline) {
1785
				$items = preg_split("/[ ]+/i", $routeline);
1786
				if (ip_in_subnet($srvip, $items[0])) {
1787
					$destif = trim($items[6]);
1788
					break;
1789
				}
1790
			}
1791
		}
1792

    
1793
		if (!isset($destif)) {
1794
			if (is_array($config['gateways']['gateway_item'])) {
1795
				foreach ($config['gateways']['gateway_item'] as $gateway) {
1796
					if (isset($gateway['defaultgw'])) {
1797
						$destif = get_real_interface($gateway['interface']);
1798
						break;
1799
					}
1800
				}
1801
			} else {
1802
				$destif = get_real_interface("wan");
1803
			}
1804
		}
1805

    
1806
		if (!empty($destif)) {
1807
			$srvifaces[] = "{$srvip}%{$destif}";
1808
		}
1809
	}
1810

    
1811
	/* fire up dhcrelay */
1812
	if (empty($dhcrelayifs) || empty($srvifaces)) {
1813
		log_error("No suitable interface found for running dhcrelay -6!");
1814
		return; /* XXX */
1815
	}
1816

    
1817
	$cmd = "/usr/local/sbin/dhcrelay -6 -pf \"{$g['varrun_path']}/dhcrelay6.pid\"";
1818
	foreach ($dhcrelayifs as $dhcrelayif) {
1819
		$cmd .= " -l {$dhcrelayif}";
1820
	}
1821
	foreach ($srvifaces as $srviface) {
1822
		$cmd .= " -u \"{$srviface}\"";
1823
	}
1824
	mwexec($cmd);
1825
	unset($cmd);
1826

    
1827
	return 0;
1828
}
1829

    
1830
function services_dyndns_configure_client($conf) {
1831

    
1832
	if (!isset($conf['enable'])) {
1833
		return;
1834
	}
1835

    
1836
	/* load up the dyndns.class */
1837
	require_once("dyndns.class");
1838

    
1839
	$dns = new updatedns($dnsService = $conf['type'],
1840
		$dnsHost = $conf['host'],
1841
		$dnsUser = $conf['username'],
1842
		$dnsPass = $conf['password'],
1843
		$dnsWildcard = $conf['wildcard'],
1844
		$dnsMX = $conf['mx'],
1845
		$dnsIf = "{$conf['interface']}",
1846
		$dnsBackMX = NULL,
1847
		$dnsServer = NULL,
1848
		$dnsPort = NULL,
1849
		$dnsUpdateURL = "{$conf['updateurl']}",
1850
		$forceUpdate = $conf['force'],
1851
		$dnsZoneID=$conf['zoneid'],
1852
		$dnsTTL=$conf['ttl'],
1853
		$dnsResultMatch = "{$conf['resultmatch']}",
1854
		$dnsRequestIf = "{$conf['requestif']}",
1855
		$dnsID = "{$conf['id']}",
1856
		$dnsVerboseLog = $conf['verboselog'],
1857
		$curlIpresolveV4 = $conf['curl_ipresolve_v4'],
1858
		$curlSslVerifypeer = $conf['curl_ssl_verifypeer']);
1859
}
1860

    
1861
function services_dyndns_configure($int = "") {
1862
	global $config, $g;
1863
	if (isset($config['system']['developerspew'])) {
1864
		$mt = microtime();
1865
		echo "services_dyndns_configure() being called $mt\n";
1866
	}
1867

    
1868
	$dyndnscfg = $config['dyndnses']['dyndns'];
1869
	$gwgroups = return_gateway_groups_array();
1870
	if (is_array($dyndnscfg)) {
1871
		if (platform_booting()) {
1872
			echo gettext("Starting DynDNS clients...");
1873
		}
1874

    
1875
		foreach ($dyndnscfg as $dyndns) {
1876
			if ((empty($int)) || ($int == $dyndns['interface']) || (is_array($gwgroups[$dyndns['interface']]))) {
1877
				$dyndns['verboselog'] = isset($dyndns['verboselog']);
1878
				$dyndns['curl_ipresolve_v4'] = isset($dyndns['curl_ipresolve_v4']);
1879
				$dyndns['curl_ssl_verifypeer'] = isset($dyndns['curl_ssl_verifypeer']);
1880
				services_dyndns_configure_client($dyndns);
1881
				sleep(1);
1882
			}
1883
		}
1884

    
1885
		if (platform_booting()) {
1886
			echo gettext("done.") . "\n";
1887
		}
1888
	}
1889

    
1890
	return 0;
1891
}
1892

    
1893
function dyndnsCheckIP($int) {
1894
	global $config;
1895
	$ip_address = get_interface_ip($int);
1896
	if (is_private_ip($ip_address)) {
1897
		$gateways_status = return_gateways_status(true);
1898
		// If the gateway for this interface is down, then the external check cannot work.
1899
		// Avoid the long wait for the external check to timeout.
1900
		if (stristr($gateways_status[$config['interfaces'][$int]['gateway']]['status'], "down")) {
1901
			return "down";
1902
		}
1903
		$hosttocheck = "http://checkip.dyndns.org";
1904
		$ip_ch = curl_init($hosttocheck);
1905
		curl_setopt($ip_ch, CURLOPT_RETURNTRANSFER, 1);
1906
		curl_setopt($ip_ch, CURLOPT_SSL_VERIFYPEER, FALSE);
1907
		curl_setopt($ip_ch, CURLOPT_INTERFACE, 'host!' . $ip_address);
1908
		curl_setopt($ip_ch, CURLOPT_CONNECTTIMEOUT, '30');
1909
		curl_setopt($ip_ch, CURLOPT_TIMEOUT, 120);
1910
		curl_setopt($ip_ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1911
		$ip_result_page = curl_exec($ip_ch);
1912
		curl_close($ip_ch);
1913
		$ip_result_decoded = urldecode($ip_result_page);
1914
		preg_match('=Current IP Address: (.*)</body>=siU', $ip_result_decoded, $matches);
1915
		$ip_address = trim($matches[1]);
1916
	}
1917
	return $ip_address;
1918
}
1919

    
1920
function services_dnsmasq_configure() {
1921
	global $config, $g;
1922
	$return = 0;
1923

    
1924
	// hard coded args: will be removed to avoid duplication if specified in custom_options
1925
	$standard_args = array(
1926
		"dns-forward-max" => "--dns-forward-max=5000",
1927
		"cache-size" => "--cache-size=10000",
1928
		"local-ttl" => "--local-ttl=1"
1929
	);
1930

    
1931

    
1932
	if (isset($config['system']['developerspew'])) {
1933
		$mt = microtime();
1934
		echo "services_dnsmasq_configure() being called $mt\n";
1935
	}
1936

    
1937
	/* kill any running dnsmasq */
1938
	if (file_exists("{$g['varrun_path']}/dnsmasq.pid")) {
1939
		sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM");
1940
	}
1941

    
1942
	if (isset($config['dnsmasq']['enable'])) {
1943

    
1944
		if (platform_booting()) {
1945
			echo gettext("Starting DNS forwarder...");
1946
		} else {
1947
			sleep(1);
1948
		}
1949

    
1950
		/* generate hosts file */
1951
		if (system_hosts_generate() != 0) {
1952
			$return = 1;
1953
		}
1954

    
1955
		$args = "";
1956

    
1957
		if (isset($config['dnsmasq']['regdhcp'])) {
1958
			$args .= " --dhcp-hostsfile={$g['varetc_path']}/hosts ";
1959
		}
1960

    
1961
		/* Setup listen port, if non-default */
1962
		if (is_port($config['dnsmasq']['port'])) {
1963
			$args .= " --port={$config['dnsmasq']['port']} ";
1964
		}
1965

    
1966
		$listen_addresses = "";
1967
		if (isset($config['dnsmasq']['interface'])) {
1968
			$interfaces = explode(",", $config['dnsmasq']['interface']);
1969
			foreach ($interfaces as $interface) {
1970
				if (is_ipaddrv4($interface)) {
1971
					$listen_addresses .= " --listen-address={$interface} ";
1972
				} else if (is_ipaddrv6($interface)) {
1973
					/*
1974
					 * XXX: Since dnsmasq does not support link-local address
1975
					 * with scope specified. These checks are being done.
1976
					 */
1977
					if (is_linklocal($interface) && strstr($interface, "%")) {
1978
						$tmpaddrll6 = explode("%", $interface);
1979
						$listen_addresses .= " --listen-address={$tmpaddrll6[0]} ";
1980
					} else {
1981
						$listen_addresses .= " --listen-address={$interface} ";
1982
					}
1983
				} else if (strstr($interface, "_vip")) {
1984
					$laddr = get_configured_carp_interface_list($interface);
1985
					if (is_ipaddr($laddr)) {
1986
						$listen_addresses .= " --listen-address={$laddr} ";
1987
					}
1988
				} else {
1989
					$if = get_real_interface($interface);
1990
					if (does_interface_exist($if)) {
1991
						$laddr = get_interface_ip($interface);
1992
						if (is_ipaddrv4($laddr)) {
1993
							$listen_addresses .= " --listen-address={$laddr} ";
1994
						}
1995
						$laddr6 = get_interface_ipv6($interface);
1996
						if (is_ipaddrv6($laddr6) && !isset($config['dnsmasq']['strictbind'])) {
1997
							/*
1998
							 * XXX: Since dnsmasq does not support link-local address
1999
							 * with scope specified. These checks are being done.
2000
							 */
2001
							if (is_linklocal($laddr6) && strstr($laddr6, "%")) {
2002
								$tmpaddrll6 = explode("%", $laddr6);
2003
								$listen_addresses .= " --listen-address={$tmpaddrll6[0]} ";
2004
							} else {
2005
								$listen_addresses .= " --listen-address={$laddr6} ";
2006
							}
2007
						}
2008
					}
2009
				}
2010
			}
2011
			if (!empty($listen_addresses)) {
2012
				$args .= " {$listen_addresses} ";
2013
				if (isset($config['dnsmasq']['strictbind'])) {
2014
					$args .= " --bind-interfaces ";
2015
				}
2016
			}
2017
		}
2018

    
2019
		/* If selected, then first forward reverse lookups for private IPv4 addresses to nowhere. */
2020
		/* Only make entries for reverse domains that do not have a matching domain override. */
2021
		if (isset($config['dnsmasq']['no_private_reverse'])) {
2022
			/* Note: Carrier Grade NAT (CGN) addresses 100.64.0.0/10 are intentionally not here. */
2023
			/* End-users should not be aware of CGN addresses, so reverse lookups for these should not happen. */
2024
			/* Just the pfSense WAN might get a CGN address from an ISP. */
2025

    
2026
			// Build an array of domain overrides to help in checking for matches.
2027
			$override_a = array();
2028
			if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2029
				foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2030
					$override_a[$override['domain']] = "y";
2031
				}
2032
			}
2033

    
2034
			// Build an array of the private reverse lookup domain names
2035
			$reverse_domain_a = array("10.in-addr.arpa", "168.192.in-addr.arpa");
2036
			// Unfortunately the 172.16.0.0/12 range does not map nicely to the in-addr.arpa scheme.
2037
			for ($subnet_num = 16; $subnet_num < 32; $subnet_num++) {
2038
				$reverse_domain_a[] = "$subnet_num.172.in-addr.arpa";
2039
			}
2040

    
2041
			// Set the --server parameter to nowhere for each reverse domain name that was not specifically specified in a domain override.
2042
			foreach ($reverse_domain_a as $reverse_domain) {
2043
				if (!isset($override_a[$reverse_domain])) {
2044
					$args .= " --server=/$reverse_domain/ ";
2045
				}
2046
			}
2047
			unset($override_a);
2048
			unset($reverse_domain_a);
2049
		}
2050

    
2051
		/* Setup forwarded domains */
2052
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2053
			foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2054
				if ($override['ip'] == "!") {
2055
					$override[ip] = "";
2056
				}
2057
				$args .= ' --server=/' . $override['domain'] . '/' . $override['ip'];
2058
			}
2059
		}
2060

    
2061
		/* Allow DNS Rebind for forwarded domains */
2062
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2063
			if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
2064
				foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2065
					$args .= ' --rebind-domain-ok=/' . $override['domain'] . '/ ';
2066
				}
2067
			}
2068
		}
2069

    
2070
		if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
2071
			$dns_rebind = "--rebind-localhost-ok --stop-dns-rebind";
2072
		}
2073

    
2074
		if (isset($config['dnsmasq']['strict_order'])) {
2075
			$args .= " --strict-order ";
2076
		}
2077

    
2078
		if (isset($config['dnsmasq']['domain_needed'])) {
2079
			$args .= " --domain-needed ";
2080
		}
2081

    
2082
		if ($config['dnsmasq']['custom_options']) {
2083
			foreach (preg_split('/\s+/', $config['dnsmasq']['custom_options']) as $c) {
2084
				$args .= " " . escapeshellarg("--{$c}");
2085
				$p = explode('=', $c);
2086
				if (array_key_exists($p[0], $standard_args)) {
2087
					unset($standard_args[$p[0]]);
2088
				}
2089
			}
2090
		}
2091
		$args .= ' ' . implode(' ', array_values($standard_args));
2092

    
2093
		/* run dnsmasq */
2094
		$cmd = "/usr/local/sbin/dnsmasq --all-servers {$dns_rebind} {$args}";
2095
		//log_error("dnsmasq command: {$cmd}");
2096
		mwexec_bg($cmd);
2097
		unset($args);
2098

    
2099
		system_dhcpleases_configure();
2100

    
2101
		if (platform_booting()) {
2102
			echo gettext("done.") . "\n";
2103
		}
2104
	}
2105

    
2106
	if (!platform_booting()) {
2107
		if (services_dhcpd_configure() != 0) {
2108
			$return = 1;
2109
		}
2110
	}
2111

    
2112
	return $return;
2113
}
2114

    
2115
function services_unbound_configure() {
2116
	global $config, $g;
2117
	$return = 0;
2118

    
2119
	if (isset($config['system']['developerspew'])) {
2120
		$mt = microtime();
2121
		echo "services_unbound_configure() being called $mt\n";
2122
	}
2123

    
2124
	// kill any running Unbound instance
2125
	if (file_exists("{$g['varrun_path']}/unbound.pid")) {
2126
		sigkillbypid("{$g['varrun_path']}/unbound.pid", "TERM");
2127
	}
2128

    
2129
	if (isset($config['unbound']['enable'])) {
2130
		if (platform_booting()) {
2131
			echo gettext("Starting DNS Resolver...");
2132
		} else {
2133
			sleep(1);
2134
		}
2135

    
2136
		/* generate hosts file */
2137
		if (system_hosts_generate() != 0) {
2138
			$return = 1;
2139
		}
2140

    
2141
		require_once('/etc/inc/unbound.inc');
2142
		sync_unbound_service();
2143
		if (platform_booting()) {
2144
			echo gettext("done.") . "\n";
2145
		}
2146

    
2147
		system_dhcpleases_configure();
2148
	}
2149

    
2150
	if (!platform_booting()) {
2151
		if (services_dhcpd_configure() != 0) {
2152
			$return = 1;
2153
		}
2154
	}
2155

    
2156
	return $return;
2157
}
2158

    
2159
function services_snmpd_configure() {
2160
	global $config, $g;
2161
	if (isset($config['system']['developerspew'])) {
2162
		$mt = microtime();
2163
		echo "services_snmpd_configure() being called $mt\n";
2164
	}
2165

    
2166
	/* kill any running snmpd */
2167
	sigkillbypid("{$g['varrun_path']}/snmpd.pid", "TERM");
2168
	sleep(2);
2169
	if (is_process_running("bsnmpd")) {
2170
		mwexec("/usr/bin/killall bsnmpd", true);
2171
	}
2172

    
2173
	if (isset($config['snmpd']['enable'])) {
2174

    
2175
		if (platform_booting()) {
2176
			echo gettext("Starting SNMP daemon... ");
2177
		}
2178

    
2179
		/* generate snmpd.conf */
2180
		$fd = fopen("{$g['varetc_path']}/snmpd.conf", "w");
2181
		if (!$fd) {
2182
			printf(gettext("Error: cannot open snmpd.conf in services_snmpd_configure().%s"), "\n");
2183
			return 1;
2184
		}
2185

    
2186

    
2187
		$snmpdconf = <<<EOD
2188
location := "{$config['snmpd']['syslocation']}"
2189
contact := "{$config['snmpd']['syscontact']}"
2190
read := "{$config['snmpd']['rocommunity']}"
2191

    
2192
EOD;
2193

    
2194
/* No docs on what write strings do there for disable for now.
2195
		if (isset($config['snmpd']['rwenable']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2196
			$snmpdconf .= <<<EOD
2197
# write string
2198
write := "{$config['snmpd']['rwcommunity']}"
2199

    
2200
EOD;
2201
		}
2202
*/
2203

    
2204

    
2205
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2206
			$snmpdconf .= <<<EOD
2207
# SNMP Trap support.
2208
traphost := {$config['snmpd']['trapserver']}
2209
trapport := {$config['snmpd']['trapserverport']}
2210
trap := "{$config['snmpd']['trapstring']}"
2211

    
2212

    
2213
EOD;
2214
		}
2215

    
2216
		$version = trim(file_get_contents('/etc/version'));
2217
		$platform = trim(file_get_contents('/etc/platform'));
2218
		if (($platform == "pfSense") && ($g['product_name'] != "pfSense")) {
2219
			$platform = $g['product_name'];
2220
		}
2221
		$sysDescr = "{$g['product_name']} " . php_uname("n") .
2222
			" {$version} {$platform} " . php_uname("s") .
2223
			" " . php_uname("r") . " " . php_uname("m");
2224

    
2225
		$snmpdconf .= <<<EOD
2226
system := 1     # pfSense
2227
%snmpd
2228
sysDescr			= "{$sysDescr}"
2229
begemotSnmpdDebugDumpPdus       = 2
2230
begemotSnmpdDebugSyslogPri      = 7
2231
begemotSnmpdCommunityString.0.1 = $(read)
2232

    
2233
EOD;
2234

    
2235
/* No docs on what write strings do there for disable for now.
2236
		if (isset($config['snmpd']['rwcommunity']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2237
			$snmpdconf .= <<<EOD
2238
begemotSnmpdCommunityString.0.2 = $(write)
2239

    
2240
EOD;
2241
		}
2242
*/
2243

    
2244

    
2245
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2246
			$snmpdconf .= <<<EOD
2247
begemotTrapSinkStatus.[$(traphost)].$(trapport) = 4
2248
begemotTrapSinkVersion.[$(traphost)].$(trapport) = 2
2249
begemotTrapSinkComm.[$(traphost)].$(trapport) = $(trap)
2250

    
2251
EOD;
2252
		}
2253

    
2254

    
2255
		$snmpdconf .= <<<EOD
2256
begemotSnmpdCommunityDisable    = 1
2257

    
2258
EOD;
2259

    
2260
		if (isset($config['snmpd']['bindlan'])) {
2261
			$config['snmpd']['bindip'] = 'lan';
2262
			unset($config['snmpd']['bindlan']);
2263
		}
2264
		$bind_to_ip = "0.0.0.0";
2265
		if (isset($config['snmpd']['bindip'])) {
2266
			if (is_ipaddr($config['snmpd']['bindip'])) {
2267
				$bind_to_ip = $config['snmpd']['bindip'];
2268
			} else {
2269
				$if = get_real_interface($config['snmpd']['bindip']);
2270
				if (does_interface_exist($if)) {
2271
					$bind_to_ip = get_interface_ip($config['snmpd']['bindip']);
2272
				}
2273
			}
2274
		}
2275

    
2276
		if (is_port($config['snmpd']['pollport'])) {
2277
			$snmpdconf .= <<<EOD
2278
begemotSnmpdPortStatus.{$bind_to_ip}.{$config['snmpd']['pollport']} = 1
2279

    
2280
EOD;
2281

    
2282
		}
2283

    
2284
		$snmpdconf .= <<<EOD
2285
begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1
2286
begemotSnmpdLocalPortType."/var/run/snmpd.sock" = 4
2287

    
2288
# These are bsnmp macros not php vars.
2289
sysContact      = $(contact)
2290
sysLocation     = $(location)
2291
sysObjectId     = 1.3.6.1.4.1.12325.1.1.2.1.$(system)
2292

    
2293
snmpEnableAuthenTraps = 2
2294

    
2295
EOD;
2296

    
2297
		if (is_array($config['snmpd']['modules'])) {
2298
			if (isset($config['snmpd']['modules']['mibii'])) {
2299
			$snmpdconf .= <<<EOD
2300
begemotSnmpdModulePath."mibII"  = "/usr/lib/snmp_mibII.so"
2301

    
2302
EOD;
2303
			}
2304

    
2305
			if (isset($config['snmpd']['modules']['netgraph'])) {
2306
				$snmpdconf .= <<<EOD
2307
begemotSnmpdModulePath."netgraph" = "/usr/lib/snmp_netgraph.so"
2308
%netgraph
2309
begemotNgControlNodeName = "snmpd"
2310

    
2311
EOD;
2312
			}
2313

    
2314
			if (isset($config['snmpd']['modules']['pf'])) {
2315
				$snmpdconf .= <<<EOD
2316
begemotSnmpdModulePath."pf"     = "/usr/lib/snmp_pf.so"
2317

    
2318
EOD;
2319
			}
2320

    
2321
			if (isset($config['snmpd']['modules']['hostres'])) {
2322
				/* XXX: hostres module crashes APU - ticket #4403 */
2323
				$specplatform = system_identify_specific_platform();
2324
				if ($specplatform['name'] == 'APU') {
2325
					log_error("'Host Resources' SNMP module was ignored because it can potentially crash system on APU boards");
2326
				} else {
2327
					$snmpdconf .= <<<EOD
2328
begemotSnmpdModulePath."hostres"     = "/usr/lib/snmp_hostres.so"
2329

    
2330
EOD;
2331
				}
2332
				unset($specplatform);
2333
			}
2334

    
2335
			if (isset($config['snmpd']['modules']['bridge'])) {
2336
				$snmpdconf .= <<<EOD
2337
begemotSnmpdModulePath."bridge"     = "/usr/lib/snmp_bridge.so"
2338
# config must end with blank line
2339

    
2340
EOD;
2341
			}
2342
			if (isset($config['snmpd']['modules']['ucd'])) {
2343
				$snmpdconf .= <<<EOD
2344
begemotSnmpdModulePath."ucd"     = "/usr/local/lib/snmp_ucd.so"
2345

    
2346
EOD;
2347
			}
2348
			if (isset($config['snmpd']['modules']['regex'])) {
2349
				$snmpdconf .= <<<EOD
2350
begemotSnmpdModulePath."regex"     = "/usr/local/lib/snmp_regex.so"
2351

    
2352
EOD;
2353
			}
2354
		}
2355

    
2356
		fwrite($fd, $snmpdconf);
2357
		fclose($fd);
2358
		unset($snmpdconf);
2359

    
2360
		if (isset($config['snmpd']['bindlan'])) {
2361
			$bindlan = "";
2362
		}
2363

    
2364
		/* run bsnmpd */
2365
		mwexec("/usr/sbin/bsnmpd -c {$g['varetc_path']}/snmpd.conf" .
2366
			"{$bindlan} -p {$g['varrun_path']}/snmpd.pid");
2367

    
2368
		if (platform_booting()) {
2369
			echo gettext("done.") . "\n";
2370
		}
2371
	}
2372

    
2373
	return 0;
2374
}
2375

    
2376
function services_dnsupdate_process($int = "", $updatehost = "", $forced = false) {
2377
	global $config, $g;
2378
	if (isset($config['system']['developerspew'])) {
2379
		$mt = microtime();
2380
		echo "services_dnsupdate_process() being called $mt\n";
2381
	}
2382

    
2383
	/* Dynamic DNS updating active? */
2384
	if (is_array($config['dnsupdates']['dnsupdate'])) {
2385
		$notify_text = "";
2386
		foreach ($config['dnsupdates']['dnsupdate'] as $i => $dnsupdate) {
2387
			if (!isset($dnsupdate['enable'])) {
2388
				continue;
2389
			}
2390
			if (!empty($int) && $int != $dnsupdate['interface']) {
2391
				continue;
2392
			}
2393
			if (!empty($updatehost) && ($updatehost != $dnsupdate['host'])) {
2394
				continue;
2395
			}
2396

    
2397
			/* determine interface name */
2398
			$if = get_real_interface($dnsupdate['interface']);
2399

    
2400
			if (isset($dnsupdate['usepublicip'])) {
2401
				$wanip = dyndnsCheckIP($dnsupdate['interface']);
2402
			} else {
2403
				$wanip = get_interface_ip($dnsupdate['interface']);
2404
			}
2405

    
2406
			$wanipv6 = get_interface_ipv6($dnsupdate['interface']);
2407
			$cacheFile = "{$g['conf_path']}/dyndns_{$dnsupdate['interface']}_rfc2136_" . escapeshellarg($dnsupdate['host']) . "_{$dnsupdate['server']}.cache";
2408
			$currentTime = time();
2409

    
2410
			if ($wanip || $wanipv6) {
2411
				$keyname = $dnsupdate['keyname'];
2412
				/* trailing dot */
2413
				if (substr($keyname, -1) != ".") {
2414
					$keyname .= ".";
2415
				}
2416

    
2417
				$hostname = $dnsupdate['host'];
2418
				/* trailing dot */
2419
				if (substr($hostname, -1) != ".") {
2420
					$hostname .= ".";
2421
				}
2422

    
2423
				/* write private key file
2424
				   this is dumb - public and private keys are the same for HMAC-MD5,
2425
				   but nsupdate insists on having both */
2426
				$fd = fopen("{$g['varetc_path']}/K{$i}{$keyname}+157+00000.private", "w");
2427
				$privkey = <<<EOD
2428
Private-key-format: v1.2
2429
Algorithm: 157 (HMAC)
2430
Key: {$dnsupdate['keydata']}
2431

    
2432
EOD;
2433
				fwrite($fd, $privkey);
2434
				fclose($fd);
2435

    
2436
				/* write public key file */
2437
				if ($dnsupdate['keytype'] == "zone") {
2438
					$flags = 257;
2439
					$proto = 3;
2440
				} else if ($dnsupdate['keytype'] == "host") {
2441
					$flags = 513;
2442
					$proto = 3;
2443
				} else if ($dnsupdate['keytype'] == "user") {
2444
					$flags = 0;
2445
					$proto = 2;
2446
				}
2447

    
2448
				$fd = fopen("{$g['varetc_path']}/K{$i}{$keyname}+157+00000.key", "w");
2449
				fwrite($fd, "{$keyname} IN KEY {$flags} {$proto} 157 {$dnsupdate['keydata']}\n");
2450
				fclose($fd);
2451

    
2452
				/* generate update instructions */
2453
				$upinst = "";
2454
				if (!empty($dnsupdate['server'])) {
2455
					$upinst .= "server {$dnsupdate['server']}\n";
2456
				}
2457

    
2458
				if (file_exists($cacheFile)) {
2459
					list($cachedipv4, $cacheTimev4) = explode("|", file_get_contents($cacheFile));
2460
				}
2461
				if (file_exists("{$cacheFile}.ipv6")) {
2462
					list($cachedipv6, $cacheTimev6) = explode("|", file_get_contents("{$cacheFile}.ipv6"));
2463
				}
2464

    
2465
				// 25 Days
2466
				$maxCacheAgeSecs = 25 * 24 * 60 * 60;
2467
				$need_update = false;
2468

    
2469
				conf_mount_rw();
2470
				/* Update IPv4 if we have it. */
2471
				if (is_ipaddrv4($wanip) && $dnsupdate['recordtype'] != "AAAA") {
2472
					if (($wanip != $cachedipv4) || (($currentTime - $cacheTimev4) > $maxCacheAgeSecs) || $forced) {
2473
						$upinst .= "update delete {$dnsupdate['host']}. A\n";
2474
						$upinst .= "update add {$dnsupdate['host']}. {$dnsupdate['ttl']} A {$wanip}\n";
2475
						$notify_text .= sprintf(gettext("DynDNS updated IP Address (A) for {$dnsupdate['host']} on %s (%s) to %s"), convert_real_interface_to_friendly_descr($if), $if, $wanip) . "\n";
2476
						@file_put_contents($cacheFile, "{$wanip}|{$currentTime}");
2477
						log_error("phpDynDNS: updating cache file {$cacheFile}: {$wanip}");
2478
						$need_update = true;
2479
					} else {
2480
						log_error("phpDynDNS: Not updating {$dnsupdate['host']} A record because the IP address has not changed.");
2481
					}
2482
				} else {
2483
					@unlink($cacheFile);
2484
				}
2485

    
2486
				/* Update IPv6 if we have it. */
2487
				if (is_ipaddrv6($wanipv6) && $dnsupdate['recordtype'] != "A") {
2488
					if (($wanipv6 != $cachedipv6) || (($currentTime - $cacheTimev6) > $maxCacheAgeSecs) || $forced) {
2489
						$upinst .= "update delete {$dnsupdate['host']}. AAAA\n";
2490
						$upinst .= "update add {$dnsupdate['host']}. {$dnsupdate['ttl']} AAAA {$wanipv6}\n";
2491
						$notify_text .= sprintf(gettext("DynDNS updated IPv6 Address (AAAA) for {$dnsupdate['host']} on %s (%s) to %s"), convert_real_interface_to_friendly_descr($if), $if, $wanipv6) . "\n";
2492
						@file_put_contents("{$cacheFile}.ipv6", "{$wanipv6}|{$currentTime}");
2493
						log_error("phpDynDNS: updating cache file {$cacheFile}.ipv6: {$wanipv6}");
2494
						$need_update = true;
2495
					} else {
2496
						log_error("phpDynDNS: Not updating {$dnsupdate['host']} AAAA record because the IPv6 address has not changed.");
2497
					}
2498
				} else {
2499
					@unlink("{$cacheFile}.ipv6");
2500
				}
2501
				conf_mount_ro();
2502

    
2503
				$upinst .= "\n";	/* mind that trailing newline! */
2504

    
2505
				if ($need_update) {
2506
					@file_put_contents("{$g['varetc_path']}/nsupdatecmds{$i}", $upinst);
2507
					unset($upinst);
2508
					/* invoke nsupdate */
2509
					$cmd = "/usr/local/bin/nsupdate -k {$g['varetc_path']}/K{$i}{$keyname}+157+00000.key";
2510
					if (isset($dnsupdate['usetcp'])) {
2511
						$cmd .= " -v";
2512
					}
2513
					$cmd .= " {$g['varetc_path']}/nsupdatecmds{$i}";
2514
					mwexec_bg($cmd);
2515
					unset($cmd);
2516
				}
2517
			}
2518
		}
2519
		if (!empty($notify_text)) {
2520
			notify_all_remote($notify_text);
2521
		}
2522
	}
2523

    
2524
	return 0;
2525
}
2526

    
2527
/* configure cron service */
2528
function configure_cron() {
2529
	global $g, $config;
2530

    
2531
	conf_mount_rw();
2532
	/* preserve existing crontab entries */
2533
	$crontab_contents = file("/etc/crontab", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2534

    
2535
	for ($i = 0; $i < count($crontab_contents); $i++) {
2536
		$cron_item =& $crontab_contents[$i];
2537
		if (strpos($cron_item, "# pfSense specific crontab entries") !== false) {
2538
			array_splice($crontab_contents, $i - 1);
2539
			break;
2540
		}
2541
	}
2542
	$crontab_contents = implode("\n", $crontab_contents) . "\n";
2543

    
2544

    
2545
	if (is_array($config['cron']['item'])) {
2546
		$crontab_contents .= "#\n";
2547
		$crontab_contents .= "# " . gettext("pfSense specific crontab entries") . "\n";
2548
		$crontab_contents .= "# " .gettext("Created:") . " " . date("F j, Y, g:i a") . "\n";
2549
		$crontab_contents .= "#\n";
2550

    
2551
		if (isset($config['system']['proxyurl']) && !empty($config['system']['proxyurl'])) {
2552
			$http_proxy = $config['system']['proxyurl'];
2553
			if (isset($config['system']['proxyport']) && !empty($config['system']['proxyport'])) {
2554
				$http_proxy .= ':' . $config['system']['proxyport'];
2555
			}
2556
			$crontab_contents .= "HTTP_PROXY={$http_proxy}";
2557
		}
2558

    
2559
		foreach ($config['cron']['item'] as $item) {
2560
			$crontab_contents .= "\n{$item['minute']}\t";
2561
			$crontab_contents .= "{$item['hour']}\t";
2562
			$crontab_contents .= "{$item['mday']}\t";
2563
			$crontab_contents .= "{$item['month']}\t";
2564
			$crontab_contents .= "{$item['wday']}\t";
2565
			$crontab_contents .= "{$item['who']}\t";
2566
			$crontab_contents .= "{$item['command']}";
2567
		}
2568

    
2569
		$crontab_contents .= "\n#\n";
2570
		$crontab_contents .= "# " . gettext("If possible do not add items to this file manually.") . "\n";
2571
		$crontab_contents .= "# " . gettext("If you do so, this file must be terminated with a blank line (e.g. new line)") . "\n";
2572
		$crontab_contents .= "#\n\n";
2573
	}
2574

    
2575
	/* please maintain the newline at the end of file */
2576
	file_put_contents("/etc/crontab", $crontab_contents);
2577
	unset($crontab_contents);
2578

    
2579
	/* do a HUP kill to force sync changes */
2580
	sigkillbypid("{$g['varrun_path']}/cron.pid", "HUP");
2581

    
2582
	conf_mount_ro();
2583
}
2584

    
2585
function upnp_action ($action) {
2586
	global $g, $config;
2587
	switch ($action) {
2588
		case "start":
2589
			if (file_exists('/var/etc/miniupnpd.conf')) {
2590
				@unlink("{$g['varrun_path']}/miniupnpd.pid");
2591
				mwexec_bg("/usr/local/sbin/miniupnpd -f /var/etc/miniupnpd.conf -P {$g['varrun_path']}/miniupnpd.pid");
2592
			}
2593
			break;
2594
		case "stop":
2595
			killbypid("{$g['varrun_path']}/miniupnpd.pid");
2596
			while ((int)exec("/bin/pgrep -a miniupnpd | wc -l") > 0) {
2597
				mwexec('killall miniupnpd 2>/dev/null', true);
2598
			}
2599
			mwexec('/sbin/pfctl -aminiupnpd -Fr 2>&1 >/dev/null');
2600
			mwexec('/sbin/pfctl -aminiupnpd -Fn 2>&1 >/dev/null');
2601
			break;
2602
		case "restart":
2603
			upnp_action('stop');
2604
			upnp_action('start');
2605
			break;
2606
	}
2607
}
2608

    
2609
function upnp_start() {
2610
	global $config;
2611

    
2612
	if (!isset($config['installedpackages']['miniupnpd']['config'])) {
2613
		return;
2614
	}
2615

    
2616
	if ($config['installedpackages']['miniupnpd']['config'][0]['enable']) {
2617
		echo gettext("Starting UPnP service... ");
2618
		require_once('/usr/local/pkg/miniupnpd.inc');
2619
		sync_package_miniupnpd();
2620
		echo "done.\n";
2621
	}
2622
}
2623

    
2624
function install_cron_job($command, $active=false, $minute="0", $hour="*", $monthday="*", $month="*", $weekday="*", $who="root") {
2625
	global $config, $g;
2626

    
2627
	$is_installed = false;
2628
	$cron_changed = true;
2629

    
2630
	if (!is_array($config['cron'])) {
2631
		$config['cron'] = array();
2632
	}
2633
	if (!is_array($config['cron']['item'])) {
2634
		$config['cron']['item'] = array();
2635
	}
2636

    
2637
	$x=0;
2638
	foreach ($config['cron']['item'] as $item) {
2639
		if (strstr($item['command'], $command)) {
2640
			$is_installed = true;
2641
			break;
2642
		}
2643
		$x++;
2644
	}
2645

    
2646
	if ($active) {
2647
		$cron_item = array();
2648
		$cron_item['minute'] = $minute;
2649
		$cron_item['hour'] = $hour;
2650
		$cron_item['mday'] = $monthday;
2651
		$cron_item['month'] = $month;
2652
		$cron_item['wday'] = $weekday;
2653
		$cron_item['who'] = $who;
2654
		$cron_item['command'] = $command;
2655
		if (!$is_installed) {
2656
			$config['cron']['item'][] = $cron_item;
2657
			write_config(sprintf(gettext("Installed cron job for %s"), $command));
2658
		} else {
2659
			if ($config['cron']['item'][$x] == $cron_item) {
2660
				$cron_changed = false;
2661
				log_error(sprintf(gettext("Checked cron job for %s, no change needed"), $command));
2662
			} else {
2663
				$config['cron']['item'][$x] = $cron_item;
2664
				write_config(sprintf(gettext("Updated cron job for %s"), $command));
2665
			}
2666
		}
2667
	} else {
2668
		if ($is_installed == true) {
2669
			unset($config['cron']['item'][$x]);
2670
			write_config(sprintf(gettext("Removed cron job for %s"), $command));
2671
		}
2672
	}
2673

    
2674
	if ($cron_changed) {
2675
		configure_cron();
2676
	}
2677
}
2678

    
2679
?>
(49-49/67)