Project

General

Profile

Download (82.6 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 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),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 (isset($config['system']['developerspew'])) {
49
		$mt = microtime();
50
		echo "services_radvd_configure() being called $mt\n";
51
	}
52

    
53
	if (!is_array($config['dhcpdv6'])) {
54
		$config['dhcpdv6'] = array();
55
	}
56

    
57
	$Iflist = get_configured_interface_list();
58
	$Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces());
59
	$carplist = get_configured_carp_interface_list();
60

    
61
	$radvdconf = "# Automatically Generated, do not edit\n";
62

    
63
	/* Process all links which need the router advertise daemon */
64
	$radvdifs = array();
65

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

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

    
83
		/* are router advertisements enabled? */
84
		if ($dhcpv6ifconf['ramode'] == "disabled") {
85
			continue;
86
		}
87

    
88
		if (!isset($dhcpv6ifconf['rapriority'])) {
89
			$dhcpv6ifconf['rapriority'] = "medium";
90
		}
91

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

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

    
113
		if (isset($radvdifs[$realif])) {
114
			continue;
115
		}
116

    
117
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
118
		if (!is_ipaddrv6($ifcfgipv6)) {
119
			continue;
120
		}
121

    
122
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
123
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
124
		$radvdifs[$realif] = $realif;
125

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

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

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

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

    
293
		if (strstr($if, "_vip")) {
294
			// CARP IP, find parent
295
			$ifparent = link_carp_interface_to_parent($if);
296
			$realif = convert_friendly_interface_to_real_interface_name($ifparent);
297
		} else {
298
			$realif = get_real_interface($if, "inet6");
299
		}
300

    
301
		/* prevent duplicate entries, manual overrides */
302
		if (isset($radvdifs[$realif])) {
303
			continue;
304
		}
305

    
306
		$ifcfgipv6 = get_interface_ipv6($if);
307
		if (!is_ipaddrv6($ifcfgipv6)) {
308
			$subnetv6 = "::";
309
			$ifcfgsnv6 = "64";
310
		} else {
311
			$ifcfgsnv6 = get_interface_subnetv6($if);
312
			$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
313
		}
314
		$radvdifs[$realif] = $realif;
315

    
316
		$autotype = $config['interfaces'][$trackif]['ipaddrv6'];
317

    
318
		if ($g['debug']) {
319
			log_error("configuring RA on {$if} for type {$autotype} radvd subnet {$subnetv6}/{$ifcfgsnv6}");
320
		}
321

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

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

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

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

    
390
function services_dhcpd_configure($family = "all", $blacklist = array()) {
391
	global $config, $g;
392

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

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

    
416
	if ($family == "all" || $family == "inet") {
417
		services_dhcpdv4_configure();
418
	}
419
	if ($family == "all" || $family == "inet6") {
420
		services_dhcpdv6_configure($blacklist);
421
		services_radvd_configure($blacklist);
422
	}
423
}
424

    
425
function services_dhcpdv4_configure() {
426
	global $config, $g;
427
	$need_ddns_updates = false;
428
	$ddns_zones = array();
429

    
430
	if ($g['services_dhcp_server_enable'] == false) {
431
		return;
432
	}
433

    
434
	if (isset($config['system']['developerspew'])) {
435
		$mt = microtime();
436
		echo "services_dhcpdv4_configure($if) being called $mt\n";
437
	}
438

    
439
	/* kill any running dhcpd */
440
	if (isvalidpid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid")) {
441
		killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid");
442
	}
443

    
444
	/* DHCP enabled on any interfaces? */
445
	if (!is_dhcp_server_enabled()) {
446
		return 0;
447
	}
448

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

    
462
	if (platform_booting()) {
463
		/* restore the leases, if we have them */
464
		if (file_exists("{$g['cf_conf_path']}/dhcpleases.tgz")) {
465
			$dhcprestore = "";
466
			$dhcpreturn = "";
467
			exec("cd /;LANG=C /usr/bin/tar -xzf {$g['cf_conf_path']}/dhcpleases.tgz 2>&1", $dhcprestore, $dhcpreturn);
468
			$dhcprestore = implode(" ", $dhcprestore);
469
			if ($dhcpreturn <> 0) {
470
				log_error(sprintf(gettext('DHCP leases restore failed exited with %1$s, the error is: %2$s%3$s'), $dhcpreturn, $dhcprestore, "\n"));
471
			}
472
		}
473
		/* 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. */
474
		if (($g['platform'] == $g['product_name']) && !isset($config['system']['use_mfs_tmpvar'])) {
475
			unlink_if_exists("{$g['cf_conf_path']}/dhcpleases.tgz");
476
		}
477
	}
478

    
479
	$syscfg = $config['system'];
480
	if (!is_array($config['dhcpd'])) {
481
		$config['dhcpd'] = array();
482
	}
483
	$dhcpdcfg = $config['dhcpd'];
484
	$Iflist = get_configured_interface_list();
485

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

    
496
	if (platform_booting()) {
497
		echo gettext("Starting DHCP service...");
498
	} else {
499
		sleep(1);
500
	}
501

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

    
516
	$dhcpdconf = <<<EOD
517

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

    
531
EOD;
532

    
533
	if (!isset($dhcpifconf['disableauthoritative'])) {
534
		$dhcpdconf .= "authoritative;\n";
535
	}
536

    
537
	if (isset($dhcpifconf['alwaysbroadcast'])) {
538
		$dhcpdconf .= "always-broadcast on\n";
539
	}
540

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

    
553
	/*    loop through and determine if we need to setup
554
	 *    failover peer "bleh" entries
555
	 */
556
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
557

    
558
		if (!isset($config['interfaces'][$dhcpif]['enable'])) {
559
			continue;
560
		}
561

    
562
		interfaces_staticarp_configure($dhcpif);
563

    
564
		if (!isset($dhcpifconf['enable'])) {
565
			continue;
566
		}
567

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

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

    
622
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
623

    
624
		$newzone = array();
625
		$ifcfg = $config['interfaces'][$dhcpif];
626

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

    
635
		if (!is_ipaddr($subnet)) {
636
			continue;
637
		}
638

    
639
		if ($is_olsr_enabled == true) {
640
			if ($dhcpifconf['netmask']) {
641
				$subnetmask = gen_subnet_mask($dhcpifconf['netmask']);
642
			}
643
		}
644

    
645
		$all_pools = array();
646
		$all_pools[] = $dhcpifconf;
647
		if (is_array($dhcpifconf['pool'])) {
648
			$all_pools = array_merge($all_pools, $dhcpifconf['pool']);
649
		}
650

    
651
		$dnscfg = "";
652

    
653
		if ($dhcpifconf['domain']) {
654
			$dnscfg .= "	option domain-name \"{$dhcpifconf['domain']}\";\n";
655
		}
656

    
657
		if ($dhcpifconf['domainsearchlist'] <> "") {
658
			$dnscfg .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpifconf['domainsearchlist'])) . "\";\n";
659
		}
660

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

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

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

    
724
		$dhcpdconf .= "subnet {$subnet} netmask {$subnetmask} {\n";
725

    
726
		// Setup pool options
727
		foreach ($all_pools as $poolconf) {
728
			if (!(ip_in_subnet($poolconf['range']['from'], "{$subnet}/{$ifcfgsn}") && ip_in_subnet($poolconf['range']['to'], "{$subnet}/{$ifcfgsn}"))) {
729
				// If the user has changed the subnet from the interfaces page and applied,
730
				// but has not updated the DHCP range, then the range to/from of the pool can be outside the subnet.
731
				// This can also happen when implementing the batch of changes when the setup wizard reloads the new settings.
732
				$error_msg = sprintf(gettext("Invalid DHCP pool %s - %s for %s subnet %s/%s detected. Please correct the settings in Services, DHCP Server"), $poolconf['range']['from'], $poolconf['range']['to'], convert_real_interface_to_friendly_descr($dhcpif), $subnet, $ifcfgsn);
733
				$do_file_notice = true;
734
				$conf_ipv4_address = $ifcfg['ipaddr'];
735
				$conf_ipv4_subnetmask = $ifcfg['subnet'];
736
				if (is_ipaddrv4($conf_ipv4_address) && is_subnet("{$conf_ipv4_address}/{$conf_ipv4_subnetmask}")) {
737
					$conf_subnet_base = gen_subnet($conf_ipv4_address, $conf_ipv4_subnetmask);
738
					if (ip_in_subnet($poolconf['range']['from'], "{$conf_subnet_base}/{$conf_ipv4_subnetmask}") &&
739
					    ip_in_subnet($poolconf['range']['to'], "{$conf_subnet_base}/{$conf_ipv4_subnetmask}")) {
740
						// Even though the running interface subnet does not match the pool range,
741
						// the interface subnet in the config file contains the pool range.
742
						// We are somewhere part-way through a settings reload, e.g. after running the setup wizard.
743
						// services_dhcpdv4_configure will be called again later when the new interface settings from
744
						// the config are applied and at that time everything will match up.
745
						// Ignore this pool on this interface for now and just log the error to the system log.
746
						log_error($error_msg);
747
						$do_file_notice = false;
748
					}
749
				}
750
				if ($do_file_notice) {
751
					file_notice("DHCP", $error_msg);
752
				}
753
				continue;
754
			}
755
			$dhcpdconf .= "	pool {\n";
756
			/* is failover dns setup? */
757
			if (is_array($poolconf['dnsserver']) && $poolconf['dnsserver'][0] <> "") {
758
				$dhcpdconf .= "		option domain-name-servers {$poolconf['dnsserver'][0]}";
759
				if ($poolconf['dnsserver'][1] <> "") {
760
					$dhcpdconf .= ",{$poolconf['dnsserver'][1]}";
761
				}
762
				if ($poolconf['dnsserver'][2] <> "") {
763
					$dhcpdconf .= ",{$poolconf['dnsserver'][2]}";
764
				}
765
				if ($poolconf['dnsserver'][3] <> "") {
766
					$dhcpdconf .= ",{$poolconf['dnsserver'][3]}";
767
				}
768
				$dhcpdconf .= ";\n";
769
			}
770

    
771
			/* allow/deny MACs */
772
			$mac_allow_list = array_unique(explode(',', $poolconf['mac_allow']));
773
			foreach ($mac_allow_list as $mac) {
774
				if (empty($mac)) {
775
					continue;
776
				}
777
				$dhcpdconf .= "		allow members of \"" . str_replace(':', '', $mac) . "\";\n";
778
			}
779
			$mac_deny_list = array_unique(explode(',', $poolconf['mac_deny']));
780
			foreach ($mac_deny_list as $mac) {
781
				if (empty($mac)) {
782
					continue;
783
				}
784
				$dhcpdconf .= "		deny members of \"" . str_replace(':', '', $mac) . "\";\n";
785
			}
786

    
787
			if ($poolconf['failover_peerip'] <> "") {
788
				$dhcpdconf .= "		deny dynamic bootp clients;\n";
789
			}
790

    
791
			if (isset($poolconf['denyunknown'])) {
792
			   $dhcpdconf .= "		deny unknown-clients;\n";
793
			}
794

    
795
			if ($poolconf['gateway'] && $poolconf['gateway'] != "none" && ($poolconf['gateway'] != $dhcpifconf['gateway'])) {
796
				$dhcpdconf .= "		option routers {$poolconf['gateway']};\n";
797
			}
798

    
799
			if ($dhcpifconf['failover_peerip'] <> "") {
800
				$dhcpdconf .= "		failover peer \"dhcp_{$dhcpif}\";\n";
801
			}
802

    
803
			$pdnscfg = "";
804

    
805
			if ($poolconf['domain'] && ($poolconf['domain'] != $dhcpifconf['domain'])) {
806
				$pdnscfg .= "		option domain-name \"{$poolconf['domain']}\";\n";
807
			}
808

    
809
			if (!empty($poolconf['domainsearchlist']) && ($poolconf['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
810
				$pdnscfg .= "		option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $poolconf['domainsearchlist'])) . "\";\n";
811
			}
812

    
813
			if (isset($poolconf['ddnsupdate'])) {
814
				if (($poolconf['ddnsdomain'] <> "") && ($poolconf['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
815
					$pdnscfg .= "		ddns-domainname \"{$poolconf['ddnsdomain']}\";\n";
816
				}
817
				$pdnscfg .= "		ddns-update-style interim;\n";
818
			}
819

    
820
			if (is_array($poolconf['dnsserver']) && ($poolconf['dnsserver'][0]) && ($poolconf['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
821
				$pdnscfg .= "		option domain-name-servers " . join(",", $poolconf['dnsserver']) . ";\n";
822
			}
823
			$dhcpdconf .= "{$pdnscfg}";
824

    
825
			// default-lease-time
826
			if ($poolconf['defaultleasetime'] && ($poolconf['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) {
827
				$dhcpdconf .= "		default-lease-time {$poolconf['defaultleasetime']};\n";
828
			}
829

    
830
			// max-lease-time
831
			if ($poolconf['maxleasetime'] && ($poolconf['maxleasetime'] != $dhcpifconf['maxleasetime'])) {
832
				$dhcpdconf .= "		max-lease-time {$poolconf['maxleasetime']};\n";
833
			}
834

    
835
			// netbios-name*
836
			if (is_array($poolconf['winsserver']) && $poolconf['winsserver'][0] && ($poolconf['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
837
				$dhcpdconf .= "		option netbios-name-servers " . join(",", $poolconf['winsserver']) . ";\n";
838
				$dhcpdconf .= "		option netbios-node-type 8;\n";
839
			}
840

    
841
			// ntp-servers
842
			if (is_array($poolconf['ntpserver']) && $poolconf['ntpserver'][0] && ($poolconf['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
843
				$dhcpdconf .= "		option ntp-servers " . join(",", $poolconf['ntpserver']) . ";\n";
844
			}
845

    
846
			// tftp-server-name
847
			if (!empty($poolconf['tftp']) && ($poolconf['tftp'] != $dhcpifconf['tftp'])) {
848
				$dhcpdconf .= "		option tftp-server-name \"{$poolconf['tftp']}\";\n";
849
			}
850

    
851
			// ldap-server
852
			if (!empty($poolconf['ldap']) && ($poolconf['ldap'] != $dhcpifconf['ldap'])) {
853
				$dhcpdconf .= "		option ldap-server \"{$poolconf['ldap']}\";\n";
854
			}
855

    
856
			// net boot information
857
			if (isset($poolconf['netboot'])) {
858
				if (!empty($poolconf['nextserver']) && ($poolconf['nextserver'] != $dhcpifconf['nextserver'])) {
859
					$dhcpdconf .= "		next-server {$poolconf['nextserver']};\n";
860
				}
861
				if (!empty($poolconf['filename']) && ($poolconf['filename'] != $dhcpifconf['filename'])) {
862
					$dhcpdconf .= "		filename \"{$poolconf['filename']}\";\n";
863
				}
864
				if (!empty($poolconf['rootpath']) && ($poolconf['rootpath'] != $dhcpifconf['rootpath'])) {
865
					$dhcpdconf .= "		option root-path \"{$poolconf['rootpath']}\";\n";
866
				}
867
			}
868
			$dhcpdconf .= "		range {$poolconf['range']['from']} {$poolconf['range']['to']};\n";
869
			$dhcpdconf .= "	}\n\n";
870
		}
871
// End of settings inside pools
872

    
873
		if ($dhcpifconf['gateway'] && $dhcpifconf['gateway'] != "none") {
874
			$routers = $dhcpifconf['gateway'];
875
			$add_routers = true;
876
		} elseif ($dhcpifconf['gateway'] == "none") {
877
			$add_routers = false;
878
		} else {
879
			$add_routers = $enable_add_routers;
880
			$routers = $ifcfgip;
881
		}
882
		if ($add_routers) {
883
			$dhcpdconf .= "	option routers {$routers};\n";
884
		}
885

    
886
		$dhcpdconf .= <<<EOD
887
$dnscfg
888

    
889
EOD;
890
		// default-lease-time
891
		if ($dhcpifconf['defaultleasetime']) {
892
			$dhcpdconf .= "	default-lease-time {$dhcpifconf['defaultleasetime']};\n";
893
		}
894

    
895
		// max-lease-time
896
		if ($dhcpifconf['maxleasetime']) {
897
			$dhcpdconf .= "	max-lease-time {$dhcpifconf['maxleasetime']};\n";
898
		}
899

    
900
		// netbios-name*
901
		if (is_array($dhcpifconf['winsserver']) && $dhcpifconf['winsserver'][0]) {
902
			$dhcpdconf .= "	option netbios-name-servers " . join(",", $dhcpifconf['winsserver']) . ";\n";
903
			$dhcpdconf .= "	option netbios-node-type 8;\n";
904
		}
905

    
906
		// ntp-servers
907
		if (is_array($dhcpifconf['ntpserver']) && $dhcpifconf['ntpserver'][0]) {
908
			$dhcpdconf .= "	option ntp-servers " . join(",", $dhcpifconf['ntpserver']) . ";\n";
909
		}
910

    
911
		// tftp-server-name
912
		if ($dhcpifconf['tftp'] <> "") {
913
			$dhcpdconf .= "	option tftp-server-name \"{$dhcpifconf['tftp']}\";\n";
914
		}
915

    
916
		// Handle option, number rowhelper values
917
		$dhcpdconf .= "\n";
918
		if ($dhcpifconf['numberoptions']['item']) {
919
			foreach ($dhcpifconf['numberoptions']['item'] as $itemidx => $item) {
920
				if (empty($item['type']) || $item['type'] == "text") {
921
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} \"{$item['value']}\";\n";
922
				} else {
923
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} {$item['value']};\n";
924
				}
925
			}
926
		}
927

    
928
		// ldap-server
929
		if ($dhcpifconf['ldap'] <> "") {
930
			$dhcpdconf .= "	option ldap-server \"{$dhcpifconf['ldap']}\";\n";
931
		}
932

    
933
		// net boot information
934
		if (isset($dhcpifconf['netboot'])) {
935
			if ($dhcpifconf['nextserver'] <> "") {
936
				$dhcpdconf .= "	next-server {$dhcpifconf['nextserver']};\n";
937
			}
938
			if (!empty($dhcpifconf['filename']) && !empty($dhcpifconf['filename32']) && !empty($dhcpifconf['filename64'])) {
939
				$dhcpdconf .= "	if option arch = 00:06 {\n";
940
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename32']}\";\n";
941
				$dhcpdconf .= "	} else if option arch = 00:07 {\n";
942
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename64']}\";\n";
943
				$dhcpdconf .= "	} else if option arch = 00:09 {\n";
944
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename64']}\";\n";
945
				$dhcpdconf .= "	} else {\n";
946
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename']}\";\n";
947
				$dhcpdconf .= "	}\n\n";
948
			} elseif (!empty($dhcpifconf['filename'])) {
949
				$dhcpdconf .= "	filename \"{$dhcpifconf['filename']}\";\n";
950
			}
951
			if (!empty($dhcpifconf['rootpath'])) {
952
				$dhcpdconf .= "	option root-path \"{$dhcpifconf['rootpath']}\";\n";
953
			}
954
		}
955

    
956
		$dhcpdconf .= <<<EOD
957
}
958

    
959
EOD;
960

    
961
		/* add static mappings */
962
		if (is_array($dhcpifconf['staticmap'])) {
963

    
964
			$i = 0;
965
			foreach ($dhcpifconf['staticmap'] as $sm) {
966
				$dhcpdconf .= "host s_{$dhcpif}_{$i} {\n";
967

    
968
				if ($sm['mac']) {
969
					$dhcpdconf .= "        hardware ethernet {$sm['mac']};\n";
970
				}
971

    
972
				if ($sm['cid']) {
973
					$dhcpdconf .= "        option dhcp-client-identifier \"{$sm['cid']}\";\n";
974
				}
975

    
976
				if ($sm['ipaddr']) {
977
					$dhcpdconf .= "	fixed-address {$sm['ipaddr']};\n";
978
				}
979

    
980
				if ($sm['hostname']) {
981
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
982
					$dhhostname = str_replace(".", "_", $dhhostname);
983
					$dhcpdconf .= "	option host-name \"{$dhhostname}\";\n";
984
				}
985
				if ($sm['filename']) {
986
					$dhcpdconf .= "	filename \"{$sm['filename']}\";\n";
987
				}
988

    
989
				if ($sm['rootpath']) {
990
					$dhcpdconf .= "	option root-path \"{$sm['rootpath']}\";\n";
991
				}
992

    
993
				if ($sm['gateway'] && ($sm['gateway'] != $dhcpifconf['gateway'])) {
994
					$dhcpdconf .= "	option routers {$sm['gateway']};\n";
995
				}
996

    
997
				$smdnscfg = "";
998

    
999
				if ($sm['domain'] && ($sm['domain'] != $dhcpifconf['domain'])) {
1000
					$smdnscfg .= "	option domain-name \"{$sm['domain']}\";\n";
1001
				}
1002

    
1003
				if (!empty($sm['domainsearchlist']) && ($sm['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
1004
					$smdnscfg .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $sm['domainsearchlist'])) . "\";\n";
1005
				}
1006

    
1007
				if (isset($sm['ddnsupdate'])) {
1008
					if (($sm['ddnsdomain'] <> "") && ($sm['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
1009
						$pdnscfg .= "		ddns-domainname \"{$sm['ddnsdomain']}\";\n";
1010
					}
1011
					$pdnscfg .= "		ddns-update-style interim;\n";
1012
				}
1013

    
1014
				if (is_array($sm['dnsserver']) && ($sm['dnsserver'][0]) && ($sm['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
1015
					$smdnscfg .= "	option domain-name-servers " . join(",", $sm['dnsserver']) . ";\n";
1016
				}
1017
				$dhcpdconf .= "{$smdnscfg}";
1018

    
1019
				// default-lease-time
1020
				if ($sm['defaultleasetime'] && ($sm['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) {
1021
					$dhcpdconf .= "	default-lease-time {$sm['defaultleasetime']};\n";
1022
				}
1023

    
1024
				// max-lease-time
1025
				if ($sm['maxleasetime'] && ($sm['maxleasetime'] != $dhcpifconf['maxleasetime'])) {
1026
					$dhcpdconf .= "	max-lease-time {$sm['maxleasetime']};\n";
1027
				}
1028

    
1029
				// netbios-name*
1030
				if (is_array($sm['winsserver']) && $sm['winsserver'][0] && ($sm['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
1031
					$dhcpdconf .= "	option netbios-name-servers " . join(",", $sm['winsserver']) . ";\n";
1032
					$dhcpdconf .= "	option netbios-node-type 8;\n";
1033
				}
1034

    
1035
				// ntp-servers
1036
				if (is_array($sm['ntpserver']) && $sm['ntpserver'][0] && ($sm['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
1037
					$dhcpdconf .= "	option ntp-servers " . join(",", $sm['ntpserver']) . ";\n";
1038
				}
1039

    
1040
				// tftp-server-name
1041
				if (!empty($sm['tftp']) && ($sm['tftp'] != $dhcpifconf['tftp'])) {
1042
					$dhcpdconf .= "	option tftp-server-name \"{$sm['tftp']}\";\n";
1043
				}
1044

    
1045
				$dhcpdconf .= "}\n";
1046
				$i++;
1047
			}
1048
		}
1049

    
1050
		$dhcpdifs[] = get_real_interface($dhcpif);
1051
		if ($newzone['domain-name']) {
1052
			if ($need_ddns_updates) {
1053
				$newzone['dns-servers'] = array($dhcpifconf['ddnsdomainprimary']);
1054
			}
1055
			$ddns_zones[] = $newzone;
1056
		}
1057
	}
1058

    
1059
	if ($need_ddns_updates) {
1060
		$dhcpdconf .= "ddns-update-style interim;\n";
1061
		$dhcpdconf .= "update-static-leases on;\n";
1062

    
1063
		$dhcpdconf .= dhcpdkey($dhcpifconf);
1064
		$dhcpdconf .= dhcpdzones($ddns_zones, $dhcpifconf);
1065
	}
1066

    
1067
	/* write dhcpd.conf */
1068
	if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpd.conf", $dhcpdconf)) {
1069
		printf(gettext("Error: cannot open dhcpd.conf in services_dhcpdv4_configure().%s"), "\n");
1070
		unset($dhcpdconf);
1071
		return 1;
1072
	}
1073
	unset($dhcpdconf);
1074

    
1075
	/* create an empty leases database */
1076
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases")) {
1077
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases");
1078
	}
1079

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

    
1084
	/* fire up dhcpd in a chroot */
1085
	if (count($dhcpdifs) > 0) {
1086
		mwexec("/usr/local/sbin/dhcpd -user dhcpd -group _dhcp -chroot {$g['dhcpd_chroot_path']} -cf /etc/dhcpd.conf -pf {$g['varrun_path']}/dhcpd.pid " .
1087
			join(" ", $dhcpdifs));
1088
	}
1089

    
1090
	if (platform_booting()) {
1091
		print "done.\n";
1092
	}
1093

    
1094
	return 0;
1095
}
1096

    
1097
function dhcpdkey($dhcpifconf) {
1098
	$dhcpdconf = "";
1099
	if ($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "") {
1100
		$dhcpdconf .= "key {$dhcpifconf['ddnsdomainkeyname']} {\n";
1101
		$dhcpdconf .= "	algorithm hmac-md5;\n";
1102
		$dhcpdconf .= "	secret {$dhcpifconf['ddnsdomainkey']};\n";
1103
		$dhcpdconf .= "}\n";
1104
	}
1105

    
1106
	return $dhcpdconf;
1107
}
1108

    
1109
function dhcpdzones($ddns_zones, $dhcpifconf) {
1110
	$dhcpdconf = "";
1111

    
1112
	if (is_array($ddns_zones)) {
1113
		$added_zones = array();
1114
		foreach ($ddns_zones as $zone) {
1115
			if (!is_array($zone) || empty($zone) || !is_array($zone['dns-servers'])) {
1116
				continue;
1117
			}
1118
			$primary = $zone['dns-servers'][0];
1119
			$secondary = empty($zone['dns-servers'][1]) ? "" : $zone['dns-servers'][1];
1120

    
1121
			// Make sure we aren't using any invalid or IPv6 DNS servers.
1122
			if (!is_ipaddrv4($primary)) {
1123
				if (is_ipaddrv4($secondary)) {
1124
					$primary = $secondary;
1125
					$secondary = "";
1126
				} else {
1127
					continue;
1128
				}
1129
			}
1130

    
1131
			// We don't need to add zones multiple times.
1132
			if ($zone['domain-name'] && !in_array($zone['domain-name'], $added_zones)) {
1133
				$dhcpdconf .= "zone {$zone['domain-name']}. {\n";
1134
				$dhcpdconf .= "	primary {$primary};\n";
1135
				if (is_ipaddrv4($secondary)) {
1136
					$dhcpdconf .= "	secondary {$secondary};\n";
1137
				}
1138
				if ($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "") {
1139
					$dhcpdconf .= "	key {$dhcpifconf['ddnsdomainkeyname']};\n";
1140
				}
1141
				$dhcpdconf .= "}\n";
1142
				$added_zones[] = $zone['domain-name'];
1143
			}
1144
			if ($zone['ptr-domain'] && !in_array($zone['ptr-domain'], $added_zones)) {
1145
				$dhcpdconf .= "zone {$zone['ptr-domain']} {\n";
1146
				$dhcpdconf .= "	primary {$primary};\n";
1147
				if (is_ipaddrv4($secondary)) {
1148
					$dhcpdconf .= "	secondary {$secondary};\n";
1149
				}
1150
				if ($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "") {
1151
					$dhcpdconf .= "	key {$dhcpifconf['ddnsdomainkeyname']};\n";
1152
				}
1153
				$dhcpdconf .= "}\n";
1154
				$added_zones[] = $zone['ptr-domain'];
1155
			}
1156
		}
1157
	}
1158

    
1159
	return $dhcpdconf;
1160
}
1161

    
1162
function services_dhcpdv6_configure($blacklist = array()) {
1163
	global $config, $g;
1164

    
1165
	if ($g['services_dhcp_server_enable'] == false) {
1166
		return;
1167
	}
1168

    
1169
	if (isset($config['system']['developerspew'])) {
1170
		$mt = microtime();
1171
		echo "services_dhcpd_configure($if) being called $mt\n";
1172
	}
1173

    
1174
	/* kill any running dhcpd */
1175
	if (isvalidpid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid")) {
1176
		killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid");
1177
	}
1178
	if (isvalidpid("{$g['varrun_path']}/dhcpleases6.pid")) {
1179
		killbypid("{$g['varrun_path']}/dhcpleases6.pid");
1180
	}
1181

    
1182
	/* DHCP enabled on any interfaces? */
1183
	if (!is_dhcpv6_server_enabled()) {
1184
		return 0;
1185
	}
1186

    
1187
	if (platform_booting()) {
1188
		if ($g['platform'] != $g['product_name']) {
1189
			/* restore the leases, if we have them */
1190
			if (file_exists("{$g['cf_conf_path']}/dhcp6leases.tgz")) {
1191
				$dhcprestore = "";
1192
				$dhcpreturn = "";
1193
				exec("cd /;LANG=C /usr/bin/tar -xzf {$g['cf_conf_path']}/dhcp6leases.tgz 2>&1", $dhcprestore, $dhcpreturn);
1194
				$dhcprestore = implode(" ", $dhcprestore);
1195
				if ($dhcpreturn <> 0) {
1196
					log_error("DHCP leases v6 restore failed exited with $dhcpreturn, the error is: $dhcprestore\n");
1197
				}
1198
			}
1199
		}
1200
	}
1201

    
1202
	$syscfg = $config['system'];
1203
	if (!is_array($config['dhcpdv6'])) {
1204
		$config['dhcpdv6'] = array();
1205
	}
1206
	$dhcpdv6cfg = $config['dhcpdv6'];
1207
	$Iflist = get_configured_interface_list();
1208
	$Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces());
1209

    
1210

    
1211
	if (platform_booting()) {
1212
		echo "Starting DHCPv6 service...";
1213
	} else {
1214
		sleep(1);
1215
	}
1216

    
1217
	/* we add a fake entry for interfaces that are set to track6 another WAN */
1218
	foreach ($Iflist as $ifname) {
1219
		/* Do not put in the config an interface which is down */
1220
		if (isset($blacklist[$ifname])) {
1221
			continue;
1222
		}
1223
		if (!empty($config['interfaces'][$ifname]['track6-interface'])) {
1224
			$realif = get_real_interface($ifname, "inet6");
1225
			$ifcfgipv6 = get_interface_ipv6($ifname);
1226
			if (!is_ipaddrv6($ifcfgipv6)) {
1227
				continue;
1228
			}
1229
			$ifcfgipv6 = Net_IPv6::getNetmask($ifcfgipv6, 64);
1230
			$trackifname = $config['interfaces'][$ifname]['track6-interface'];
1231
			$trackcfg = $config['interfaces'][$trackifname];
1232
			$pdlen = calculate_ipv6_delegation_length($trackifname);
1233
			$ifcfgipv6arr =explode(":", $ifcfgipv6);
1234
			$dhcpdv6cfg[$ifname] = array();
1235
			$dhcpdv6cfg[$ifname]['enable'] = true;
1236
			/* range */
1237
			$ifcfgipv6arr[7] = "1000";
1238
			$dhcpdv6cfg[$ifname]['range'] = array();
1239
			$dhcpdv6cfg[$ifname]['range']['from'] = Net_IPv6::compress(implode(":", $ifcfgipv6arr));
1240
			$ifcfgipv6arr[7] = "2000";
1241
			$dhcpdv6cfg[$ifname]['range']['to'] = Net_IPv6::compress(implode(":", $ifcfgipv6arr));
1242
			/* prefix length > 0? We can add dhcp6 prefix delegation server */
1243
			if ($pdlen > 2) {
1244
				$pdlenmax = $pdlen;
1245
				$pdlenhalf = $pdlenmax -1;
1246
				$pdlenmin = (64 - ceil($pdlenhalf / 4));
1247
				$dhcpdv6cfg[$ifname]['prefixrange'] = array();
1248
				$dhcpdv6cfg[$ifname]['prefixrange']['prefixlength'] = $pdlenmin;
1249

    
1250
				/* set the delegation start to half the current address block */
1251
				$range = Net_IPv6::parseAddress($ifcfgipv6, (64 - $pdlenmax));
1252
				$range['start'] = Net_IPv6::getNetmask($range['end'], (64 - $pdlenhalf));
1253

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

    
1258
				$dhcpdv6cfg[$ifname]['prefixrange']['from'] = Net_IPv6::compress($range['start']);
1259
				$dhcpdv6cfg[$ifname]['prefixrange']['to'] = Net_IPv6::compress($range['end']);
1260
			}
1261
			$dhcpdv6cfg[$ifname]['dns6ip'] = get_interface_ipv6($ifname);
1262
		}
1263
	}
1264

    
1265
	$custoptionsv6 = "";
1266
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1267
		if (is_array($dhcpv6ifconf['numberoptions']) && is_array($dhcpv6ifconf['numberoptions']['item'])) {
1268
			foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
1269
				$custoptionsv6 .= "option custom-{$dhcpv6if}-{$itemv6idx} code {$itemv6['number']} = text;\n";
1270
			}
1271
		}
1272
	}
1273

    
1274
	if (isset($dhcpv6ifconf['netboot']) && !empty($dhcpv6ifconf['bootfile_url'])) {
1275
		$custoptionsv6 .= "option dhcp6.bootfile-url code 59 = string;\n";
1276
	}
1277

    
1278
	$dhcpdv6conf = <<<EOD
1279

    
1280
option domain-name "{$syscfg['domain']}";
1281
option ldap-server code 95 = text;
1282
option domain-search-list code 119 = text;
1283
{$custoptionsv6}
1284
default-lease-time 7200;
1285
max-lease-time 86400;
1286
log-facility local7;
1287
one-lease-per-client true;
1288
deny duplicates;
1289
ping-check true;
1290
update-conflict-detection false;
1291

    
1292
EOD;
1293

    
1294
	if (!isset($dhcpv6ifconf['disableauthoritative'])) {
1295
		$dhcpdv6conf .= "authoritative;\n";
1296
	}
1297

    
1298
	if (isset($dhcpv6ifconf['alwaysbroadcast'])) {
1299
		$dhcpdv6conf .= "always-broadcast on\n";
1300
	}
1301

    
1302
	$dhcpdv6ifs = array();
1303

    
1304
	$dhcpv6num = 0;
1305
	$nsupdate = false;
1306

    
1307
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1308

    
1309
		$ddns_zones = array();
1310

    
1311
		$ifcfgv6 = $config['interfaces'][$dhcpv6if];
1312

    
1313
		if (!isset($dhcpv6ifconf['enable']) || !isset($Iflist[$dhcpv6if]) || !isset($ifcfgv6['enable'])) {
1314
			continue;
1315
		}
1316
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1317
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
1318
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1319

    
1320
		if ($is_olsr_enabled == true) {
1321
			if ($dhcpv6ifconf['netmask']) {
1322
				$subnetmask = gen_subnet_maskv6($dhcpv6ifconf['netmask']);
1323
			}
1324
		}
1325

    
1326
		$dnscfgv6 = "";
1327

    
1328
		if ($dhcpv6ifconf['domain']) {
1329
			$dnscfgv6 .= "	option domain-name \"{$dhcpv6ifconf['domain']}\";\n";
1330
		}
1331

    
1332
		if ($dhcpv6ifconf['domainsearchlist'] <> "") {
1333
			$dnscfgv6 .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpv6ifconf['domainsearchlist'])) . "\";\n";
1334
		}
1335

    
1336
		if (isset($dhcpv6ifconf['ddnsupdate'])) {
1337
			if ($dhcpv6ifconf['ddnsdomain'] <> "") {
1338
				$dnscfgv6 .= "	ddns-domainname \"{$dhcpv6ifconf['ddnsdomain']}\";\n";
1339
			}
1340
			$dnscfgv6 .= "	ddns-update-style interim;\n";
1341
			$nsupdate = true;
1342
		}
1343

    
1344
		if (is_array($dhcpv6ifconf['dnsserver']) && ($dhcpv6ifconf['dnsserver'][0])) {
1345
			$dnscfgv6 .= "	option dhcp6.name-servers " . join(",", $dhcpv6ifconf['dnsserver']) . ";";
1346
		} else if (((isset($config['dnsmasq']['enable'])) || isset($config['unbound']['enable'])) && (is_ipaddrv6($ifcfgipv6))) {
1347
			$dnscfgv6 .= "	option dhcp6.name-servers {$ifcfgipv6};";
1348
		} else if (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
1349
			$dns_arrv6 = array();
1350
			foreach ($syscfg['dnsserver'] as $dnsserver) {
1351
				if (is_ipaddrv6($dnsserver)) {
1352
					$dns_arrv6[] = $dnsserver;
1353
				}
1354
			}
1355
			if (!empty($dns_arrv6)) {
1356
				$dnscfgv6 .= "	option dhcp6.name-servers " . join(",", $dns_arrv6) . ";";
1357
			}
1358
		}
1359

    
1360
		if ($dhcpv6ifconf['domain']) {
1361
			$newzone = array();
1362
			$newzone['domain-name'] = $dhcpv6ifconf['domain'];
1363
			$newzone['dns-servers'][] = $dhcpv6ifconf['ddnsdomainprimary'];
1364
			$ddns_zones[] = $newzone;
1365
		}
1366

    
1367
		if (is_ipaddrv6($ifcfgipv6)) {
1368
			$dhcpdv6conf .= "subnet6 {$subnetv6}/{$ifcfgsnv6}";
1369
		} else {
1370
			$subnet6 = gen_subnetv6($dhcpv6ifconf['range']['from'], "64");
1371
			$dhcpdv6conf .= "subnet6 {$subnet6}/64";
1372
		}
1373
		$dhcpdv6conf .= " {\n";
1374

    
1375
		$dhcpdv6conf .= <<<EOD
1376
	range6 {$dhcpv6ifconf['range']['from']} {$dhcpv6ifconf['range']['to']};
1377
$dnscfgv6
1378

    
1379
EOD;
1380

    
1381
		if (is_ipaddrv6($dhcpv6ifconf['prefixrange']['from']) && is_ipaddrv6($dhcpv6ifconf['prefixrange']['to'])) {
1382
			$dhcpdv6conf .= "	prefix6 {$dhcpv6ifconf['prefixrange']['from']} {$dhcpv6ifconf['prefixrange']['to']} /{$dhcpv6ifconf['prefixrange']['prefixlength']};\n";
1383
		}
1384
		if (is_ipaddrv6($dhcpv6ifconf['dns6ip'])) {
1385
			$dhcpdv6conf .= "	option dhcp6.name-servers {$dhcpv6ifconf['dns6ip']};\n";
1386
		}
1387
		// default-lease-time
1388
		if ($dhcpv6ifconf['defaultleasetime']) {
1389
			$dhcpdv6conf .= "	default-lease-time {$dhcpv6ifconf['defaultleasetime']};\n";
1390
		}
1391

    
1392
		// max-lease-time
1393
		if ($dhcpv6ifconf['maxleasetime']) {
1394
			$dhcpdv6conf .= "	max-lease-time {$dhcpv6ifconf['maxleasetime']};\n";
1395
		}
1396

    
1397
		// ntp-servers
1398
		if (is_array($dhcpv6ifconf['ntpserver']) && $dhcpv6ifconf['ntpserver'][0]) {
1399
			$ntpservers = array();
1400
			foreach ($dhcpv6ifconf['ntpserver'] as $ntpserver) {
1401
				if (is_ipaddrv6($ntpserver)) {
1402
					$ntpservers[] = $ntpserver;
1403
				}
1404
			}
1405
			if (count($ntpservers) > 0) {
1406
				$dhcpdv6conf .= "       option dhcp6.sntp-servers " . join(",", $dhcpv6ifconf['ntpserver']) . ";\n";
1407
			}
1408
		}
1409
		// tftp-server-name
1410
		/* Needs ISC DHCPD support
1411
		 if ($dhcpv6ifconf['tftp'] <> "") {
1412
			$dhcpdv6conf .= "	option tftp-server-name \"{$dhcpv6ifconf['tftp']}\";\n";
1413
		 }
1414
		*/
1415

    
1416
		// Handle option, number rowhelper values
1417
		$dhcpdv6conf .= "\n";
1418
		if ($dhcpv6ifconf['numberoptions']['item']) {
1419
			foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
1420
				$dhcpdv6conf .= "	option custom-{$dhcpv6if}-{$itemv6idx} \"{$itemv6['value']}\";\n";
1421
			}
1422
		}
1423

    
1424
		// ldap-server
1425
		if ($dhcpv6ifconf['ldap'] <> "") {
1426
			$dhcpdv6conf .= "	option ldap-server \"{$dhcpv6ifconf['ldap']}\";\n";
1427
		}
1428

    
1429
		// net boot information
1430
		if (isset($dhcpv6ifconf['netboot'])) {
1431
			if (!empty($dhcpv6ifconf['bootfile_url'])) {
1432
				$dhcpdv6conf .= "	option dhcp6.bootfile-url \"{$dhcpv6ifconf['bootfile_url']}\";\n";
1433
			}
1434
		}
1435

    
1436
		$dhcpdv6conf .= "}\n";
1437

    
1438
		/* add static mappings */
1439
		/* Needs to use DUID */
1440
		if (is_array($dhcpv6ifconf['staticmap'])) {
1441
			$i = 0;
1442
			foreach ($dhcpv6ifconf['staticmap'] as $sm) {
1443
				$dhcpdv6conf .= <<<EOD
1444
host s_{$dhcpv6if}_{$i} {
1445
	host-identifier option dhcp6.client-id {$sm['duid']};
1446

    
1447
EOD;
1448
				if ($sm['ipaddrv6']) {
1449
					$dhcpdv6conf .= "	fixed-address6 {$sm['ipaddrv6']};\n";
1450
				}
1451

    
1452
				if ($sm['hostname']) {
1453
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
1454
					$dhhostname = str_replace(".", "_", $dhhostname);
1455
					$dhcpdv6conf .= "	option host-name {$dhhostname};\n";
1456
				}
1457
				if ($sm['filename']) {
1458
					$dhcpdv6conf .= "	filename \"{$sm['filename']}\";\n";
1459
				}
1460

    
1461
				if ($sm['rootpath']) {
1462
					$dhcpdv6conf .= "	option root-path \"{$sm['rootpath']}\";\n";
1463
				}
1464

    
1465
				$dhcpdv6conf .= "}\n";
1466
				$i++;
1467
			}
1468
		}
1469

    
1470
		if ($dhcpv6ifconf['domain']) {
1471
			$dhcpdv6conf .= dhcpdkey($dhcpv6ifconf);
1472
			$dhcpdv6conf .= dhcpdzones($ddns_zones, $dhcpv6ifconf);
1473
		}
1474

    
1475
		if ($config['dhcpdv6'][$dhcpv6if]['ramode'] <> "unmanaged" && isset($config['interfaces'][$dhcpv6if]['enable'])) {
1476
			if (preg_match("/poes/si", $dhcpv6if)) {
1477
				/* magic here */
1478
				$dhcpdv6ifs = array_merge($dhcpdv6ifs, get_pppoes_child_interfaces($dhcpv6if));
1479
			} else {
1480
				$realif = get_real_interface($dhcpv6if, "inet6");
1481
				if (stristr("$realif", "bridge")) {
1482
					$mac = get_interface_mac($realif);
1483
					$v6address = generate_ipv6_from_mac($mac);
1484
					/* Create link local address for bridges */
1485
					mwexec("/sbin/ifconfig {$realif} inet6 {$v6address}");
1486
				}
1487
				$realif = escapeshellcmd($realif);
1488
				$dhcpdv6ifs[] = $realif;
1489
			}
1490
		}
1491
	}
1492

    
1493
	if ($nsupdate) {
1494
		$dhcpdv6conf .= "ddns-update-style interim;\n";
1495
	} else {
1496
		$dhcpdv6conf .= "ddns-update-style none;\n";
1497
	}
1498

    
1499
	/* write dhcpdv6.conf */
1500
	if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf", $dhcpdv6conf)) {
1501
		log_error("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n");
1502
		if (platform_booting()) {
1503
			printf("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n");
1504
		}
1505
		unset($dhcpdv6conf);
1506
		return 1;
1507
	}
1508
	unset($dhcpdv6conf);
1509

    
1510
	/* create an empty leases v6 database */
1511
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases")) {
1512
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases");
1513
	}
1514

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

    
1519
	/* fire up dhcpd in a chroot */
1520
	if (count($dhcpdv6ifs) > 0) {
1521
		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 " .
1522
			join(" ", $dhcpdv6ifs));
1523
		mwexec("/usr/local/sbin/dhcpleases6 -c \"/usr/local/bin/php-cgi -f /usr/local/sbin/prefixes.php|/bin/sh\" -l {$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases");
1524
	}
1525
	if (platform_booting()) {
1526
		print gettext("done.") . "\n";
1527
	}
1528

    
1529
	return 0;
1530
}
1531

    
1532
function services_igmpproxy_configure() {
1533
	global $config, $g;
1534

    
1535
	/* kill any running igmpproxy */
1536
	killbyname("igmpproxy");
1537

    
1538
	if (!is_array($config['igmpproxy']['igmpentry']) || (count($config['igmpproxy']['igmpentry']) == 0)) {
1539
		return 1;
1540
	}
1541

    
1542
	$iflist = get_configured_interface_list();
1543

    
1544
	$igmpconf = <<<EOD
1545

    
1546
##------------------------------------------------------
1547
## Enable Quickleave mode (Sends Leave instantly)
1548
##------------------------------------------------------
1549
quickleave
1550

    
1551
EOD;
1552

    
1553
	foreach ($config['igmpproxy']['igmpentry'] as $igmpcf) {
1554
		unset($iflist[$igmpcf['ifname']]);
1555
		$realif = get_real_interface($igmpcf['ifname']);
1556
		if (empty($igmpcf['threshold'])) {
1557
			$threshld = 1;
1558
		} else {
1559
			$threshld = $igmpcf['threshold'];
1560
		}
1561
		$igmpconf .= "phyint {$realif} {$igmpcf['type']} ratelimit 0 threshold {$threshld}\n";
1562

    
1563
		if ($igmpcf['address'] <> "") {
1564
			$item = explode(" ", $igmpcf['address']);
1565
			foreach ($item as $iww) {
1566
				$igmpconf .= "altnet {$iww}\n";
1567
			}
1568
		}
1569
		$igmpconf .= "\n";
1570
	}
1571
	foreach ($iflist as $ifn) {
1572
		$realif = get_real_interface($ifn);
1573
		$igmpconf .= "phyint {$realif} disabled\n";
1574
	}
1575
	$igmpconf .= "\n";
1576

    
1577
	$igmpfl = fopen($g['tmp_path'] . "/igmpproxy.conf", "w");
1578
	if (!$igmpfl) {
1579
		log_error(gettext("Could not write Igmpproxy configuration file!"));
1580
		return;
1581
	}
1582
	fwrite($igmpfl, $igmpconf);
1583
	fclose($igmpfl);
1584
	unset($igmpconf);
1585

    
1586
	mwexec_bg("/usr/local/sbin/igmpproxy -v {$g['tmp_path']}/igmpproxy.conf");
1587
	log_error(gettext("Started IGMP proxy service."));
1588

    
1589
	return 0;
1590
}
1591

    
1592
function services_dhcrelay_configure() {
1593
	global $config, $g;
1594

    
1595
	if (isset($config['system']['developerspew'])) {
1596
		$mt = microtime();
1597
		echo "services_dhcrelay_configure() being called $mt\n";
1598
	}
1599

    
1600
	/* kill any running dhcrelay */
1601
	killbypid("{$g['varrun_path']}/dhcrelay.pid");
1602

    
1603
	$dhcrelaycfg =& $config['dhcrelay'];
1604

    
1605
	/* DHCPRelay enabled on any interfaces? */
1606
	if (!isset($dhcrelaycfg['enable'])) {
1607
		return 0;
1608
	}
1609

    
1610
	if (platform_booting()) {
1611
		echo gettext("Starting DHCP relay service...");
1612
	} else {
1613
		sleep(1);
1614
	}
1615

    
1616
	$iflist = get_configured_interface_list();
1617

    
1618
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1619
	foreach ($dhcifaces as $dhcrelayif) {
1620
		if (!isset($iflist[$dhcrelayif]) ||
1621
		    link_interface_to_bridge($dhcrelayif)) {
1622
			continue;
1623
		}
1624

    
1625
		if (is_ipaddr(get_interface_ip($dhcrelayif))) {
1626
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1627
		}
1628
	}
1629

    
1630
	/*
1631
	 * In order for the relay to work, it needs to be active
1632
	 * on the interface in which the destination server sits.
1633
	 */
1634
	$srvips = explode(",", $dhcrelaycfg['server']);
1635
	if (!is_array($srvips)) {
1636
		log_error("No destination IP has been configured!");
1637
		return;
1638
	}
1639

    
1640
	foreach ($srvips as $srcidx => $srvip) {
1641
		unset($destif);
1642
		foreach ($iflist as $ifname) {
1643
			$subnet = get_interface_ip($ifname);
1644
			if (!is_ipaddr($subnet)) {
1645
				continue;
1646
			}
1647
			$subnet .= "/" . get_interface_subnet($ifname);
1648
			if (ip_in_subnet($srvip, $subnet)) {
1649
				$destif = get_real_interface($ifname);
1650
				break;
1651
			}
1652
		}
1653
		if (!isset($destif)) {
1654
			foreach (get_staticroutes() as $rtent) {
1655
				if (ip_in_subnet($srvip, $rtent['network'])) {
1656
					$a_gateways = return_gateways_array(true);
1657
					$destif = $a_gateways[$rtent['gateway']]['interface'];
1658
					break;
1659
				}
1660
			}
1661
		}
1662

    
1663
		if (!isset($destif)) {
1664
			/* Create a array from the existing route table */
1665
			exec("/usr/bin/netstat -rnWf inet", $route_str);
1666
			array_shift($route_str);
1667
			array_shift($route_str);
1668
			array_shift($route_str);
1669
			array_shift($route_str);
1670
			$route_arr = array();
1671
			foreach ($route_str as $routeline) {
1672
				$items = preg_split("/[ ]+/i", $routeline);
1673
				if (is_subnetv4($items[0])) {
1674
					$subnet = $items[0];
1675
				} elseif (is_ipaddrv4($items[0])) {
1676
					$subnet = "{$items[0]}/32";
1677
				} else {
1678
					// Not a subnet or IP address, skip to the next line.
1679
					continue;
1680
				}
1681
				if (ip_in_subnet($srvip, $subnet)) {
1682
					$destif = trim($items[6]);
1683
					break;
1684
				}
1685
			}
1686
		}
1687

    
1688
		if (!isset($destif)) {
1689
			if (is_array($config['gateways']['gateway_item'])) {
1690
				foreach ($config['gateways']['gateway_item'] as $gateway) {
1691
					if (isset($gateway['defaultgw'])) {
1692
						$destif = get_real_interface($gateway['interface']);
1693
						break;
1694
					}
1695
				}
1696
			} else {
1697
				$destif = get_real_interface("wan");
1698
			}
1699
		}
1700

    
1701
		if (!empty($destif)) {
1702
			$dhcrelayifs[] = $destif;
1703
		}
1704
	}
1705
	$dhcrelayifs = array_unique($dhcrelayifs);
1706

    
1707
	/* fire up dhcrelay */
1708
	if (empty($dhcrelayifs)) {
1709
		log_error("No suitable interface found for running dhcrelay!");
1710
		return; /* XXX */
1711
	}
1712

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

    
1715
	if (isset($dhcrelaycfg['agentoption'])) {
1716
		$cmd .= " -a -m replace";
1717
	}
1718

    
1719
	$cmd .= " " . implode(" ", $srvips);
1720
	mwexec($cmd);
1721
	unset($cmd);
1722

    
1723
	return 0;
1724
}
1725

    
1726
function services_dhcrelay6_configure() {
1727
	global $config, $g;
1728

    
1729
	if (isset($config['system']['developerspew'])) {
1730
		$mt = microtime();
1731
		echo "services_dhcrelay6_configure() being called $mt\n";
1732
	}
1733

    
1734
	/* kill any running dhcrelay */
1735
	killbypid("{$g['varrun_path']}/dhcrelay6.pid");
1736

    
1737
	$dhcrelaycfg =& $config['dhcrelay6'];
1738

    
1739
	/* DHCPv6 Relay enabled on any interfaces? */
1740
	if (!isset($dhcrelaycfg['enable'])) {
1741
		return 0;
1742
	}
1743

    
1744
	if (platform_booting()) {
1745
		echo gettext("Starting DHCPv6 relay service...");
1746
	} else {
1747
		sleep(1);
1748
	}
1749

    
1750
	$iflist = get_configured_interface_list();
1751

    
1752
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1753
	foreach ($dhcifaces as $dhcrelayif) {
1754
		if (!isset($iflist[$dhcrelayif]) ||
1755
		    link_interface_to_bridge($dhcrelayif)) {
1756
			continue;
1757
		}
1758

    
1759
		if (is_ipaddrv6(get_interface_ipv6($dhcrelayif))) {
1760
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1761
		}
1762
	}
1763
	$dhcrelayifs = array_unique($dhcrelayifs);
1764

    
1765
	/*
1766
	 * In order for the relay to work, it needs to be active
1767
	 * on the interface in which the destination server sits.
1768
	 */
1769
	$srvips = explode(",", $dhcrelaycfg['server']);
1770
	$srvifaces = array();
1771
	foreach ($srvips as $srcidx => $srvip) {
1772
		unset($destif);
1773
		foreach ($iflist as $ifname) {
1774
			$subnet = get_interface_ipv6($ifname);
1775
			if (!is_ipaddrv6($subnet)) {
1776
				continue;
1777
			}
1778
			$subnet .= "/" . get_interface_subnetv6($ifname);
1779
			if (ip_in_subnet($srvip, $subnet)) {
1780
				$destif = get_real_interface($ifname);
1781
				break;
1782
			}
1783
		}
1784
		if (!isset($destif)) {
1785
			if (is_array($config['staticroutes']['route'])) {
1786
				foreach ($config['staticroutes']['route'] as $rtent) {
1787
					if (ip_in_subnet($srvip, $rtent['network'])) {
1788
						$a_gateways = return_gateways_array(true);
1789
						$destif = $a_gateways[$rtent['gateway']]['interface'];
1790
						break;
1791
					}
1792
				}
1793
			}
1794
		}
1795

    
1796
		if (!isset($destif)) {
1797
			/* Create a array from the existing route table */
1798
			exec("/usr/bin/netstat -rnWf inet6", $route_str);
1799
			array_shift($route_str);
1800
			array_shift($route_str);
1801
			array_shift($route_str);
1802
			array_shift($route_str);
1803
			$route_arr = array();
1804
			foreach ($route_str as $routeline) {
1805
				$items = preg_split("/[ ]+/i", $routeline);
1806
				if (ip_in_subnet($srvip, $items[0])) {
1807
					$destif = trim($items[6]);
1808
					break;
1809
				}
1810
			}
1811
		}
1812

    
1813
		if (!isset($destif)) {
1814
			if (is_array($config['gateways']['gateway_item'])) {
1815
				foreach ($config['gateways']['gateway_item'] as $gateway) {
1816
					if (isset($gateway['defaultgw'])) {
1817
						$destif = get_real_interface($gateway['interface']);
1818
						break;
1819
					}
1820
				}
1821
			} else {
1822
				$destif = get_real_interface("wan");
1823
			}
1824
		}
1825

    
1826
		if (!empty($destif)) {
1827
			$srvifaces[] = "{$srvip}%{$destif}";
1828
		}
1829
	}
1830

    
1831
	/* fire up dhcrelay */
1832
	if (empty($dhcrelayifs) || empty($srvifaces)) {
1833
		log_error("No suitable interface found for running dhcrelay -6!");
1834
		return; /* XXX */
1835
	}
1836

    
1837
	$cmd = "/usr/local/sbin/dhcrelay -6 -pf \"{$g['varrun_path']}/dhcrelay6.pid\"";
1838
	foreach ($dhcrelayifs as $dhcrelayif) {
1839
		$cmd .= " -l {$dhcrelayif}";
1840
	}
1841
	foreach ($srvifaces as $srviface) {
1842
		$cmd .= " -u \"{$srviface}\"";
1843
	}
1844
	mwexec($cmd);
1845
	unset($cmd);
1846

    
1847
	return 0;
1848
}
1849

    
1850
function services_dyndns_configure_client($conf) {
1851

    
1852
	if (!isset($conf['enable'])) {
1853
		return;
1854
	}
1855

    
1856
	/* load up the dyndns.class */
1857
	require_once("dyndns.class");
1858

    
1859
	$dns = new updatedns($dnsService = $conf['type'],
1860
		$dnsHost = $conf['host'],
1861
		$dnsUser = $conf['username'],
1862
		$dnsPass = $conf['password'],
1863
		$dnsWildcard = $conf['wildcard'],
1864
		$dnsMX = $conf['mx'],
1865
		$dnsIf = "{$conf['interface']}",
1866
		$dnsBackMX = NULL,
1867
		$dnsServer = NULL,
1868
		$dnsPort = NULL,
1869
		$dnsUpdateURL = "{$conf['updateurl']}",
1870
		$forceUpdate = $conf['force'],
1871
		$dnsZoneID = $conf['zoneid'],
1872
		$dnsTTL = $conf['ttl'],
1873
		$dnsResultMatch = "{$conf['resultmatch']}",
1874
		$dnsRequestIf = "{$conf['requestif']}",
1875
		$dnsID = "{$conf['id']}",
1876
		$dnsVerboseLog = $conf['verboselog'],
1877
		$curlIpresolveV4 = $conf['curl_ipresolve_v4'],
1878
		$curlSslVerifypeer = $conf['curl_ssl_verifypeer']);
1879
}
1880

    
1881
function services_dyndns_configure($int = "") {
1882
	global $config, $g;
1883
	if (isset($config['system']['developerspew'])) {
1884
		$mt = microtime();
1885
		echo "services_dyndns_configure() being called $mt\n";
1886
	}
1887

    
1888
	$dyndnscfg = $config['dyndnses']['dyndns'];
1889
	$gwgroups = return_gateway_groups_array();
1890
	if (is_array($dyndnscfg)) {
1891
		if (platform_booting()) {
1892
			echo gettext("Starting DynDNS clients...");
1893
		}
1894

    
1895
		foreach ($dyndnscfg as $dyndns) {
1896
			if ((empty($int)) || ($int == $dyndns['interface']) || (is_array($gwgroups[$dyndns['interface']]))) {
1897
				$dyndns['verboselog'] = isset($dyndns['verboselog']);
1898
				$dyndns['curl_ipresolve_v4'] = isset($dyndns['curl_ipresolve_v4']);
1899
				$dyndns['curl_ssl_verifypeer'] = isset($dyndns['curl_ssl_verifypeer']);
1900
				services_dyndns_configure_client($dyndns);
1901
				sleep(1);
1902
			}
1903
		}
1904

    
1905
		if (platform_booting()) {
1906
			echo gettext("done.") . "\n";
1907
		}
1908
	}
1909

    
1910
	return 0;
1911
}
1912

    
1913
function dyndnsCheckIP($int) {
1914
	global $config;
1915
	$ip_address = get_interface_ip($int);
1916
	if (is_private_ip($ip_address)) {
1917
		$gateways_status = return_gateways_status(true);
1918
		// If the gateway for this interface is down, then the external check cannot work.
1919
		// Avoid the long wait for the external check to timeout.
1920
		if (stristr($gateways_status[$config['interfaces'][$int]['gateway']]['status'], "down")) {
1921
			return "down";
1922
		}
1923
		$hosttocheck = "http://checkip.dyndns.org";
1924
		$ip_ch = curl_init($hosttocheck);
1925
		curl_setopt($ip_ch, CURLOPT_RETURNTRANSFER, 1);
1926
		curl_setopt($ip_ch, CURLOPT_SSL_VERIFYPEER, FALSE);
1927
		curl_setopt($ip_ch, CURLOPT_INTERFACE, 'host!' . $ip_address);
1928
		curl_setopt($ip_ch, CURLOPT_CONNECTTIMEOUT, '30');
1929
		curl_setopt($ip_ch, CURLOPT_TIMEOUT, 120);
1930
		curl_setopt($ip_ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1931
		$ip_result_page = curl_exec($ip_ch);
1932
		curl_close($ip_ch);
1933
		$ip_result_decoded = urldecode($ip_result_page);
1934
		preg_match('=Current IP Address: (.*)</body>=siU', $ip_result_decoded, $matches);
1935
		$ip_address = trim($matches[1]);
1936
	}
1937
	return $ip_address;
1938
}
1939

    
1940
function services_dnsmasq_configure() {
1941
	global $config, $g;
1942
	$return = 0;
1943

    
1944
	// hard coded args: will be removed to avoid duplication if specified in custom_options
1945
	$standard_args = array(
1946
		"dns-forward-max" => "--dns-forward-max=5000",
1947
		"cache-size" => "--cache-size=10000",
1948
		"local-ttl" => "--local-ttl=1"
1949
	);
1950

    
1951

    
1952
	if (isset($config['system']['developerspew'])) {
1953
		$mt = microtime();
1954
		echo "services_dnsmasq_configure() being called $mt\n";
1955
	}
1956

    
1957
	/* kill any running dnsmasq */
1958
	if (file_exists("{$g['varrun_path']}/dnsmasq.pid")) {
1959
		sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM");
1960
	}
1961

    
1962
	if (isset($config['dnsmasq']['enable'])) {
1963

    
1964
		if (platform_booting()) {
1965
			echo gettext("Starting DNS forwarder...");
1966
		} else {
1967
			sleep(1);
1968
		}
1969

    
1970
		/* generate hosts file */
1971
		if (system_hosts_generate() != 0) {
1972
			$return = 1;
1973
		}
1974

    
1975
		$args = "";
1976

    
1977
		if (isset($config['dnsmasq']['regdhcp'])) {
1978
			$args .= " --dhcp-hostsfile={$g['varetc_path']}/hosts ";
1979
		}
1980

    
1981
		/* Setup listen port, if non-default */
1982
		if (is_port($config['dnsmasq']['port'])) {
1983
			$args .= " --port={$config['dnsmasq']['port']} ";
1984
		}
1985

    
1986
		$listen_addresses = "";
1987
		if (isset($config['dnsmasq']['interface'])) {
1988
			$interfaces = explode(",", $config['dnsmasq']['interface']);
1989
			foreach ($interfaces as $interface) {
1990
				if (is_ipaddrv4($interface)) {
1991
					$listen_addresses .= " --listen-address={$interface} ";
1992
				} else if (is_ipaddrv6($interface)) {
1993
					/*
1994
					 * XXX: Since dnsmasq does not support link-local address
1995
					 * with scope specified. These checks are being done.
1996
					 */
1997
					if (is_linklocal($interface) && strstr($interface, "%")) {
1998
						$tmpaddrll6 = explode("%", $interface);
1999
						$listen_addresses .= " --listen-address={$tmpaddrll6[0]} ";
2000
					} else {
2001
						$listen_addresses .= " --listen-address={$interface} ";
2002
					}
2003
				} else if (strstr($interface, "_vip")) {
2004
					$laddr = get_configured_carp_interface_list($interface);
2005
					if (is_ipaddr($laddr)) {
2006
						$listen_addresses .= " --listen-address={$laddr} ";
2007
					}
2008
				} else {
2009
					$if = get_real_interface($interface);
2010
					if (does_interface_exist($if)) {
2011
						$laddr = get_interface_ip($interface);
2012
						if (is_ipaddrv4($laddr)) {
2013
							$listen_addresses .= " --listen-address={$laddr} ";
2014
						}
2015
						$laddr6 = get_interface_ipv6($interface);
2016
						if (is_ipaddrv6($laddr6) && !isset($config['dnsmasq']['strictbind'])) {
2017
							/*
2018
							 * XXX: Since dnsmasq does not support link-local address
2019
							 * with scope specified. These checks are being done.
2020
							 */
2021
							if (is_linklocal($laddr6) && strstr($laddr6, "%")) {
2022
								$tmpaddrll6 = explode("%", $laddr6);
2023
								$listen_addresses .= " --listen-address={$tmpaddrll6[0]} ";
2024
							} else {
2025
								$listen_addresses .= " --listen-address={$laddr6} ";
2026
							}
2027
						}
2028
					}
2029
				}
2030
			}
2031
			if (!empty($listen_addresses)) {
2032
				$args .= " {$listen_addresses} ";
2033
				if (isset($config['dnsmasq']['strictbind'])) {
2034
					$args .= " --bind-interfaces ";
2035
				}
2036
			}
2037
		}
2038

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

    
2046
			// Build an array of domain overrides to help in checking for matches.
2047
			$override_a = array();
2048
			if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2049
				foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2050
					$override_a[$override['domain']] = "y";
2051
				}
2052
			}
2053

    
2054
			// Build an array of the private reverse lookup domain names
2055
			$reverse_domain_a = array("10.in-addr.arpa", "168.192.in-addr.arpa");
2056
			// Unfortunately the 172.16.0.0/12 range does not map nicely to the in-addr.arpa scheme.
2057
			for ($subnet_num = 16; $subnet_num < 32; $subnet_num++) {
2058
				$reverse_domain_a[] = "$subnet_num.172.in-addr.arpa";
2059
			}
2060

    
2061
			// Set the --server parameter to nowhere for each reverse domain name that was not specifically specified in a domain override.
2062
			foreach ($reverse_domain_a as $reverse_domain) {
2063
				if (!isset($override_a[$reverse_domain])) {
2064
					$args .= " --server=/$reverse_domain/ ";
2065
				}
2066
			}
2067
			unset($override_a);
2068
			unset($reverse_domain_a);
2069
		}
2070

    
2071
		/* Setup forwarded domains */
2072
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2073
			foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2074
				if ($override['ip'] == "!") {
2075
					$override[ip] = "";
2076
				}
2077
				$args .= ' --server=/' . $override['domain'] . '/' . $override['ip'];
2078
			}
2079
		}
2080

    
2081
		/* Allow DNS Rebind for forwarded domains */
2082
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2083
			if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
2084
				foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2085
					$args .= ' --rebind-domain-ok=/' . $override['domain'] . '/ ';
2086
				}
2087
			}
2088
		}
2089

    
2090
		if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
2091
			$dns_rebind = "--rebind-localhost-ok --stop-dns-rebind";
2092
		}
2093

    
2094
		if (isset($config['dnsmasq']['strict_order'])) {
2095
			$args .= " --strict-order ";
2096
		}
2097

    
2098
		if (isset($config['dnsmasq']['domain_needed'])) {
2099
			$args .= " --domain-needed ";
2100
		}
2101

    
2102
		if ($config['dnsmasq']['custom_options']) {
2103
			foreach (preg_split('/\s+/', $config['dnsmasq']['custom_options']) as $c) {
2104
				$args .= " " . escapeshellarg("--{$c}");
2105
				$p = explode('=', $c);
2106
				if (array_key_exists($p[0], $standard_args)) {
2107
					unset($standard_args[$p[0]]);
2108
				}
2109
			}
2110
		}
2111
		$args .= ' ' . implode(' ', array_values($standard_args));
2112

    
2113
		/* run dnsmasq */
2114
		$cmd = "/usr/local/sbin/dnsmasq --all-servers {$dns_rebind} {$args}";
2115
		//log_error("dnsmasq command: {$cmd}");
2116
		mwexec_bg($cmd);
2117
		unset($args);
2118

    
2119
		system_dhcpleases_configure();
2120

    
2121
		if (platform_booting()) {
2122
			echo gettext("done.") . "\n";
2123
		}
2124
	}
2125

    
2126
	if (!platform_booting()) {
2127
		if (services_dhcpd_configure() != 0) {
2128
			$return = 1;
2129
		}
2130
	}
2131

    
2132
	return $return;
2133
}
2134

    
2135
function services_unbound_configure() {
2136
	global $config, $g;
2137
	$return = 0;
2138

    
2139
	if (isset($config['system']['developerspew'])) {
2140
		$mt = microtime();
2141
		echo "services_unbound_configure() being called $mt\n";
2142
	}
2143

    
2144
	// kill any running Unbound instance
2145
	if (file_exists("{$g['varrun_path']}/unbound.pid")) {
2146
		sigkillbypid("{$g['varrun_path']}/unbound.pid", "TERM");
2147
	}
2148

    
2149
	if (isset($config['unbound']['enable'])) {
2150
		if (platform_booting()) {
2151
			echo gettext("Starting DNS Resolver...");
2152
		} else {
2153
			sleep(1);
2154
		}
2155

    
2156
		/* generate hosts file */
2157
		if (system_hosts_generate() != 0) {
2158
			$return = 1;
2159
		}
2160

    
2161
		require_once('/etc/inc/unbound.inc');
2162
		sync_unbound_service();
2163
		if (platform_booting()) {
2164
			echo gettext("done.") . "\n";
2165
		}
2166

    
2167
		system_dhcpleases_configure();
2168
	}
2169

    
2170
	if (!platform_booting()) {
2171
		if (services_dhcpd_configure() != 0) {
2172
			$return = 1;
2173
		}
2174
	}
2175

    
2176
	return $return;
2177
}
2178

    
2179
function services_snmpd_configure() {
2180
	global $config, $g;
2181
	if (isset($config['system']['developerspew'])) {
2182
		$mt = microtime();
2183
		echo "services_snmpd_configure() being called $mt\n";
2184
	}
2185

    
2186
	/* kill any running snmpd */
2187
	sigkillbypid("{$g['varrun_path']}/snmpd.pid", "TERM");
2188
	sleep(2);
2189
	if (is_process_running("bsnmpd")) {
2190
		mwexec("/usr/bin/killall bsnmpd", true);
2191
	}
2192

    
2193
	if (isset($config['snmpd']['enable'])) {
2194

    
2195
		if (platform_booting()) {
2196
			echo gettext("Starting SNMP daemon... ");
2197
		}
2198

    
2199
		/* generate snmpd.conf */
2200
		$fd = fopen("{$g['varetc_path']}/snmpd.conf", "w");
2201
		if (!$fd) {
2202
			printf(gettext("Error: cannot open snmpd.conf in services_snmpd_configure().%s"), "\n");
2203
			return 1;
2204
		}
2205

    
2206

    
2207
		$snmpdconf = <<<EOD
2208
location := "{$config['snmpd']['syslocation']}"
2209
contact := "{$config['snmpd']['syscontact']}"
2210
read := "{$config['snmpd']['rocommunity']}"
2211

    
2212
EOD;
2213

    
2214
/* No docs on what write strings do there for disable for now.
2215
		if (isset($config['snmpd']['rwenable']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2216
			$snmpdconf .= <<<EOD
2217
# write string
2218
write := "{$config['snmpd']['rwcommunity']}"
2219

    
2220
EOD;
2221
		}
2222
*/
2223

    
2224

    
2225
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2226
			$snmpdconf .= <<<EOD
2227
# SNMP Trap support.
2228
traphost := {$config['snmpd']['trapserver']}
2229
trapport := {$config['snmpd']['trapserverport']}
2230
trap := "{$config['snmpd']['trapstring']}"
2231

    
2232

    
2233
EOD;
2234
		}
2235

    
2236
		$platform = trim(file_get_contents('/etc/platform'));
2237
		if (($platform == "pfSense") && ($g['product_name'] != "pfSense")) {
2238
			$platform = $g['product_name'];
2239
		}
2240
		$sysDescr = "{$g['product_name']} " . php_uname("n") .
2241
			" {$g['product_version']} {$platform} " . php_uname("s") .
2242
			" " . php_uname("r") . " " . php_uname("m");
2243

    
2244
		$snmpdconf .= <<<EOD
2245
system := 1     # pfSense
2246
%snmpd
2247
sysDescr			= "{$sysDescr}"
2248
begemotSnmpdDebugDumpPdus       = 2
2249
begemotSnmpdDebugSyslogPri      = 7
2250
begemotSnmpdCommunityString.0.1 = $(read)
2251

    
2252
EOD;
2253

    
2254
/* No docs on what write strings do there for disable for now.
2255
		if (isset($config['snmpd']['rwcommunity']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2256
			$snmpdconf .= <<<EOD
2257
begemotSnmpdCommunityString.0.2 = $(write)
2258

    
2259
EOD;
2260
		}
2261
*/
2262

    
2263

    
2264
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2265
			$snmpdconf .= <<<EOD
2266
begemotTrapSinkStatus.[$(traphost)].$(trapport) = 4
2267
begemotTrapSinkVersion.[$(traphost)].$(trapport) = 2
2268
begemotTrapSinkComm.[$(traphost)].$(trapport) = $(trap)
2269

    
2270
EOD;
2271
		}
2272

    
2273

    
2274
		$snmpdconf .= <<<EOD
2275
begemotSnmpdCommunityDisable    = 1
2276

    
2277
EOD;
2278

    
2279
		$bind_to_ip = "0.0.0.0";
2280
		if (isset($config['snmpd']['bindip'])) {
2281
			if (is_ipaddr($config['snmpd']['bindip'])) {
2282
				$bind_to_ip = $config['snmpd']['bindip'];
2283
			} else {
2284
				$if = get_real_interface($config['snmpd']['bindip']);
2285
				if (does_interface_exist($if)) {
2286
					$bind_to_ip = get_interface_ip($config['snmpd']['bindip']);
2287
				}
2288
			}
2289
		}
2290

    
2291
		if (is_port($config['snmpd']['pollport'])) {
2292
			$snmpdconf .= <<<EOD
2293
begemotSnmpdPortStatus.{$bind_to_ip}.{$config['snmpd']['pollport']} = 1
2294

    
2295
EOD;
2296

    
2297
		}
2298

    
2299
		$snmpdconf .= <<<EOD
2300
begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1
2301
begemotSnmpdLocalPortType."/var/run/snmpd.sock" = 4
2302

    
2303
# These are bsnmp macros not php vars.
2304
sysContact      = $(contact)
2305
sysLocation     = $(location)
2306
sysObjectId     = 1.3.6.1.4.1.12325.1.1.2.1.$(system)
2307

    
2308
snmpEnableAuthenTraps = 2
2309

    
2310
EOD;
2311

    
2312
		if (is_array($config['snmpd']['modules'])) {
2313
			if (isset($config['snmpd']['modules']['mibii'])) {
2314
			$snmpdconf .= <<<EOD
2315
begemotSnmpdModulePath."mibII"  = "/usr/lib/snmp_mibII.so"
2316

    
2317
EOD;
2318
			}
2319

    
2320
			if (isset($config['snmpd']['modules']['netgraph'])) {
2321
				$snmpdconf .= <<<EOD
2322
begemotSnmpdModulePath."netgraph" = "/usr/lib/snmp_netgraph.so"
2323
%netgraph
2324
begemotNgControlNodeName = "snmpd"
2325

    
2326
EOD;
2327
			}
2328

    
2329
			if (isset($config['snmpd']['modules']['pf'])) {
2330
				$snmpdconf .= <<<EOD
2331
begemotSnmpdModulePath."pf"     = "/usr/lib/snmp_pf.so"
2332

    
2333
EOD;
2334
			}
2335

    
2336
			if (isset($config['snmpd']['modules']['hostres'])) {
2337
				$snmpdconf .= <<<EOD
2338
begemotSnmpdModulePath."hostres"     = "/usr/lib/snmp_hostres.so"
2339

    
2340
EOD;
2341
			}
2342

    
2343
			if (isset($config['snmpd']['modules']['bridge'])) {
2344
				$snmpdconf .= <<<EOD
2345
begemotSnmpdModulePath."bridge"     = "/usr/lib/snmp_bridge.so"
2346
# config must end with blank line
2347

    
2348
EOD;
2349
			}
2350
			if (isset($config['snmpd']['modules']['ucd'])) {
2351
				$snmpdconf .= <<<EOD
2352
begemotSnmpdModulePath."ucd"     = "/usr/local/lib/snmp_ucd.so"
2353

    
2354
EOD;
2355
			}
2356
			if (isset($config['snmpd']['modules']['regex'])) {
2357
				$snmpdconf .= <<<EOD
2358
begemotSnmpdModulePath."regex"     = "/usr/local/lib/snmp_regex.so"
2359

    
2360
EOD;
2361
			}
2362
		}
2363

    
2364
		fwrite($fd, $snmpdconf);
2365
		fclose($fd);
2366
		unset($snmpdconf);
2367

    
2368
		/* run bsnmpd */
2369
		mwexec("/usr/sbin/bsnmpd -c {$g['varetc_path']}/snmpd.conf" .
2370
			" -p {$g['varrun_path']}/snmpd.pid");
2371

    
2372
		if (platform_booting()) {
2373
			echo gettext("done.") . "\n";
2374
		}
2375
	}
2376

    
2377
	return 0;
2378
}
2379

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

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

    
2401
			/* determine interface name */
2402
			$if = get_real_interface($dnsupdate['interface']);
2403

    
2404
			if (isset($dnsupdate['usepublicip'])) {
2405
				$wanip = dyndnsCheckIP($dnsupdate['interface']);
2406
			} else {
2407
				$wanip = get_interface_ip($dnsupdate['interface']);
2408
			}
2409

    
2410
			$wanipv6 = get_interface_ipv6($dnsupdate['interface']);
2411
			$cacheFile = "{$g['conf_path']}/dyndns_{$dnsupdate['interface']}_rfc2136_" . escapeshellarg($dnsupdate['host']) . "_{$dnsupdate['server']}.cache";
2412
			$currentTime = time();
2413

    
2414
			if ($wanip || $wanipv6) {
2415
				$keyname = $dnsupdate['keyname'];
2416
				/* trailing dot */
2417
				if (substr($keyname, -1) != ".") {
2418
					$keyname .= ".";
2419
				}
2420

    
2421
				$hostname = $dnsupdate['host'];
2422
				/* trailing dot */
2423
				if (substr($hostname, -1) != ".") {
2424
					$hostname .= ".";
2425
				}
2426

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

    
2436
EOD;
2437
				fwrite($fd, $privkey);
2438
				fclose($fd);
2439

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

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

    
2456
				/* generate update instructions */
2457
				$upinst = "";
2458
				if (!empty($dnsupdate['server'])) {
2459
					$upinst .= "server {$dnsupdate['server']}\n";
2460
				}
2461

    
2462
				if (file_exists($cacheFile)) {
2463
					list($cachedipv4, $cacheTimev4) = explode("|", file_get_contents($cacheFile));
2464
				}
2465
				if (file_exists("{$cacheFile}.ipv6")) {
2466
					list($cachedipv6, $cacheTimev6) = explode("|", file_get_contents("{$cacheFile}.ipv6"));
2467
				}
2468

    
2469
				// 25 Days
2470
				$maxCacheAgeSecs = 25 * 24 * 60 * 60;
2471
				$need_update = false;
2472

    
2473
				conf_mount_rw();
2474
				/* Update IPv4 if we have it. */
2475
				if (is_ipaddrv4($wanip) && $dnsupdate['recordtype'] != "AAAA") {
2476
					if (($wanip != $cachedipv4) || (($currentTime - $cacheTimev4) > $maxCacheAgeSecs) || $forced) {
2477
						$upinst .= "update delete {$dnsupdate['host']}. A\n";
2478
						$upinst .= "update add {$dnsupdate['host']}. {$dnsupdate['ttl']} A {$wanip}\n";
2479
						$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";
2480
						@file_put_contents($cacheFile, "{$wanip}|{$currentTime}");
2481
						log_error("phpDynDNS: updating cache file {$cacheFile}: {$wanip}");
2482
						$need_update = true;
2483
					} else {
2484
						log_error("phpDynDNS: Not updating {$dnsupdate['host']} A record because the IP address has not changed.");
2485
					}
2486
				} else {
2487
					@unlink($cacheFile);
2488
				}
2489

    
2490
				/* Update IPv6 if we have it. */
2491
				if (is_ipaddrv6($wanipv6) && $dnsupdate['recordtype'] != "A") {
2492
					if (($wanipv6 != $cachedipv6) || (($currentTime - $cacheTimev6) > $maxCacheAgeSecs) || $forced) {
2493
						$upinst .= "update delete {$dnsupdate['host']}. AAAA\n";
2494
						$upinst .= "update add {$dnsupdate['host']}. {$dnsupdate['ttl']} AAAA {$wanipv6}\n";
2495
						$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";
2496
						@file_put_contents("{$cacheFile}.ipv6", "{$wanipv6}|{$currentTime}");
2497
						log_error("phpDynDNS: updating cache file {$cacheFile}.ipv6: {$wanipv6}");
2498
						$need_update = true;
2499
					} else {
2500
						log_error("phpDynDNS: Not updating {$dnsupdate['host']} AAAA record because the IPv6 address has not changed.");
2501
					}
2502
				} else {
2503
					@unlink("{$cacheFile}.ipv6");
2504
				}
2505
				conf_mount_ro();
2506

    
2507
				$upinst .= "\n";	/* mind that trailing newline! */
2508

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

    
2528
	return 0;
2529
}
2530

    
2531
/* configure cron service */
2532
function configure_cron() {
2533
	global $g, $config;
2534

    
2535
	conf_mount_rw();
2536
	/* preserve existing crontab entries */
2537
	$crontab_contents = file("/etc/crontab", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2538

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

    
2548

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

    
2555
		if (isset($config['system']['proxyurl']) && !empty($config['system']['proxyurl'])) {
2556
			$http_proxy = $config['system']['proxyurl'];
2557
			if (isset($config['system']['proxyport']) && !empty($config['system']['proxyport'])) {
2558
				$http_proxy .= ':' . $config['system']['proxyport'];
2559
			}
2560
			$crontab_contents .= "HTTP_PROXY={$http_proxy}";
2561
		}
2562

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

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

    
2579
	/* please maintain the newline at the end of file */
2580
	file_put_contents("/etc/crontab", $crontab_contents);
2581
	unset($crontab_contents);
2582

    
2583
	/* make sure that cron is running and start it if it got killed somehow */
2584
	if (!is_process_running("cron")) {
2585
		exec("cd /tmp && /usr/sbin/cron -s 2>/dev/null");
2586
	} else {
2587
	/* do a HUP kill to force sync changes */
2588
		sigkillbypid("{$g['varrun_path']}/cron.pid", "HUP");
2589
	}
2590

    
2591
	conf_mount_ro();
2592
}
2593

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

    
2618
function upnp_start() {
2619
	global $config;
2620

    
2621
	if (!isset($config['installedpackages']['miniupnpd']['config'])) {
2622
		return;
2623
	}
2624

    
2625
	if ($config['installedpackages']['miniupnpd']['config'][0]['enable']) {
2626
		echo gettext("Starting UPnP service... ");
2627
		require_once('/usr/local/pkg/miniupnpd.inc');
2628
		sync_package_miniupnpd();
2629
		echo "done.\n";
2630
	}
2631
}
2632

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

    
2636
	$is_installed = false;
2637
	$cron_changed = true;
2638

    
2639
	if (!is_array($config['cron'])) {
2640
		$config['cron'] = array();
2641
	}
2642
	if (!is_array($config['cron']['item'])) {
2643
		$config['cron']['item'] = array();
2644
	}
2645

    
2646
	$x = 0;
2647
	foreach ($config['cron']['item'] as $item) {
2648
		if (strstr($item['command'], $command)) {
2649
			$is_installed = true;
2650
			break;
2651
		}
2652
		$x++;
2653
	}
2654

    
2655
	if ($active) {
2656
		$cron_item = array();
2657
		$cron_item['minute'] = $minute;
2658
		$cron_item['hour'] = $hour;
2659
		$cron_item['mday'] = $monthday;
2660
		$cron_item['month'] = $month;
2661
		$cron_item['wday'] = $weekday;
2662
		$cron_item['who'] = $who;
2663
		$cron_item['command'] = $command;
2664
		if (!$is_installed) {
2665
			$config['cron']['item'][] = $cron_item;
2666
			write_config(sprintf(gettext("Installed cron job for %s"), $command));
2667
		} else {
2668
			if ($config['cron']['item'][$x] == $cron_item) {
2669
				$cron_changed = false;
2670
				log_error(sprintf(gettext("Checked cron job for %s, no change needed"), $command));
2671
			} else {
2672
				$config['cron']['item'][$x] = $cron_item;
2673
				write_config(sprintf(gettext("Updated cron job for %s"), $command));
2674
			}
2675
		}
2676
	} else {
2677
		if ($is_installed == true) {
2678
			unset($config['cron']['item'][$x]);
2679
			write_config(sprintf(gettext("Removed cron job for %s"), $command));
2680
		}
2681
	}
2682

    
2683
	if ($cron_changed) {
2684
		configure_cron();
2685
	}
2686
}
2687

    
2688
?>
(49-49/65)