Project

General

Profile

Download (82.8 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	services.inc
4

    
5
	part of pfSense (https://www.pfsense.org)
6
	Copyright (c) 2004-2016 Electric Sheep Fencing, LLC.
7
	All rights reserved.
8

    
9
	originally part of m0n0wall (http://m0n0.ch/wall)
10
	Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
11
	All rights reserved.
12

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

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

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

    
24
	3. All advertising materials mentioning features or use of this software
25
	   must display the following acknowledgment:
26
	   "This product includes software developed by the pfSense Project
27
	   for use in the pfSense® software distribution. (http://www.pfsense.org/).
28

    
29
	4. The names "pfSense" and "pfSense Project" must not be used to
30
	   endorse or promote products derived from this software without
31
	   prior written permission. For written permission, please contact
32
	   coreteam@pfsense.org.
33

    
34
	5. Products derived from this software may not be called "pfSense"
35
	   nor may "pfSense" appear in their names without prior written
36
	   permission of the Electric Sheep Fencing, LLC.
37

    
38
	6. Redistributions of any form whatsoever must retain the following
39
	   acknowledgment:
40

    
41
	"This product includes software developed by the pfSense Project
42
	for use in the pfSense software distribution (http://www.pfsense.org/).
43

    
44
	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
45
	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
47
	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
48
	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
49
	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
50
	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
51
	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52
	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
53
	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
54
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
55
	OF THE POSSIBILITY OF SUCH DAMAGE.
56
*/
57

    
58
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 spdns spdns-v6 zoneedit');
59
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,SPDNS,SPDNS (v6),ZoneEdit');
60

    
61
/* implement ipv6 route advertising daemon */
62
function services_radvd_configure($blacklist = array()) {
63
	global $config, $g;
64

    
65
	if (isset($config['system']['developerspew'])) {
66
		$mt = microtime();
67
		echo "services_radvd_configure() being called $mt\n";
68
	}
69

    
70
	if (!is_array($config['dhcpdv6'])) {
71
		$config['dhcpdv6'] = array();
72
	}
73

    
74
	$Iflist = get_configured_interface_list();
75
	$Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces());
76

    
77
	$radvdconf = "# Automatically Generated, do not edit\n";
78

    
79
	/* Process all links which need the router advertise daemon */
80
	$radvdifs = array();
81

    
82
	/* handle manually configured DHCP6 server settings first */
83
	foreach ($config['dhcpdv6'] as $dhcpv6if => $dhcpv6ifconf) {
84
		if (!is_array($config['interfaces'][$dhcpv6if])) {
85
			continue;
86
		}
87
		if (!isset($config['interfaces'][$dhcpv6if]['enable'])) {
88
			continue;
89
		}
90

    
91
		/* Do not put in the config an interface which is down */
92
		if (isset($blacklist[$dhcpv6if])) {
93
			continue;
94
		}
95
		if (!isset($dhcpv6ifconf['ramode'])) {
96
			$dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode'];
97
		}
98

    
99
		/* are router advertisements enabled? */
100
		if ($dhcpv6ifconf['ramode'] == "disabled") {
101
			continue;
102
		}
103

    
104
		if (!isset($dhcpv6ifconf['rapriority'])) {
105
			$dhcpv6ifconf['rapriority'] = "medium";
106
		}
107

    
108
		$realif = get_real_interface($dhcpv6if, "inet6");
109

    
110
		if (isset($radvdifs[$realif])) {
111
			continue;
112
		}
113

    
114
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
115
		if (!is_ipaddrv6($ifcfgipv6)) {
116
			continue;
117
		}
118

    
119
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
120
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
121
		$radvdifs[$realif] = $realif;
122

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

    
186
		if (is_numericint($dhcpv6ifconf['ravalidlifetime'])) {
187
		  $radvdconf .= "\t\tAdvValidLifetime {$dhcpv6ifconf['ravalidlifetime']};\n";
188
		} else {
189
		  $radvdconf .= "\t\tAdvValidLifetime 86400;\n";
190
		}
191

    
192
		if (is_numericint($dhcpv6ifconf['rapreferredlifetime'])) {
193
		  $radvdconf .= "\t\tAdvPreferredLifetime {$dhcpv6ifconf['rapreferredlifetime']};\n";
194
		} else {
195
		  $radvdconf .= "\t\tAdvPreferredLifetime 14400;\n";
196
		}
197

    
198
		$radvdconf .= "\t};\n";
199

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

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

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

    
291
		$realif = get_real_interface($if, "inet6");
292

    
293
		/* prevent duplicate entries, manual overrides */
294
		if (isset($radvdifs[$realif])) {
295
			continue;
296
		}
297

    
298
		$ifcfgipv6 = get_interface_ipv6($if);
299
		if (!is_ipaddrv6($ifcfgipv6)) {
300
			$subnetv6 = "::";
301
			$ifcfgsnv6 = "64";
302
		} else {
303
			$ifcfgsnv6 = get_interface_subnetv6($if);
304
			$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
305
		}
306
		$radvdifs[$realif] = $realif;
307

    
308
		$autotype = $config['interfaces'][$trackif]['ipaddrv6'];
309

    
310
		if ($g['debug']) {
311
			log_error("configuring RA on {$if} for type {$autotype} radvd subnet {$subnetv6}/{$ifcfgsnv6}");
312
		}
313

    
314
		$radvdconf .= "# Generated config for {$autotype} delegation from {$trackif} on {$if}\n";
315
		$radvdconf .= "interface {$realif} {\n";
316
		$radvdconf .= "\tAdvSendAdvert on;\n";
317
		$radvdconf .= "\tMinRtrAdvInterval 3;\n";
318
		$radvdconf .= "\tMaxRtrAdvInterval 10;\n";
319
		$mtu = get_interface_mtu($realif);
320
		if (is_numeric($mtu)) {
321
			$radvdconf .= "\tAdvLinkMTU {$mtu};\n";
322
		} else {
323
			$radvdconf .= "\tAdvLinkMTU 1280;\n";
324
		}
325
		$radvdconf .= "\tAdvOtherConfigFlag on;\n";
326
		$radvdconf .= "\t\tprefix {$subnetv6}/{$ifcfgsnv6} {\n";
327
		$radvdconf .= "\t\tAdvOnLink on;\n";
328
		$radvdconf .= "\t\tAdvAutonomous on;\n";
329
		$radvdconf .= "\t\tAdvRouterAddr on;\n";
330
		$radvdconf .= "\t};\n";
331

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

    
355
	/* write radvd.conf */
356
	if (!@file_put_contents("{$g['varetc_path']}/radvd.conf", $radvdconf)) {
357
		log_error("Error: cannot open radvd.conf in services_radvd_configure().\n");
358
		if (platform_booting()) {
359
			printf("Error: cannot open radvd.conf in services_radvd_configure().\n");
360
		}
361
	}
362
	unset($radvdconf);
363

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

    
382
function services_dhcpd_configure($family = "all", $blacklist = array()) {
383
	global $config, $g;
384

    
385
	/* configure DHCPD chroot once */
386
	$fd = fopen("{$g['tmp_path']}/dhcpd.sh", "w");
387
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}\n");
388
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/dev\n");
389
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/etc\n");
390
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/usr/local/sbin\n");
391
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/var/db\n");
392
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/var/run\n");
393
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/usr\n");
394
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/lib\n");
395
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/run\n");
396
	fwrite($fd, "/usr/sbin/chown -R dhcpd:_dhcp {$g['dhcpd_chroot_path']}/*\n");
397
	fwrite($fd, "/bin/cp -n /lib/libc.so.* {$g['dhcpd_chroot_path']}/lib/\n");
398
	fwrite($fd, "/bin/cp -n /usr/local/sbin/dhcpd {$g['dhcpd_chroot_path']}/usr/local/sbin/\n");
399
	fwrite($fd, "/bin/chmod a+rx {$g['dhcpd_chroot_path']}/usr/local/sbin/dhcpd\n");
400

    
401
	$status = `/sbin/mount | /usr/bin/grep -v grep | /usr/bin/grep "{$g['dhcpd_chroot_path']}/dev"`;
402
	if (!trim($status)) {
403
		fwrite($fd, "/sbin/mount -t devfs devfs {$g['dhcpd_chroot_path']}/dev\n");
404
	}
405
	fclose($fd);
406
	mwexec("/bin/sh {$g['tmp_path']}/dhcpd.sh");
407

    
408
	if ($family == "all" || $family == "inet") {
409
		services_dhcpdv4_configure();
410
	}
411
	if ($family == "all" || $family == "inet6") {
412
		services_dhcpdv6_configure($blacklist);
413
		services_radvd_configure($blacklist);
414
	}
415
}
416

    
417
function services_dhcpdv4_configure() {
418
	global $config, $g;
419
	$need_ddns_updates = false;
420
	$ddns_zones = array();
421

    
422
	if ($g['services_dhcp_server_enable'] == false) {
423
		return;
424
	}
425

    
426
	if (isset($config['system']['developerspew'])) {
427
		$mt = microtime();
428
		echo "services_dhcpdv4_configure($if) being called $mt\n";
429
	}
430

    
431
	/* kill any running dhcpd */
432
	if (isvalidpid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid")) {
433
		killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid");
434
	}
435

    
436
	/* DHCP enabled on any interfaces? */
437
	if (!is_dhcp_server_enabled()) {
438
		return 0;
439
	}
440

    
441
	/* if OLSRD is enabled, allow WAN to house DHCP. */
442
	if (!function_exists('is_package_installed')) {
443
		require_once('pkg-utils.inc');
444
	}
445
	if (is_package_installed('olsrd') && isset($config['installedpackages']['olsrd'])) {
446
		foreach ($config['installedpackages']['olsrd']['config'] as $olsrd) {
447
			if (isset($olsrd['enable']) && $olsrd['enable'] == "on") {
448
				$is_olsr_enabled = true;
449
				break;
450
			}
451
		}
452
	}
453

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

    
471
	$syscfg = $config['system'];
472
	if (!is_array($config['dhcpd'])) {
473
		$config['dhcpd'] = array();
474
	}
475
	$dhcpdcfg = $config['dhcpd'];
476
	$Iflist = get_configured_interface_list();
477

    
478
	/* Only consider DNS servers with IPv4 addresses for the IPv4 DHCP server. */
479
	$dns_arrv4 = array();
480
	if (is_array($syscfg['dnsserver'])) {
481
		foreach ($syscfg['dnsserver'] as $dnsserver) {
482
			if (is_ipaddrv4($dnsserver)) {
483
				$dns_arrv4[] = $dnsserver;
484
			}
485
		}
486
	}
487

    
488
	if (platform_booting()) {
489
		echo gettext("Starting DHCP service...");
490
	} else {
491
		sleep(1);
492
	}
493

    
494
	$custoptions = "";
495
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
496
		if (is_array($dhcpifconf['numberoptions']) && is_array($dhcpifconf['numberoptions']['item'])) {
497
			foreach ($dhcpifconf['numberoptions']['item'] as $itemidx => $item) {
498
				if (!empty($item['type'])) {
499
					$itemtype = $item['type'];
500
				} else {
501
					$itemtype = "text";
502
				}
503
				$custoptions .= "option custom-{$dhcpif}-{$itemidx} code {$item['number']} = {$itemtype};\n";
504
			}
505
		}
506
	}
507

    
508
	$dhcpdconf = <<<EOD
509

    
510
option domain-name "{$syscfg['domain']}";
511
option ldap-server code 95 = text;
512
option domain-search-list code 119 = text;
513
option arch code 93 = unsigned integer 16; # RFC4578
514
{$custoptions}
515
default-lease-time 7200;
516
max-lease-time 86400;
517
log-facility local7;
518
one-lease-per-client true;
519
deny duplicates;
520
ping-check true;
521
update-conflict-detection false;
522

    
523
EOD;
524

    
525
	if (!isset($dhcpifconf['disableauthoritative'])) {
526
		$dhcpdconf .= "authoritative;\n";
527
	}
528

    
529
	if (isset($dhcpifconf['alwaysbroadcast'])) {
530
		$dhcpdconf .= "always-broadcast on\n";
531
	}
532

    
533
	$dhcpdifs = array();
534
	$enable_add_routers = false;
535
	$gateways_arr = return_gateways_array();
536
	/* only add a routers line if the system has any IPv4 gateway at all */
537
	/* a static route has a gateway, manually overriding this field always works */
538
	foreach ($gateways_arr as $gwitem) {
539
		if ($gwitem['ipprotocol'] == "inet") {
540
			$enable_add_routers = true;
541
			break;
542
		}
543
	}
544

    
545
	/*    loop through and determine if we need to setup
546
	 *    failover peer "bleh" entries
547
	 */
548
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
549

    
550
		if (!isset($config['interfaces'][$dhcpif]['enable'])) {
551
			continue;
552
		}
553

    
554
		interfaces_staticarp_configure($dhcpif);
555

    
556
		if (!isset($dhcpifconf['enable'])) {
557
			continue;
558
		}
559

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

    
595
			if (is_ipaddrv4($intip)) {
596
				$dhcpdconf .= <<<EOPP
597
failover peer "dhcp_{$dhcpif}" {
598
  {$type};
599
  address {$intip};
600
  port {$my_port};
601
  peer address {$dhcpifconf['failover_peerip']};
602
  peer port {$peer_port};
603
  max-response-delay 10;
604
  max-unacked-updates 10;
605
  {$dhcpdconf_pri}
606
  load balance max seconds 3;
607
}
608
\n
609
EOPP;
610
			}
611
		}
612
	}
613

    
614
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
615

    
616
		$newzone = array();
617
		$ifcfg = $config['interfaces'][$dhcpif];
618

    
619
		if (!isset($dhcpifconf['enable']) || !isset($Iflist[$dhcpif])) {
620
			continue;
621
		}
622
		$ifcfgip = get_interface_ip($dhcpif);
623
		$ifcfgsn = get_interface_subnet($dhcpif);
624
		$subnet = gen_subnet($ifcfgip, $ifcfgsn);
625
		$subnetmask = gen_subnet_mask($ifcfgsn);
626

    
627
		if (!is_ipaddr($subnet)) {
628
			continue;
629
		}
630

    
631
		if ($is_olsr_enabled == true) {
632
			if ($dhcpifconf['netmask']) {
633
				$subnetmask = gen_subnet_mask($dhcpifconf['netmask']);
634
			}
635
		}
636

    
637
		$all_pools = array();
638
		$all_pools[] = $dhcpifconf;
639
		if (is_array($dhcpifconf['pool'])) {
640
			$all_pools = array_merge($all_pools, $dhcpifconf['pool']);
641
		}
642

    
643
		$dnscfg = "";
644

    
645
		if ($dhcpifconf['domain']) {
646
			$dnscfg .= "	option domain-name \"{$dhcpifconf['domain']}\";\n";
647
		}
648

    
649
		if ($dhcpifconf['domainsearchlist'] <> "") {
650
			$dnscfg .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpifconf['domainsearchlist'])) . "\";\n";
651
		}
652

    
653
		if (isset($dhcpifconf['ddnsupdate'])) {
654
			$need_ddns_updates = true;
655
			$newzone = array();
656
			if ($dhcpifconf['ddnsdomain'] <> "") {
657
				$newzone['domain-name'] = $dhcpifconf['ddnsdomain'];
658
				$dnscfg .= "	ddns-domainname \"{$dhcpifconf['ddnsdomain']}\";\n";
659
			} else {
660
				$newzone['domain-name'] = $config['system']['domain'];
661
			}
662

    
663
			$revsubnet = array_reverse(explode('.',$subnet));
664

    
665
			/* Take care of full classes first */
666
			switch ($ifcfgsn) {
667
				case 8:
668
					$start_octet = 3;
669
					break;
670
				case 16:
671
					$start_octet = 2;
672
					break;
673
				case 24:
674
					$start_octet = 1;
675
					break;
676
				default:
677
					$start_octet = 0;
678
					/* Add subnet bitmask to first octet */
679
					$revsubnet[0] .= '-' . $ifcfgsn;
680
					break;
681

    
682
			}
683

    
684
			$ptr_domain = '';
685
			for ($octet = 0; $octet <= 3; $octet++) {
686
				if ($octet < $start_octet) {
687
					continue;
688
				}
689
				$ptr_domain .= (empty($ptr_domain) ? '' : '.');
690
				$ptr_domain .= $revsubnet[$octet];
691
			}
692
			$ptr_domain .= ".in-addr.arpa";
693
			$newzone['ptr-domain'] = $ptr_domain;
694
			unset($ptr_domain, $revsubnet, $start_octet);
695
		}
696

    
697
		if (is_array($dhcpifconf['dnsserver']) && ($dhcpifconf['dnsserver'][0])) {
698
			$dnscfg .= "	option domain-name-servers " . join(",", $dhcpifconf['dnsserver']) . ";";
699
			if ($newzone['domain-name']) {
700
				$newzone['dns-servers'] = $dhcpifconf['dnsserver'];
701
			}
702
		} else if (isset($config['dnsmasq']['enable'])) {
703
			$dnscfg .= "	option domain-name-servers {$ifcfgip};";
704
			if ($newzone['domain-name'] && is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
705
				$newzone['dns-servers'] = $syscfg['dnsserver'];
706
			}
707
		} else if (isset($config['unbound']['enable'])) {
708
			$dnscfg .= "	option domain-name-servers {$ifcfgip};";
709
		} else if (!empty($dns_arrv4)) {
710
			$dnscfg .= "	option domain-name-servers " . join(",", $dns_arrv4) . ";";
711
			if ($newzone['domain-name']) {
712
				$newzone['dns-servers'] = $dns_arrv4;
713
			}
714
		}
715

    
716
		/* Create classes - These all contain comma separated lists. Join them into one
717
		   big comma separated string then split them all up. */
718
		$all_mac_strings = array();
719
		if (is_array($dhcpifconf['pool'])) {
720
			foreach ($all_pools as $poolconf) {
721
				$all_mac_strings[] = $poolconf['mac_allow'];
722
				$all_mac_strings[] = $poolconf['mac_deny'];
723
			}
724
		}
725
		$all_mac_strings[] = $dhcpifconf['mac_allow'];
726
		$all_mac_strings[] = $dhcpifconf['mac_deny'];
727
		if (!empty($all_mac_strings)) {
728
			$all_mac_list = array_unique(explode(',', implode(',', $all_mac_strings)));
729
			foreach ($all_mac_list as $mac) {
730
				if (empty($mac)) {
731
					continue;
732
				}
733
				$dhcpdconf .= 'class "' . str_replace(':', '', $mac) . '" {' . "\n";
734
				// Skip the first octet of the MAC address - for media type, typically Ethernet ("01") and match the rest.
735
				$dhcpdconf .= '	match if substring (hardware, 1, ' . (substr_count($mac, ':') + 1) . ') = ' . $mac . ';' . "\n";
736
				$dhcpdconf .= '}' . "\n";
737
			}
738
		}
739

    
740
		$dhcpdconf .= "subnet {$subnet} netmask {$subnetmask} {\n";
741

    
742
		// Setup pool options
743
		foreach ($all_pools as $poolconf) {
744
			if (!(ip_in_subnet($poolconf['range']['from'], "{$subnet}/{$ifcfgsn}") && ip_in_subnet($poolconf['range']['to'], "{$subnet}/{$ifcfgsn}"))) {
745
				// If the user has changed the subnet from the interfaces page and applied,
746
				// but has not updated the DHCP range, then the range to/from of the pool can be outside the subnet.
747
				// This can also happen when implementing the batch of changes when the setup wizard reloads the new settings.
748
				$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);
749
				$do_file_notice = true;
750
				$conf_ipv4_address = $ifcfg['ipaddr'];
751
				$conf_ipv4_subnetmask = $ifcfg['subnet'];
752
				if (is_ipaddrv4($conf_ipv4_address) && is_subnet("{$conf_ipv4_address}/{$conf_ipv4_subnetmask}")) {
753
					$conf_subnet_base = gen_subnet($conf_ipv4_address, $conf_ipv4_subnetmask);
754
					if (ip_in_subnet($poolconf['range']['from'], "{$conf_subnet_base}/{$conf_ipv4_subnetmask}") &&
755
					    ip_in_subnet($poolconf['range']['to'], "{$conf_subnet_base}/{$conf_ipv4_subnetmask}")) {
756
						// Even though the running interface subnet does not match the pool range,
757
						// the interface subnet in the config file contains the pool range.
758
						// We are somewhere part-way through a settings reload, e.g. after running the setup wizard.
759
						// services_dhcpdv4_configure will be called again later when the new interface settings from
760
						// the config are applied and at that time everything will match up.
761
						// Ignore this pool on this interface for now and just log the error to the system log.
762
						log_error($error_msg);
763
						$do_file_notice = false;
764
					}
765
				}
766
				if ($do_file_notice) {
767
					file_notice("DHCP", $error_msg);
768
				}
769
				continue;
770
			}
771
			$dhcpdconf .= "	pool {\n";
772
			/* is failover dns setup? */
773
			if (is_array($poolconf['dnsserver']) && $poolconf['dnsserver'][0] <> "") {
774
				$dhcpdconf .= "		option domain-name-servers {$poolconf['dnsserver'][0]}";
775
				if ($poolconf['dnsserver'][1] <> "") {
776
					$dhcpdconf .= ",{$poolconf['dnsserver'][1]}";
777
				}
778
				if ($poolconf['dnsserver'][2] <> "") {
779
					$dhcpdconf .= ",{$poolconf['dnsserver'][2]}";
780
				}
781
				if ($poolconf['dnsserver'][3] <> "") {
782
					$dhcpdconf .= ",{$poolconf['dnsserver'][3]}";
783
				}
784
				$dhcpdconf .= ";\n";
785
			}
786

    
787
			/* allow/deny MACs */
788
			$mac_allow_list = array_unique(explode(',', $poolconf['mac_allow']));
789
			foreach ($mac_allow_list as $mac) {
790
				if (empty($mac)) {
791
					continue;
792
				}
793
				$dhcpdconf .= "		allow members of \"" . str_replace(':', '', $mac) . "\";\n";
794
			}
795
			$deny_action = "deny";
796
			if (isset($poolconf['nonak'])) {
797
				$deny_action = "ignore";
798
			}
799
			$mac_deny_list = array_unique(explode(',', $poolconf['mac_deny']));
800
			foreach ($mac_deny_list as $mac) {
801
				if (empty($mac)) {
802
					continue;
803
				}
804
				$dhcpdconf .= "		$deny_action members of \"" . str_replace(':', '', $mac) . "\";\n";
805
			}
806

    
807
			if ($poolconf['failover_peerip'] <> "") {
808
				$dhcpdconf .= "		$deny_action dynamic bootp clients;\n";
809
			}
810

    
811
			if (isset($poolconf['denyunknown'])) {
812
				$dhcpdconf .= "		$deny_action unknown-clients;\n";
813
			}
814

    
815
			if ($poolconf['gateway'] && $poolconf['gateway'] != "none" && ($poolconf['gateway'] != $dhcpifconf['gateway'])) {
816
				$dhcpdconf .= "		option routers {$poolconf['gateway']};\n";
817
			}
818

    
819
			if ($dhcpifconf['failover_peerip'] <> "") {
820
				$dhcpdconf .= "		failover peer \"dhcp_{$dhcpif}\";\n";
821
			}
822

    
823
			$pdnscfg = "";
824

    
825
			if ($poolconf['domain'] && ($poolconf['domain'] != $dhcpifconf['domain'])) {
826
				$pdnscfg .= "		option domain-name \"{$poolconf['domain']}\";\n";
827
			}
828

    
829
			if (!empty($poolconf['domainsearchlist']) && ($poolconf['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
830
				$pdnscfg .= "		option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $poolconf['domainsearchlist'])) . "\";\n";
831
			}
832

    
833
			if (isset($poolconf['ddnsupdate'])) {
834
				if (($poolconf['ddnsdomain'] <> "") && ($poolconf['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
835
					$pdnscfg .= "		ddns-domainname \"{$poolconf['ddnsdomain']}\";\n";
836
				}
837
				$pdnscfg .= "		ddns-update-style interim;\n";
838
			}
839

    
840
			if (is_array($poolconf['dnsserver']) && ($poolconf['dnsserver'][0]) && ($poolconf['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
841
				$pdnscfg .= "		option domain-name-servers " . join(",", $poolconf['dnsserver']) . ";\n";
842
			}
843
			$dhcpdconf .= "{$pdnscfg}";
844

    
845
			// default-lease-time
846
			if ($poolconf['defaultleasetime'] && ($poolconf['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) {
847
				$dhcpdconf .= "		default-lease-time {$poolconf['defaultleasetime']};\n";
848
			}
849

    
850
			// max-lease-time
851
			if ($poolconf['maxleasetime'] && ($poolconf['maxleasetime'] != $dhcpifconf['maxleasetime'])) {
852
				$dhcpdconf .= "		max-lease-time {$poolconf['maxleasetime']};\n";
853
			}
854

    
855
			// netbios-name*
856
			if (is_array($poolconf['winsserver']) && $poolconf['winsserver'][0] && ($poolconf['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
857
				$dhcpdconf .= "		option netbios-name-servers " . join(",", $poolconf['winsserver']) . ";\n";
858
				$dhcpdconf .= "		option netbios-node-type 8;\n";
859
			}
860

    
861
			// ntp-servers
862
			if (is_array($poolconf['ntpserver']) && $poolconf['ntpserver'][0] && ($poolconf['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
863
				$dhcpdconf .= "		option ntp-servers " . join(",", $poolconf['ntpserver']) . ";\n";
864
			}
865

    
866
			// tftp-server-name
867
			if (!empty($poolconf['tftp']) && ($poolconf['tftp'] != $dhcpifconf['tftp'])) {
868
				$dhcpdconf .= "		option tftp-server-name \"{$poolconf['tftp']}\";\n";
869
			}
870

    
871
			// ldap-server
872
			if (!empty($poolconf['ldap']) && ($poolconf['ldap'] != $dhcpifconf['ldap'])) {
873
				$dhcpdconf .= "		option ldap-server \"{$poolconf['ldap']}\";\n";
874
			}
875

    
876
			// net boot information
877
			if (isset($poolconf['netboot'])) {
878
				if (!empty($poolconf['nextserver']) && ($poolconf['nextserver'] != $dhcpifconf['nextserver'])) {
879
					$dhcpdconf .= "		next-server {$poolconf['nextserver']};\n";
880
				}
881
				if (!empty($poolconf['filename']) && ($poolconf['filename'] != $dhcpifconf['filename'])) {
882
					$dhcpdconf .= "		filename \"{$poolconf['filename']}\";\n";
883
				}
884
				if (!empty($poolconf['rootpath']) && ($poolconf['rootpath'] != $dhcpifconf['rootpath'])) {
885
					$dhcpdconf .= "		option root-path \"{$poolconf['rootpath']}\";\n";
886
				}
887
			}
888
			$dhcpdconf .= "		range {$poolconf['range']['from']} {$poolconf['range']['to']};\n";
889
			$dhcpdconf .= "	}\n\n";
890
		}
891
// End of settings inside pools
892

    
893
		if ($dhcpifconf['gateway'] && $dhcpifconf['gateway'] != "none") {
894
			$routers = $dhcpifconf['gateway'];
895
			$add_routers = true;
896
		} elseif ($dhcpifconf['gateway'] == "none") {
897
			$add_routers = false;
898
		} else {
899
			$add_routers = $enable_add_routers;
900
			$routers = $ifcfgip;
901
		}
902
		if ($add_routers) {
903
			$dhcpdconf .= "	option routers {$routers};\n";
904
		}
905

    
906
		$dhcpdconf .= <<<EOD
907
$dnscfg
908

    
909
EOD;
910
		// default-lease-time
911
		if ($dhcpifconf['defaultleasetime']) {
912
			$dhcpdconf .= "	default-lease-time {$dhcpifconf['defaultleasetime']};\n";
913
		}
914

    
915
		// max-lease-time
916
		if ($dhcpifconf['maxleasetime']) {
917
			$dhcpdconf .= "	max-lease-time {$dhcpifconf['maxleasetime']};\n";
918
		}
919

    
920
		// netbios-name*
921
		if (is_array($dhcpifconf['winsserver']) && $dhcpifconf['winsserver'][0]) {
922
			$dhcpdconf .= "	option netbios-name-servers " . join(",", $dhcpifconf['winsserver']) . ";\n";
923
			$dhcpdconf .= "	option netbios-node-type 8;\n";
924
		}
925

    
926
		// ntp-servers
927
		if (is_array($dhcpifconf['ntpserver']) && $dhcpifconf['ntpserver'][0]) {
928
			$dhcpdconf .= "	option ntp-servers " . join(",", $dhcpifconf['ntpserver']) . ";\n";
929
		}
930

    
931
		// tftp-server-name
932
		if ($dhcpifconf['tftp'] <> "") {
933
			$dhcpdconf .= "	option tftp-server-name \"{$dhcpifconf['tftp']}\";\n";
934
		}
935

    
936
		// Handle option, number rowhelper values
937
		$dhcpdconf .= "\n";
938
		if ($dhcpifconf['numberoptions']['item']) {
939
			foreach ($dhcpifconf['numberoptions']['item'] as $itemidx => $item) {
940
				$item_value = base64_decode($item['value']);
941
				if (empty($item['type']) || $item['type'] == "text") {
942
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} \"{$item_value}\";\n";
943
				} else {
944
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} {$item_value};\n";
945
				}
946
			}
947
		}
948

    
949
		// ldap-server
950
		if ($dhcpifconf['ldap'] <> "") {
951
			$dhcpdconf .= "	option ldap-server \"{$dhcpifconf['ldap']}\";\n";
952
		}
953

    
954
		// net boot information
955
		if (isset($dhcpifconf['netboot'])) {
956
			if ($dhcpifconf['nextserver'] <> "") {
957
				$dhcpdconf .= "	next-server {$dhcpifconf['nextserver']};\n";
958
			}
959
			if (!empty($dhcpifconf['filename']) && !empty($dhcpifconf['filename32']) && !empty($dhcpifconf['filename64'])) {
960
				$dhcpdconf .= "	if option arch = 00:06 {\n";
961
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename32']}\";\n";
962
				$dhcpdconf .= "	} else if option arch = 00:07 {\n";
963
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename64']}\";\n";
964
				$dhcpdconf .= "	} else if option arch = 00:09 {\n";
965
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename64']}\";\n";
966
				$dhcpdconf .= "	} else {\n";
967
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename']}\";\n";
968
				$dhcpdconf .= "	}\n\n";
969
			} elseif (!empty($dhcpifconf['filename'])) {
970
				$dhcpdconf .= "	filename \"{$dhcpifconf['filename']}\";\n";
971
			}
972
			if (!empty($dhcpifconf['rootpath'])) {
973
				$dhcpdconf .= "	option root-path \"{$dhcpifconf['rootpath']}\";\n";
974
			}
975
		}
976

    
977
		$dhcpdconf .= <<<EOD
978
}
979

    
980
EOD;
981

    
982
		/* add static mappings */
983
		if (is_array($dhcpifconf['staticmap'])) {
984

    
985
			$i = 0;
986
			foreach ($dhcpifconf['staticmap'] as $sm) {
987
				$dhcpdconf .= "host s_{$dhcpif}_{$i} {\n";
988

    
989
				if ($sm['mac']) {
990
					$dhcpdconf .= "        hardware ethernet {$sm['mac']};\n";
991
				}
992

    
993
				if ($sm['cid']) {
994
					$dhcpdconf .= "        option dhcp-client-identifier \"{$sm['cid']}\";\n";
995
				}
996

    
997
				if ($sm['ipaddr']) {
998
					$dhcpdconf .= "	fixed-address {$sm['ipaddr']};\n";
999
				}
1000

    
1001
				if ($sm['hostname']) {
1002
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
1003
					$dhhostname = str_replace(".", "_", $dhhostname);
1004
					$dhcpdconf .= "	option host-name \"{$dhhostname}\";\n";
1005
				}
1006
				if ($sm['filename']) {
1007
					$dhcpdconf .= "	filename \"{$sm['filename']}\";\n";
1008
				}
1009

    
1010
				if ($sm['rootpath']) {
1011
					$dhcpdconf .= "	option root-path \"{$sm['rootpath']}\";\n";
1012
				}
1013

    
1014
				if ($sm['gateway'] && ($sm['gateway'] != $dhcpifconf['gateway'])) {
1015
					$dhcpdconf .= "	option routers {$sm['gateway']};\n";
1016
				}
1017

    
1018
				$smdnscfg = "";
1019

    
1020
				if ($sm['domain'] && ($sm['domain'] != $dhcpifconf['domain'])) {
1021
					$smdnscfg .= "	option domain-name \"{$sm['domain']}\";\n";
1022
				}
1023

    
1024
				if (!empty($sm['domainsearchlist']) && ($sm['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
1025
					$smdnscfg .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $sm['domainsearchlist'])) . "\";\n";
1026
				}
1027

    
1028
				if (isset($sm['ddnsupdate'])) {
1029
					if (($sm['ddnsdomain'] <> "") && ($sm['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
1030
						$pdnscfg .= "		ddns-domainname \"{$sm['ddnsdomain']}\";\n";
1031
					}
1032
					$pdnscfg .= "		ddns-update-style interim;\n";
1033
				}
1034

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

    
1040
				// default-lease-time
1041
				if ($sm['defaultleasetime'] && ($sm['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) {
1042
					$dhcpdconf .= "	default-lease-time {$sm['defaultleasetime']};\n";
1043
				}
1044

    
1045
				// max-lease-time
1046
				if ($sm['maxleasetime'] && ($sm['maxleasetime'] != $dhcpifconf['maxleasetime'])) {
1047
					$dhcpdconf .= "	max-lease-time {$sm['maxleasetime']};\n";
1048
				}
1049

    
1050
				// netbios-name*
1051
				if (is_array($sm['winsserver']) && $sm['winsserver'][0] && ($sm['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
1052
					$dhcpdconf .= "	option netbios-name-servers " . join(",", $sm['winsserver']) . ";\n";
1053
					$dhcpdconf .= "	option netbios-node-type 8;\n";
1054
				}
1055

    
1056
				// ntp-servers
1057
				if (is_array($sm['ntpserver']) && $sm['ntpserver'][0] && ($sm['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
1058
					$dhcpdconf .= "	option ntp-servers " . join(",", $sm['ntpserver']) . ";\n";
1059
				}
1060

    
1061
				// tftp-server-name
1062
				if (!empty($sm['tftp']) && ($sm['tftp'] != $dhcpifconf['tftp'])) {
1063
					$dhcpdconf .= "	option tftp-server-name \"{$sm['tftp']}\";\n";
1064
				}
1065

    
1066
				$dhcpdconf .= "}\n";
1067
				$i++;
1068
			}
1069
		}
1070

    
1071
		$dhcpdifs[] = get_real_interface($dhcpif);
1072
		if ($newzone['domain-name']) {
1073
			if ($need_ddns_updates) {
1074
				$newzone['dns-servers'] = array($dhcpifconf['ddnsdomainprimary']);
1075
				$newzone['ddnsdomainkeyname'] = $dhcpifconf['ddnsdomainkeyname'];
1076
				$newzone['ddnsdomainkey'] = $dhcpifconf['ddnsdomainkey'];
1077
				$dhcpdconf .= dhcpdkey($dhcpifconf);
1078
			}
1079
			$ddns_zones[] = $newzone;
1080
		}
1081
	}
1082

    
1083
	if ($need_ddns_updates) {
1084
		$dhcpdconf .= "ddns-update-style interim;\n";
1085
		$dhcpdconf .= "update-static-leases on;\n";
1086

    
1087
		$dhcpdconf .= dhcpdzones($ddns_zones);
1088
	}
1089

    
1090
	/* write dhcpd.conf */
1091
	if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpd.conf", $dhcpdconf)) {
1092
		printf(gettext("Error: cannot open dhcpd.conf in services_dhcpdv4_configure().%s"), "\n");
1093
		unset($dhcpdconf);
1094
		return 1;
1095
	}
1096
	unset($dhcpdconf);
1097

    
1098
	/* create an empty leases database */
1099
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases")) {
1100
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases");
1101
	}
1102

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

    
1107
	/* fire up dhcpd in a chroot */
1108
	if (count($dhcpdifs) > 0) {
1109
		mwexec("/usr/local/sbin/dhcpd -user dhcpd -group _dhcp -chroot {$g['dhcpd_chroot_path']} -cf /etc/dhcpd.conf -pf {$g['varrun_path']}/dhcpd.pid " .
1110
			join(" ", $dhcpdifs));
1111
	}
1112

    
1113
	if (platform_booting()) {
1114
		print "done.\n";
1115
	}
1116

    
1117
	return 0;
1118
}
1119

    
1120
function dhcpdkey($dhcpifconf) {
1121
	$dhcpdconf = "";
1122
	if ($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "") {
1123
		$dhcpdconf .= "key {$dhcpifconf['ddnsdomainkeyname']} {\n";
1124
		$dhcpdconf .= "	algorithm hmac-md5;\n";
1125
		$dhcpdconf .= "	secret {$dhcpifconf['ddnsdomainkey']};\n";
1126
		$dhcpdconf .= "}\n";
1127
	}
1128

    
1129
	return $dhcpdconf;
1130
}
1131

    
1132
function dhcpdzones($ddns_zones) {
1133
	$dhcpdconf = "";
1134

    
1135
	if (is_array($ddns_zones)) {
1136
		$added_zones = array();
1137
		foreach ($ddns_zones as $zone) {
1138
			if (!is_array($zone) || empty($zone) || !is_array($zone['dns-servers'])) {
1139
				continue;
1140
			}
1141
			$primary = $zone['dns-servers'][0];
1142
			$secondary = empty($zone['dns-servers'][1]) ? "" : $zone['dns-servers'][1];
1143

    
1144
			// Make sure we aren't using any invalid or IPv6 DNS servers.
1145
			if (!is_ipaddrv4($primary)) {
1146
				if (is_ipaddrv4($secondary)) {
1147
					$primary = $secondary;
1148
					$secondary = "";
1149
				} else {
1150
					continue;
1151
				}
1152
			}
1153

    
1154
			// We don't need to add zones multiple times.
1155
			if ($zone['domain-name'] && !in_array($zone['domain-name'], $added_zones)) {
1156
				$dhcpdconf .= "zone {$zone['domain-name']}. {\n";
1157
				$dhcpdconf .= "	primary {$primary};\n";
1158
				if (is_ipaddrv4($secondary)) {
1159
					$dhcpdconf .= "	secondary {$secondary};\n";
1160
				}
1161
				if ($zone['ddnsdomainkeyname'] <> "" && $zone['ddnsdomainkey'] <> "") {
1162
					$dhcpdconf .= "	key {$zone['ddnsdomainkeyname']};\n";
1163
				}
1164
				$dhcpdconf .= "}\n";
1165
				$added_zones[] = $zone['domain-name'];
1166
			}
1167
			if ($zone['ptr-domain'] && !in_array($zone['ptr-domain'], $added_zones)) {
1168
				$dhcpdconf .= "zone {$zone['ptr-domain']} {\n";
1169
				$dhcpdconf .= "	primary {$primary};\n";
1170
				if (is_ipaddrv4($secondary)) {
1171
					$dhcpdconf .= "	secondary {$secondary};\n";
1172
				}
1173
				if ($zone['ddnsdomainkeyname'] <> "" && $zone['ddnsdomainkey'] <> "") {
1174
					$dhcpdconf .= "	key {$zone['ddnsdomainkeyname']};\n";
1175
				}
1176
				$dhcpdconf .= "}\n";
1177
				$added_zones[] = $zone['ptr-domain'];
1178
			}
1179
		}
1180
	}
1181

    
1182
	return $dhcpdconf;
1183
}
1184

    
1185
function services_dhcpdv6_configure($blacklist = array()) {
1186
	global $config, $g;
1187

    
1188
	if ($g['services_dhcp_server_enable'] == false) {
1189
		return;
1190
	}
1191

    
1192
	if (isset($config['system']['developerspew'])) {
1193
		$mt = microtime();
1194
		echo "services_dhcpd_configure($if) being called $mt\n";
1195
	}
1196

    
1197
	/* kill any running dhcpd */
1198
	if (isvalidpid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid")) {
1199
		killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid");
1200
	}
1201
	if (isvalidpid("{$g['varrun_path']}/dhcpleases6.pid")) {
1202
		killbypid("{$g['varrun_path']}/dhcpleases6.pid");
1203
	}
1204

    
1205
	/* DHCP enabled on any interfaces? */
1206
	if (!is_dhcpv6_server_enabled()) {
1207
		return 0;
1208
	}
1209

    
1210
	if (platform_booting()) {
1211
		if ($g['platform'] != $g['product_name']) {
1212
			/* restore the leases, if we have them */
1213
			if (file_exists("{$g['cf_conf_path']}/dhcp6leases.tgz")) {
1214
				$dhcprestore = "";
1215
				$dhcpreturn = "";
1216
				exec("cd /;LANG=C /usr/bin/tar -xzf {$g['cf_conf_path']}/dhcp6leases.tgz 2>&1", $dhcprestore, $dhcpreturn);
1217
				$dhcprestore = implode(" ", $dhcprestore);
1218
				if ($dhcpreturn <> 0) {
1219
					log_error("DHCP leases v6 restore failed exited with $dhcpreturn, the error is: $dhcprestore\n");
1220
				}
1221
			}
1222
		}
1223
	}
1224

    
1225
	$syscfg = $config['system'];
1226
	if (!is_array($config['dhcpdv6'])) {
1227
		$config['dhcpdv6'] = array();
1228
	}
1229
	$dhcpdv6cfg = $config['dhcpdv6'];
1230
	$Iflist = get_configured_interface_list();
1231
	$Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces());
1232

    
1233

    
1234
	if (platform_booting()) {
1235
		echo "Starting DHCPv6 service...";
1236
	} else {
1237
		sleep(1);
1238
	}
1239

    
1240
	/* we add a fake entry for interfaces that are set to track6 another WAN */
1241
	foreach ($Iflist as $ifname) {
1242
		/* Do not put in the config an interface which is down */
1243
		if (isset($blacklist[$ifname])) {
1244
			continue;
1245
		}
1246
		if (!empty($config['interfaces'][$ifname]['track6-interface'])) {
1247
			$realif = get_real_interface($ifname, "inet6");
1248
			$ifcfgipv6 = get_interface_ipv6($ifname);
1249
			if (!is_ipaddrv6($ifcfgipv6)) {
1250
				continue;
1251
			}
1252
			$ifcfgipv6 = Net_IPv6::getNetmask($ifcfgipv6, 64);
1253
			$trackifname = $config['interfaces'][$ifname]['track6-interface'];
1254
			$trackcfg = $config['interfaces'][$trackifname];
1255
			$pdlen = calculate_ipv6_delegation_length($trackifname);
1256
			$ifcfgipv6arr =explode(":", $ifcfgipv6);
1257
			$dhcpdv6cfg[$ifname] = array();
1258
			$dhcpdv6cfg[$ifname]['enable'] = true;
1259
			/* range */
1260
			$ifcfgipv6arr[7] = "1000";
1261
			$dhcpdv6cfg[$ifname]['range'] = array();
1262
			$dhcpdv6cfg[$ifname]['range']['from'] = Net_IPv6::compress(implode(":", $ifcfgipv6arr));
1263
			$ifcfgipv6arr[7] = "2000";
1264
			$dhcpdv6cfg[$ifname]['range']['to'] = Net_IPv6::compress(implode(":", $ifcfgipv6arr));
1265
			/* prefix length > 0? We can add dhcp6 prefix delegation server */
1266
			if ($pdlen > 2) {
1267
				$pdlenmax = $pdlen;
1268
				$pdlenhalf = $pdlenmax -1;
1269
				$pdlenmin = (64 - ceil($pdlenhalf / 4));
1270
				$dhcpdv6cfg[$ifname]['prefixrange'] = array();
1271
				$dhcpdv6cfg[$ifname]['prefixrange']['prefixlength'] = $pdlenmin;
1272

    
1273
				/* set the delegation start to half the current address block */
1274
				$range = Net_IPv6::parseAddress($ifcfgipv6, (64 - $pdlenmax));
1275
				$range['start'] = Net_IPv6::getNetmask($range['end'], (64 - $pdlenhalf));
1276

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

    
1281
				$dhcpdv6cfg[$ifname]['prefixrange']['from'] = Net_IPv6::compress($range['start']);
1282
				$dhcpdv6cfg[$ifname]['prefixrange']['to'] = Net_IPv6::compress($range['end']);
1283
			}
1284
			$dhcpdv6cfg[$ifname]['dns6ip'] = get_interface_ipv6($ifname);
1285
		}
1286
	}
1287

    
1288
	$custoptionsv6 = "";
1289
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1290
		if (is_array($dhcpv6ifconf['numberoptions']) && is_array($dhcpv6ifconf['numberoptions']['item'])) {
1291
			foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
1292
				$custoptionsv6 .= "option custom-{$dhcpv6if}-{$itemv6idx} code {$itemv6['number']} = text;\n";
1293
			}
1294
		}
1295
	}
1296

    
1297
	if (isset($dhcpv6ifconf['netboot']) && !empty($dhcpv6ifconf['bootfile_url'])) {
1298
		$custoptionsv6 .= "option dhcp6.bootfile-url code 59 = string;\n";
1299
	}
1300

    
1301
	$dhcpdv6conf = <<<EOD
1302

    
1303
option domain-name "{$syscfg['domain']}";
1304
option ldap-server code 95 = text;
1305
option domain-search-list code 119 = text;
1306
{$custoptionsv6}
1307
default-lease-time 7200;
1308
max-lease-time 86400;
1309
log-facility local7;
1310
one-lease-per-client true;
1311
deny duplicates;
1312
ping-check true;
1313
update-conflict-detection false;
1314

    
1315
EOD;
1316

    
1317
	if (!isset($dhcpv6ifconf['disableauthoritative'])) {
1318
		$dhcpdv6conf .= "authoritative;\n";
1319
	}
1320

    
1321
	if (isset($dhcpv6ifconf['alwaysbroadcast'])) {
1322
		$dhcpdv6conf .= "always-broadcast on\n";
1323
	}
1324

    
1325
	$dhcpdv6ifs = array();
1326

    
1327
	$dhcpv6num = 0;
1328
	$nsupdate = false;
1329

    
1330
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1331

    
1332
		$ddns_zones = array();
1333

    
1334
		$ifcfgv6 = $config['interfaces'][$dhcpv6if];
1335

    
1336
		if (!isset($dhcpv6ifconf['enable']) || !isset($Iflist[$dhcpv6if]) || !isset($ifcfgv6['enable'])) {
1337
			continue;
1338
		}
1339
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1340
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
1341
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1342

    
1343
		if ($is_olsr_enabled == true) {
1344
			if ($dhcpv6ifconf['netmask']) {
1345
				$subnetmask = gen_subnet_maskv6($dhcpv6ifconf['netmask']);
1346
			}
1347
		}
1348

    
1349
		$dnscfgv6 = "";
1350

    
1351
		if ($dhcpv6ifconf['domain']) {
1352
			$dnscfgv6 .= "	option domain-name \"{$dhcpv6ifconf['domain']}\";\n";
1353
		}
1354

    
1355
		if ($dhcpv6ifconf['domainsearchlist'] <> "") {
1356
			$dnscfgv6 .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpv6ifconf['domainsearchlist'])) . "\";\n";
1357
		}
1358

    
1359
		if (isset($dhcpv6ifconf['ddnsupdate'])) {
1360
			if ($dhcpv6ifconf['ddnsdomain'] <> "") {
1361
				$dnscfgv6 .= "	ddns-domainname \"{$dhcpv6ifconf['ddnsdomain']}\";\n";
1362
			}
1363
			$dnscfgv6 .= "	ddns-update-style interim;\n";
1364
			$nsupdate = true;
1365
		}
1366

    
1367
		if (is_array($dhcpv6ifconf['dnsserver']) && ($dhcpv6ifconf['dnsserver'][0])) {
1368
			$dnscfgv6 .= "	option dhcp6.name-servers " . join(",", $dhcpv6ifconf['dnsserver']) . ";";
1369
		} else if (((isset($config['dnsmasq']['enable'])) || isset($config['unbound']['enable'])) && (is_ipaddrv6($ifcfgipv6))) {
1370
			$dnscfgv6 .= "	option dhcp6.name-servers {$ifcfgipv6};";
1371
		} else if (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
1372
			$dns_arrv6 = array();
1373
			foreach ($syscfg['dnsserver'] as $dnsserver) {
1374
				if (is_ipaddrv6($dnsserver)) {
1375
					$dns_arrv6[] = $dnsserver;
1376
				}
1377
			}
1378
			if (!empty($dns_arrv6)) {
1379
				$dnscfgv6 .= "	option dhcp6.name-servers " . join(",", $dns_arrv6) . ";";
1380
			}
1381
		}
1382

    
1383
		if ($dhcpv6ifconf['domain']) {
1384
			$newzone = array();
1385
			$newzone['domain-name'] = $dhcpv6ifconf['domain'];
1386
			$newzone['dns-servers'][] = $dhcpv6ifconf['ddnsdomainprimary'];
1387
			$newzone['ddnsdomainkeyname'] = $dhcpv6ifconf['ddnsdomainkeyname'];
1388
			$newzone['ddnsdomainkey'] = $dhcpv6ifconf['ddnsdomainkey'];
1389
			$ddns_zones[] = $newzone;
1390
		}
1391

    
1392
		if (is_ipaddrv6($ifcfgipv6)) {
1393
			$dhcpdv6conf .= "subnet6 {$subnetv6}/{$ifcfgsnv6}";
1394
		} else {
1395
			$subnet6 = gen_subnetv6($dhcpv6ifconf['range']['from'], "64");
1396
			$dhcpdv6conf .= "subnet6 {$subnet6}/64";
1397
		}
1398
		$dhcpdv6conf .= " {\n";
1399

    
1400
		$dhcpdv6conf .= <<<EOD
1401
	range6 {$dhcpv6ifconf['range']['from']} {$dhcpv6ifconf['range']['to']};
1402
$dnscfgv6
1403

    
1404
EOD;
1405

    
1406
		if (is_ipaddrv6($dhcpv6ifconf['prefixrange']['from']) && is_ipaddrv6($dhcpv6ifconf['prefixrange']['to'])) {
1407
			$dhcpdv6conf .= "	prefix6 {$dhcpv6ifconf['prefixrange']['from']} {$dhcpv6ifconf['prefixrange']['to']} /{$dhcpv6ifconf['prefixrange']['prefixlength']};\n";
1408
		}
1409
		if (is_ipaddrv6($dhcpv6ifconf['dns6ip'])) {
1410
			$dhcpdv6conf .= "	option dhcp6.name-servers {$dhcpv6ifconf['dns6ip']};\n";
1411
		}
1412
		// default-lease-time
1413
		if ($dhcpv6ifconf['defaultleasetime']) {
1414
			$dhcpdv6conf .= "	default-lease-time {$dhcpv6ifconf['defaultleasetime']};\n";
1415
		}
1416

    
1417
		// max-lease-time
1418
		if ($dhcpv6ifconf['maxleasetime']) {
1419
			$dhcpdv6conf .= "	max-lease-time {$dhcpv6ifconf['maxleasetime']};\n";
1420
		}
1421

    
1422
		// ntp-servers
1423
		if (is_array($dhcpv6ifconf['ntpserver']) && $dhcpv6ifconf['ntpserver'][0]) {
1424
			$ntpservers = array();
1425
			foreach ($dhcpv6ifconf['ntpserver'] as $ntpserver) {
1426
				if (is_ipaddrv6($ntpserver)) {
1427
					$ntpservers[] = $ntpserver;
1428
				}
1429
			}
1430
			if (count($ntpservers) > 0) {
1431
				$dhcpdv6conf .= "       option dhcp6.sntp-servers " . join(",", $dhcpv6ifconf['ntpserver']) . ";\n";
1432
			}
1433
		}
1434
		// tftp-server-name
1435
		/* Needs ISC DHCPD support
1436
		 if ($dhcpv6ifconf['tftp'] <> "") {
1437
			$dhcpdv6conf .= "	option tftp-server-name \"{$dhcpv6ifconf['tftp']}\";\n";
1438
		 }
1439
		*/
1440

    
1441
		// Handle option, number rowhelper values
1442
		$dhcpdv6conf .= "\n";
1443
		if ($dhcpv6ifconf['numberoptions']['item']) {
1444
			foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
1445
				$itemv6_value = base64_decode($itemv6['value']);
1446
				$dhcpdv6conf .= "	option custom-{$dhcpv6if}-{$itemv6idx} \"{$itemv6_value}\";\n";
1447
			}
1448
		}
1449

    
1450
		// ldap-server
1451
		if ($dhcpv6ifconf['ldap'] <> "") {
1452
			$dhcpdv6conf .= "	option ldap-server \"{$dhcpv6ifconf['ldap']}\";\n";
1453
		}
1454

    
1455
		// net boot information
1456
		if (isset($dhcpv6ifconf['netboot'])) {
1457
			if (!empty($dhcpv6ifconf['bootfile_url'])) {
1458
				$dhcpdv6conf .= "	option dhcp6.bootfile-url \"{$dhcpv6ifconf['bootfile_url']}\";\n";
1459
			}
1460
		}
1461

    
1462
		$dhcpdv6conf .= "}\n";
1463

    
1464
		/* add static mappings */
1465
		/* Needs to use DUID */
1466
		if (is_array($dhcpv6ifconf['staticmap'])) {
1467
			$i = 0;
1468
			foreach ($dhcpv6ifconf['staticmap'] as $sm) {
1469
				$dhcpdv6conf .= <<<EOD
1470
host s_{$dhcpv6if}_{$i} {
1471
	host-identifier option dhcp6.client-id {$sm['duid']};
1472

    
1473
EOD;
1474
				if ($sm['ipaddrv6']) {
1475
					$dhcpdv6conf .= "	fixed-address6 {$sm['ipaddrv6']};\n";
1476
				}
1477

    
1478
				if ($sm['hostname']) {
1479
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
1480
					$dhhostname = str_replace(".", "_", $dhhostname);
1481
					$dhcpdv6conf .= "	option host-name {$dhhostname};\n";
1482
				}
1483
				if ($sm['filename']) {
1484
					$dhcpdv6conf .= "	filename \"{$sm['filename']}\";\n";
1485
				}
1486

    
1487
				if ($sm['rootpath']) {
1488
					$dhcpdv6conf .= "	option root-path \"{$sm['rootpath']}\";\n";
1489
				}
1490

    
1491
				$dhcpdv6conf .= "}\n";
1492
				$i++;
1493
			}
1494
		}
1495

    
1496
		if ($dhcpv6ifconf['domain']) {
1497
			$dhcpdv6conf .= dhcpdkey($dhcpv6ifconf);
1498
			$dhcpdv6conf .= dhcpdzones($ddns_zones);
1499
		}
1500

    
1501
		if ($config['dhcpdv6'][$dhcpv6if]['ramode'] <> "unmanaged" && isset($config['interfaces'][$dhcpv6if]['enable'])) {
1502
			if (preg_match("/poes/si", $dhcpv6if)) {
1503
				/* magic here */
1504
				$dhcpdv6ifs = array_merge($dhcpdv6ifs, get_pppoes_child_interfaces($dhcpv6if));
1505
			} else {
1506
				$realif = get_real_interface($dhcpv6if, "inet6");
1507
				if (stristr("$realif", "bridge")) {
1508
					$mac = get_interface_mac($realif);
1509
					$v6address = generate_ipv6_from_mac($mac);
1510
					/* Create link local address for bridges */
1511
					mwexec("/sbin/ifconfig {$realif} inet6 {$v6address}");
1512
				}
1513
				$realif = escapeshellcmd($realif);
1514
				$dhcpdv6ifs[] = $realif;
1515
			}
1516
		}
1517
	}
1518

    
1519
	if ($nsupdate) {
1520
		$dhcpdv6conf .= "ddns-update-style interim;\n";
1521
	} else {
1522
		$dhcpdv6conf .= "ddns-update-style none;\n";
1523
	}
1524

    
1525
	/* write dhcpdv6.conf */
1526
	if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf", $dhcpdv6conf)) {
1527
		log_error("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n");
1528
		if (platform_booting()) {
1529
			printf("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n");
1530
		}
1531
		unset($dhcpdv6conf);
1532
		return 1;
1533
	}
1534
	unset($dhcpdv6conf);
1535

    
1536
	/* create an empty leases v6 database */
1537
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases")) {
1538
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases");
1539
	}
1540

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

    
1545
	/* fire up dhcpd in a chroot */
1546
	if (count($dhcpdv6ifs) > 0) {
1547
		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 " .
1548
			join(" ", $dhcpdv6ifs));
1549
		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");
1550
	}
1551
	if (platform_booting()) {
1552
		print gettext("done.") . "\n";
1553
	}
1554

    
1555
	return 0;
1556
}
1557

    
1558
function services_igmpproxy_configure() {
1559
	global $config, $g;
1560

    
1561
	/* kill any running igmpproxy */
1562
	killbyname("igmpproxy");
1563

    
1564
	if (!is_array($config['igmpproxy']['igmpentry']) || (count($config['igmpproxy']['igmpentry']) == 0)) {
1565
		return 1;
1566
	}
1567

    
1568
	$iflist = get_configured_interface_list();
1569

    
1570
	$igmpconf = <<<EOD
1571

    
1572
##------------------------------------------------------
1573
## Enable Quickleave mode (Sends Leave instantly)
1574
##------------------------------------------------------
1575
quickleave
1576

    
1577
EOD;
1578

    
1579
	foreach ($config['igmpproxy']['igmpentry'] as $igmpcf) {
1580
		unset($iflist[$igmpcf['ifname']]);
1581
		$realif = get_real_interface($igmpcf['ifname']);
1582
		if (empty($igmpcf['threshold'])) {
1583
			$threshld = 1;
1584
		} else {
1585
			$threshld = $igmpcf['threshold'];
1586
		}
1587
		$igmpconf .= "phyint {$realif} {$igmpcf['type']} ratelimit 0 threshold {$threshld}\n";
1588

    
1589
		if ($igmpcf['address'] <> "") {
1590
			$item = explode(" ", $igmpcf['address']);
1591
			foreach ($item as $iww) {
1592
				$igmpconf .= "altnet {$iww}\n";
1593
			}
1594
		}
1595
		$igmpconf .= "\n";
1596
	}
1597
	foreach ($iflist as $ifn) {
1598
		$realif = get_real_interface($ifn);
1599
		$igmpconf .= "phyint {$realif} disabled\n";
1600
	}
1601
	$igmpconf .= "\n";
1602

    
1603
	$igmpfl = fopen($g['tmp_path'] . "/igmpproxy.conf", "w");
1604
	if (!$igmpfl) {
1605
		log_error(gettext("Could not write Igmpproxy configuration file!"));
1606
		return;
1607
	}
1608
	fwrite($igmpfl, $igmpconf);
1609
	fclose($igmpfl);
1610
	unset($igmpconf);
1611

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

    
1615
	return 0;
1616
}
1617

    
1618
function services_dhcrelay_configure() {
1619
	global $config, $g;
1620

    
1621
	if (isset($config['system']['developerspew'])) {
1622
		$mt = microtime();
1623
		echo "services_dhcrelay_configure() being called $mt\n";
1624
	}
1625

    
1626
	/* kill any running dhcrelay */
1627
	killbypid("{$g['varrun_path']}/dhcrelay.pid");
1628

    
1629
	$dhcrelaycfg =& $config['dhcrelay'];
1630

    
1631
	/* DHCPRelay enabled on any interfaces? */
1632
	if (!isset($dhcrelaycfg['enable'])) {
1633
		return 0;
1634
	}
1635

    
1636
	if (platform_booting()) {
1637
		echo gettext("Starting DHCP relay service...");
1638
	} else {
1639
		sleep(1);
1640
	}
1641

    
1642
	$iflist = get_configured_interface_list();
1643

    
1644
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1645
	foreach ($dhcifaces as $dhcrelayif) {
1646
		if (!isset($iflist[$dhcrelayif]) ||
1647
		    link_interface_to_bridge($dhcrelayif)) {
1648
			continue;
1649
		}
1650

    
1651
		if (is_ipaddr(get_interface_ip($dhcrelayif))) {
1652
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1653
		}
1654
	}
1655

    
1656
	/*
1657
	 * In order for the relay to work, it needs to be active
1658
	 * on the interface in which the destination server sits.
1659
	 */
1660
	$srvips = explode(",", $dhcrelaycfg['server']);
1661
	if (!is_array($srvips)) {
1662
		log_error("No destination IP has been configured!");
1663
		return;
1664
	}
1665

    
1666
	foreach ($srvips as $srcidx => $srvip) {
1667
		unset($destif);
1668
		foreach ($iflist as $ifname) {
1669
			$subnet = get_interface_ip($ifname);
1670
			if (!is_ipaddr($subnet)) {
1671
				continue;
1672
			}
1673
			$subnet .= "/" . get_interface_subnet($ifname);
1674
			if (ip_in_subnet($srvip, $subnet)) {
1675
				$destif = get_real_interface($ifname);
1676
				break;
1677
			}
1678
		}
1679
		if (!isset($destif)) {
1680
			foreach (get_staticroutes() as $rtent) {
1681
				if (ip_in_subnet($srvip, $rtent['network'])) {
1682
					$a_gateways = return_gateways_array(true);
1683
					$destif = $a_gateways[$rtent['gateway']]['interface'];
1684
					break;
1685
				}
1686
			}
1687
		}
1688

    
1689
		if (!isset($destif)) {
1690
			/* Create a array from the existing route table */
1691
			exec("/usr/bin/netstat -rnWf inet", $route_str);
1692
			array_shift($route_str);
1693
			array_shift($route_str);
1694
			array_shift($route_str);
1695
			array_shift($route_str);
1696
			$route_arr = array();
1697
			foreach ($route_str as $routeline) {
1698
				$items = preg_split("/[ ]+/i", $routeline);
1699
				if (is_subnetv4($items[0])) {
1700
					$subnet = $items[0];
1701
				} elseif (is_ipaddrv4($items[0])) {
1702
					$subnet = "{$items[0]}/32";
1703
				} else {
1704
					// Not a subnet or IP address, skip to the next line.
1705
					continue;
1706
				}
1707
				if (ip_in_subnet($srvip, $subnet)) {
1708
					$destif = trim($items[6]);
1709
					break;
1710
				}
1711
			}
1712
		}
1713

    
1714
		if (!isset($destif)) {
1715
			if (is_array($config['gateways']['gateway_item'])) {
1716
				foreach ($config['gateways']['gateway_item'] as $gateway) {
1717
					if (isset($gateway['defaultgw'])) {
1718
						$destif = get_real_interface($gateway['interface']);
1719
						break;
1720
					}
1721
				}
1722
			} else {
1723
				$destif = get_real_interface("wan");
1724
			}
1725
		}
1726

    
1727
		if (!empty($destif)) {
1728
			$dhcrelayifs[] = $destif;
1729
		}
1730
	}
1731
	$dhcrelayifs = array_unique($dhcrelayifs);
1732

    
1733
	/* fire up dhcrelay */
1734
	if (empty($dhcrelayifs)) {
1735
		log_error("No suitable interface found for running dhcrelay!");
1736
		return; /* XXX */
1737
	}
1738

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

    
1741
	if (isset($dhcrelaycfg['agentoption'])) {
1742
		$cmd .= " -a -m replace";
1743
	}
1744

    
1745
	$cmd .= " " . implode(" ", $srvips);
1746
	mwexec($cmd);
1747
	unset($cmd);
1748

    
1749
	return 0;
1750
}
1751

    
1752
function services_dhcrelay6_configure() {
1753
	global $config, $g;
1754

    
1755
	if (isset($config['system']['developerspew'])) {
1756
		$mt = microtime();
1757
		echo "services_dhcrelay6_configure() being called $mt\n";
1758
	}
1759

    
1760
	/* kill any running dhcrelay */
1761
	killbypid("{$g['varrun_path']}/dhcrelay6.pid");
1762

    
1763
	$dhcrelaycfg =& $config['dhcrelay6'];
1764

    
1765
	/* DHCPv6 Relay enabled on any interfaces? */
1766
	if (!isset($dhcrelaycfg['enable'])) {
1767
		return 0;
1768
	}
1769

    
1770
	if (platform_booting()) {
1771
		echo gettext("Starting DHCPv6 relay service...");
1772
	} else {
1773
		sleep(1);
1774
	}
1775

    
1776
	$iflist = get_configured_interface_list();
1777

    
1778
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1779
	foreach ($dhcifaces as $dhcrelayif) {
1780
		if (!isset($iflist[$dhcrelayif]) ||
1781
		    link_interface_to_bridge($dhcrelayif)) {
1782
			continue;
1783
		}
1784

    
1785
		if (is_ipaddrv6(get_interface_ipv6($dhcrelayif))) {
1786
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1787
		}
1788
	}
1789
	$dhcrelayifs = array_unique($dhcrelayifs);
1790

    
1791
	/*
1792
	 * In order for the relay to work, it needs to be active
1793
	 * on the interface in which the destination server sits.
1794
	 */
1795
	$srvips = explode(",", $dhcrelaycfg['server']);
1796
	$srvifaces = array();
1797
	foreach ($srvips as $srcidx => $srvip) {
1798
		unset($destif);
1799
		foreach ($iflist as $ifname) {
1800
			$subnet = get_interface_ipv6($ifname);
1801
			if (!is_ipaddrv6($subnet)) {
1802
				continue;
1803
			}
1804
			$subnet .= "/" . get_interface_subnetv6($ifname);
1805
			if (ip_in_subnet($srvip, $subnet)) {
1806
				$destif = get_real_interface($ifname);
1807
				break;
1808
			}
1809
		}
1810
		if (!isset($destif)) {
1811
			if (is_array($config['staticroutes']['route'])) {
1812
				foreach ($config['staticroutes']['route'] as $rtent) {
1813
					if (ip_in_subnet($srvip, $rtent['network'])) {
1814
						$a_gateways = return_gateways_array(true);
1815
						$destif = $a_gateways[$rtent['gateway']]['interface'];
1816
						break;
1817
					}
1818
				}
1819
			}
1820
		}
1821

    
1822
		if (!isset($destif)) {
1823
			/* Create a array from the existing route table */
1824
			exec("/usr/bin/netstat -rnWf inet6", $route_str);
1825
			array_shift($route_str);
1826
			array_shift($route_str);
1827
			array_shift($route_str);
1828
			array_shift($route_str);
1829
			$route_arr = array();
1830
			foreach ($route_str as $routeline) {
1831
				$items = preg_split("/[ ]+/i", $routeline);
1832
				if (ip_in_subnet($srvip, $items[0])) {
1833
					$destif = trim($items[6]);
1834
					break;
1835
				}
1836
			}
1837
		}
1838

    
1839
		if (!isset($destif)) {
1840
			if (is_array($config['gateways']['gateway_item'])) {
1841
				foreach ($config['gateways']['gateway_item'] as $gateway) {
1842
					if (isset($gateway['defaultgw'])) {
1843
						$destif = get_real_interface($gateway['interface']);
1844
						break;
1845
					}
1846
				}
1847
			} else {
1848
				$destif = get_real_interface("wan");
1849
			}
1850
		}
1851

    
1852
		if (!empty($destif)) {
1853
			$srvifaces[] = "{$srvip}%{$destif}";
1854
		}
1855
	}
1856

    
1857
	/* fire up dhcrelay */
1858
	if (empty($dhcrelayifs) || empty($srvifaces)) {
1859
		log_error("No suitable interface found for running dhcrelay -6!");
1860
		return; /* XXX */
1861
	}
1862

    
1863
	$cmd = "/usr/local/sbin/dhcrelay -6 -pf \"{$g['varrun_path']}/dhcrelay6.pid\"";
1864
	foreach ($dhcrelayifs as $dhcrelayif) {
1865
		$cmd .= " -l {$dhcrelayif}";
1866
	}
1867
	foreach ($srvifaces as $srviface) {
1868
		$cmd .= " -u \"{$srviface}\"";
1869
	}
1870
	mwexec($cmd);
1871
	unset($cmd);
1872

    
1873
	return 0;
1874
}
1875

    
1876
function services_dyndns_configure_client($conf) {
1877

    
1878
	if (!isset($conf['enable'])) {
1879
		return;
1880
	}
1881

    
1882
	/* load up the dyndns.class */
1883
	require_once("dyndns.class");
1884

    
1885
	$dns = new updatedns($dnsService = $conf['type'],
1886
		$dnsHost = $conf['host'],
1887
		$dnsDomain = $conf['domainname'],
1888
		$dnsUser = $conf['username'],
1889
		$dnsPass = $conf['password'],
1890
		$dnsWildcard = $conf['wildcard'],
1891
		$dnsMX = $conf['mx'],
1892
		$dnsIf = "{$conf['interface']}",
1893
		$dnsBackMX = NULL,
1894
		$dnsServer = NULL,
1895
		$dnsPort = NULL,
1896
		$dnsUpdateURL = "{$conf['updateurl']}",
1897
		$forceUpdate = $conf['force'],
1898
		$dnsZoneID = $conf['zoneid'],
1899
		$dnsTTL = $conf['ttl'],
1900
		$dnsResultMatch = "{$conf['resultmatch']}",
1901
		$dnsRequestIf = "{$conf['requestif']}",
1902
		$dnsID = "{$conf['id']}",
1903
		$dnsVerboseLog = $conf['verboselog'],
1904
		$curlIpresolveV4 = $conf['curl_ipresolve_v4'],
1905
		$curlSslVerifypeer = $conf['curl_ssl_verifypeer']);
1906
}
1907

    
1908
function services_dyndns_configure($int = "") {
1909
	global $config, $g;
1910
	if (isset($config['system']['developerspew'])) {
1911
		$mt = microtime();
1912
		echo "services_dyndns_configure() being called $mt\n";
1913
	}
1914

    
1915
	$dyndnscfg = $config['dyndnses']['dyndns'];
1916
	$gwgroups = return_gateway_groups_array();
1917
	if (is_array($dyndnscfg)) {
1918
		if (platform_booting()) {
1919
			echo gettext("Starting DynDNS clients...");
1920
		}
1921

    
1922
		foreach ($dyndnscfg as $dyndns) {
1923
			if ((empty($int)) || ($int == $dyndns['interface']) || (is_array($gwgroups[$dyndns['interface']]))) {
1924
				$dyndns['verboselog'] = isset($dyndns['verboselog']);
1925
				$dyndns['curl_ipresolve_v4'] = isset($dyndns['curl_ipresolve_v4']);
1926
				$dyndns['curl_ssl_verifypeer'] = isset($dyndns['curl_ssl_verifypeer']);
1927
				services_dyndns_configure_client($dyndns);
1928
				sleep(1);
1929
			}
1930
		}
1931

    
1932
		if (platform_booting()) {
1933
			echo gettext("done.") . "\n";
1934
		}
1935
	}
1936

    
1937
	return 0;
1938
}
1939

    
1940
function dyndnsCheckIP($int) {
1941
	global $config;
1942
	$ip_address = get_interface_ip($int);
1943
	if (is_private_ip($ip_address)) {
1944
		$gateways_status = return_gateways_status(true);
1945
		// If the gateway for this interface is down, then the external check cannot work.
1946
		// Avoid the long wait for the external check to timeout.
1947
		if (stristr($gateways_status[$config['interfaces'][$int]['gateway']]['status'], "down")) {
1948
			return "down";
1949
		}
1950
		$hosttocheck = "http://checkip.dyndns.org";
1951
		$ip_ch = curl_init($hosttocheck);
1952
		curl_setopt($ip_ch, CURLOPT_RETURNTRANSFER, 1);
1953
		curl_setopt($ip_ch, CURLOPT_SSL_VERIFYPEER, FALSE);
1954
		curl_setopt($ip_ch, CURLOPT_INTERFACE, 'host!' . $ip_address);
1955
		curl_setopt($ip_ch, CURLOPT_CONNECTTIMEOUT, '30');
1956
		curl_setopt($ip_ch, CURLOPT_TIMEOUT, 120);
1957
		curl_setopt($ip_ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1958
		$ip_result_page = curl_exec($ip_ch);
1959
		curl_close($ip_ch);
1960
		$ip_result_decoded = urldecode($ip_result_page);
1961
		preg_match('=Current IP Address: (.*)</body>=siU', $ip_result_decoded, $matches);
1962
		$ip_address = trim($matches[1]);
1963
	}
1964
	return $ip_address;
1965
}
1966

    
1967
function services_dnsmasq_configure($restart_dhcp = true) {
1968
	global $config, $g;
1969
	$return = 0;
1970

    
1971
	// hard coded args: will be removed to avoid duplication if specified in custom_options
1972
	$standard_args = array(
1973
		"dns-forward-max" => "--dns-forward-max=5000",
1974
		"cache-size" => "--cache-size=10000",
1975
		"local-ttl" => "--local-ttl=1"
1976
	);
1977

    
1978

    
1979
	if (isset($config['system']['developerspew'])) {
1980
		$mt = microtime();
1981
		echo "services_dnsmasq_configure() being called $mt\n";
1982
	}
1983

    
1984
	/* kill any running dnsmasq */
1985
	if (file_exists("{$g['varrun_path']}/dnsmasq.pid")) {
1986
		sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM");
1987
	}
1988

    
1989
	if (isset($config['dnsmasq']['enable'])) {
1990

    
1991
		if (platform_booting()) {
1992
			echo gettext("Starting DNS forwarder...");
1993
		} else {
1994
			sleep(1);
1995
		}
1996

    
1997
		/* generate hosts file */
1998
		if (system_hosts_generate() != 0) {
1999
			$return = 1;
2000
		}
2001

    
2002
		$args = "";
2003

    
2004
		if (isset($config['dnsmasq']['regdhcp'])) {
2005
			$args .= " --dhcp-hostsfile={$g['varetc_path']}/hosts ";
2006
		}
2007

    
2008
		/* Setup listen port, if non-default */
2009
		if (is_port($config['dnsmasq']['port'])) {
2010
			$args .= " --port={$config['dnsmasq']['port']} ";
2011
		}
2012

    
2013
		$listen_addresses = "";
2014
		if (isset($config['dnsmasq']['interface'])) {
2015
			$interfaces = explode(",", $config['dnsmasq']['interface']);
2016
			foreach ($interfaces as $interface) {
2017
				$if = get_real_interface($interface);
2018
				if (does_interface_exist($if)) {
2019
					$laddr = get_interface_ip($interface);
2020
					if (is_ipaddrv4($laddr)) {
2021
						$listen_addresses .= " --listen-address={$laddr} ";
2022
					}
2023
					$laddr6 = get_interface_ipv6($interface);
2024
					if (is_ipaddrv6($laddr6) && !isset($config['dnsmasq']['strictbind'])) {
2025
						/*
2026
						 * XXX: Since dnsmasq does not support link-local address
2027
						 * with scope specified. These checks are being done.
2028
						 */
2029
						if (is_linklocal($laddr6) && strstr($laddr6, "%")) {
2030
							$tmpaddrll6 = explode("%", $laddr6);
2031
							$listen_addresses .= " --listen-address={$tmpaddrll6[0]} ";
2032
						} else {
2033
							$listen_addresses .= " --listen-address={$laddr6} ";
2034
						}
2035
					}
2036
				}
2037
			}
2038
			if (!empty($listen_addresses)) {
2039
				$args .= " {$listen_addresses} ";
2040
				if (isset($config['dnsmasq']['strictbind'])) {
2041
					$args .= " --bind-interfaces ";
2042
				}
2043
			}
2044
		}
2045

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

    
2053
			// Build an array of domain overrides to help in checking for matches.
2054
			$override_a = array();
2055
			if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2056
				foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2057
					$override_a[$override['domain']] = "y";
2058
				}
2059
			}
2060

    
2061
			// Build an array of the private reverse lookup domain names
2062
			$reverse_domain_a = array("10.in-addr.arpa", "168.192.in-addr.arpa");
2063
			// Unfortunately the 172.16.0.0/12 range does not map nicely to the in-addr.arpa scheme.
2064
			for ($subnet_num = 16; $subnet_num < 32; $subnet_num++) {
2065
				$reverse_domain_a[] = "$subnet_num.172.in-addr.arpa";
2066
			}
2067

    
2068
			// Set the --server parameter to nowhere for each reverse domain name that was not specifically specified in a domain override.
2069
			foreach ($reverse_domain_a as $reverse_domain) {
2070
				if (!isset($override_a[$reverse_domain])) {
2071
					$args .= " --server=/$reverse_domain/ ";
2072
				}
2073
			}
2074
			unset($override_a);
2075
			unset($reverse_domain_a);
2076
		}
2077

    
2078
		/* Setup forwarded domains */
2079
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2080
			foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2081
				if ($override['ip'] == "!") {
2082
					$override[ip] = "";
2083
				}
2084
				$args .= ' --server=/' . $override['domain'] . '/' . $override['ip'];
2085
			}
2086
		}
2087

    
2088
		/* Allow DNS Rebind for forwarded domains */
2089
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2090
			if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
2091
				foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2092
					$args .= ' --rebind-domain-ok=/' . $override['domain'] . '/ ';
2093
				}
2094
			}
2095
		}
2096

    
2097
		if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
2098
			$dns_rebind = "--rebind-localhost-ok --stop-dns-rebind";
2099
		}
2100

    
2101
		if (isset($config['dnsmasq']['strict_order'])) {
2102
			$args .= " --strict-order ";
2103
		}
2104

    
2105
		if (isset($config['dnsmasq']['domain_needed'])) {
2106
			$args .= " --domain-needed ";
2107
		}
2108

    
2109
		if ($config['dnsmasq']['custom_options']) {
2110
			foreach (preg_split('/\s+/', $config['dnsmasq']['custom_options']) as $c) {
2111
				$args .= " " . escapeshellarg("--{$c}");
2112
				$p = explode('=', $c);
2113
				if (array_key_exists($p[0], $standard_args)) {
2114
					unset($standard_args[$p[0]]);
2115
				}
2116
			}
2117
		}
2118
		$args .= ' ' . implode(' ', array_values($standard_args));
2119

    
2120
		/* run dnsmasq */
2121
		$cmd = "/usr/local/sbin/dnsmasq --all-servers {$dns_rebind} {$args}";
2122
		//log_error("dnsmasq command: {$cmd}");
2123
		mwexec_bg($cmd);
2124
		unset($args);
2125

    
2126
		system_dhcpleases_configure();
2127

    
2128
		if (platform_booting()) {
2129
			echo gettext("done.") . "\n";
2130
		}
2131
	}
2132

    
2133
	if (!platform_booting() && $restart_dhcp) {
2134
		if (services_dhcpd_configure() != 0) {
2135
			$return = 1;
2136
		}
2137
	}
2138

    
2139
	return $return;
2140
}
2141

    
2142
function services_unbound_configure($restart_dhcp = true) {
2143
	global $config, $g;
2144
	$return = 0;
2145

    
2146
	if (isset($config['system']['developerspew'])) {
2147
		$mt = microtime();
2148
		echo "services_unbound_configure() being called $mt\n";
2149
	}
2150

    
2151
	// kill any running Unbound instance
2152
	if (file_exists("{$g['varrun_path']}/unbound.pid")) {
2153
		sigkillbypid("{$g['varrun_path']}/unbound.pid", "TERM");
2154
	}
2155

    
2156
	if (isset($config['unbound']['enable'])) {
2157
		if (platform_booting()) {
2158
			echo gettext("Starting DNS Resolver...");
2159
		} else {
2160
			sleep(1);
2161
		}
2162

    
2163
		/* generate hosts file */
2164
		if (system_hosts_generate() != 0) {
2165
			$return = 1;
2166
		}
2167

    
2168
		require_once('/etc/inc/unbound.inc');
2169
		sync_unbound_service();
2170
		if (platform_booting()) {
2171
			echo gettext("done.") . "\n";
2172
		}
2173

    
2174
		system_dhcpleases_configure();
2175
	}
2176

    
2177
	if (!platform_booting() && $restart_dhcp) {
2178
		if (services_dhcpd_configure() != 0) {
2179
			$return = 1;
2180
		}
2181
	}
2182

    
2183
	return $return;
2184
}
2185

    
2186
function services_snmpd_configure() {
2187
	global $config, $g;
2188
	if (isset($config['system']['developerspew'])) {
2189
		$mt = microtime();
2190
		echo "services_snmpd_configure() being called $mt\n";
2191
	}
2192

    
2193
	/* kill any running snmpd */
2194
	sigkillbypid("{$g['varrun_path']}/snmpd.pid", "TERM");
2195
	sleep(2);
2196
	if (is_process_running("bsnmpd")) {
2197
		mwexec("/usr/bin/killall bsnmpd", true);
2198
	}
2199

    
2200
	if (isset($config['snmpd']['enable'])) {
2201

    
2202
		if (platform_booting()) {
2203
			echo gettext("Starting SNMP daemon... ");
2204
		}
2205

    
2206
		/* generate snmpd.conf */
2207
		$fd = fopen("{$g['varetc_path']}/snmpd.conf", "w");
2208
		if (!$fd) {
2209
			printf(gettext("Error: cannot open snmpd.conf in services_snmpd_configure().%s"), "\n");
2210
			return 1;
2211
		}
2212

    
2213

    
2214
		$snmpdconf = <<<EOD
2215
location := "{$config['snmpd']['syslocation']}"
2216
contact := "{$config['snmpd']['syscontact']}"
2217
read := "{$config['snmpd']['rocommunity']}"
2218

    
2219
EOD;
2220

    
2221
/* No docs on what write strings do there for disable for now.
2222
		if (isset($config['snmpd']['rwenable']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2223
			$snmpdconf .= <<<EOD
2224
# write string
2225
write := "{$config['snmpd']['rwcommunity']}"
2226

    
2227
EOD;
2228
		}
2229
*/
2230

    
2231

    
2232
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2233
			$snmpdconf .= <<<EOD
2234
# SNMP Trap support.
2235
traphost := {$config['snmpd']['trapserver']}
2236
trapport := {$config['snmpd']['trapserverport']}
2237
trap := "{$config['snmpd']['trapstring']}"
2238

    
2239

    
2240
EOD;
2241
		}
2242

    
2243
		$platform = trim(file_get_contents('/etc/platform'));
2244
		if (($platform == "pfSense") && ($g['product_name'] != "pfSense")) {
2245
			$platform = $g['product_name'];
2246
		}
2247
		$sysDescr = "{$g['product_name']} " . php_uname("n") .
2248
			" {$g['product_version']} {$platform} " . php_uname("s") .
2249
			" " . php_uname("r") . " " . php_uname("m");
2250

    
2251
		$snmpdconf .= <<<EOD
2252
system := 1     # pfSense
2253
%snmpd
2254
sysDescr			= "{$sysDescr}"
2255
begemotSnmpdDebugDumpPdus       = 2
2256
begemotSnmpdDebugSyslogPri      = 7
2257
begemotSnmpdCommunityString.0.1 = $(read)
2258

    
2259
EOD;
2260

    
2261
/* No docs on what write strings do there for disable for now.
2262
		if (isset($config['snmpd']['rwcommunity']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2263
			$snmpdconf .= <<<EOD
2264
begemotSnmpdCommunityString.0.2 = $(write)
2265

    
2266
EOD;
2267
		}
2268
*/
2269

    
2270

    
2271
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2272
			$snmpdconf .= <<<EOD
2273
begemotTrapSinkStatus.[$(traphost)].$(trapport) = 4
2274
begemotTrapSinkVersion.[$(traphost)].$(trapport) = 2
2275
begemotTrapSinkComm.[$(traphost)].$(trapport) = $(trap)
2276

    
2277
EOD;
2278
		}
2279

    
2280

    
2281
		$snmpdconf .= <<<EOD
2282
begemotSnmpdCommunityDisable    = 1
2283

    
2284
EOD;
2285

    
2286
		$bind_to_ip = "0.0.0.0";
2287
		if (isset($config['snmpd']['bindip'])) {
2288
			if (is_ipaddr($config['snmpd']['bindip'])) {
2289
				$bind_to_ip = $config['snmpd']['bindip'];
2290
			} else {
2291
				$if = get_real_interface($config['snmpd']['bindip']);
2292
				if (does_interface_exist($if)) {
2293
					$bind_to_ip = get_interface_ip($config['snmpd']['bindip']);
2294
				}
2295
			}
2296
		}
2297

    
2298
		if (is_port($config['snmpd']['pollport'])) {
2299
			$snmpdconf .= <<<EOD
2300
begemotSnmpdPortStatus.{$bind_to_ip}.{$config['snmpd']['pollport']} = 1
2301

    
2302
EOD;
2303

    
2304
		}
2305

    
2306
		$snmpdconf .= <<<EOD
2307
begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1
2308
begemotSnmpdLocalPortType."/var/run/snmpd.sock" = 4
2309

    
2310
# These are bsnmp macros not php vars.
2311
sysContact      = $(contact)
2312
sysLocation     = $(location)
2313
sysObjectId     = 1.3.6.1.4.1.12325.1.1.2.1.$(system)
2314

    
2315
snmpEnableAuthenTraps = 2
2316

    
2317
EOD;
2318

    
2319
		if (is_array($config['snmpd']['modules'])) {
2320
			if (isset($config['snmpd']['modules']['mibii'])) {
2321
			$snmpdconf .= <<<EOD
2322
begemotSnmpdModulePath."mibII"  = "/usr/lib/snmp_mibII.so"
2323

    
2324
EOD;
2325
			}
2326

    
2327
			if (isset($config['snmpd']['modules']['netgraph'])) {
2328
				$snmpdconf .= <<<EOD
2329
begemotSnmpdModulePath."netgraph" = "/usr/lib/snmp_netgraph.so"
2330
%netgraph
2331
begemotNgControlNodeName = "snmpd"
2332

    
2333
EOD;
2334
			}
2335

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

    
2340
EOD;
2341
			}
2342

    
2343
			if (isset($config['snmpd']['modules']['hostres'])) {
2344
				$snmpdconf .= <<<EOD
2345
begemotSnmpdModulePath."hostres"     = "/usr/lib/snmp_hostres.so"
2346

    
2347
EOD;
2348
			}
2349

    
2350
			if (isset($config['snmpd']['modules']['bridge'])) {
2351
				$snmpdconf .= <<<EOD
2352
begemotSnmpdModulePath."bridge"     = "/usr/lib/snmp_bridge.so"
2353
# config must end with blank line
2354

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

    
2361
EOD;
2362
			}
2363
			if (isset($config['snmpd']['modules']['regex'])) {
2364
				$snmpdconf .= <<<EOD
2365
begemotSnmpdModulePath."regex"     = "/usr/local/lib/snmp_regex.so"
2366

    
2367
EOD;
2368
			}
2369
		}
2370

    
2371
		fwrite($fd, $snmpdconf);
2372
		fclose($fd);
2373
		unset($snmpdconf);
2374

    
2375
		/* run bsnmpd */
2376
		mwexec("/usr/sbin/bsnmpd -c {$g['varetc_path']}/snmpd.conf" .
2377
			" -p {$g['varrun_path']}/snmpd.pid");
2378

    
2379
		if (platform_booting()) {
2380
			echo gettext("done.") . "\n";
2381
		}
2382
	}
2383

    
2384
	return 0;
2385
}
2386

    
2387
function services_dnsupdate_process($int = "", $updatehost = "", $forced = false) {
2388
	global $config, $g;
2389
	if (isset($config['system']['developerspew'])) {
2390
		$mt = microtime();
2391
		echo "services_dnsupdate_process() being called $mt\n";
2392
	}
2393

    
2394
	/* Dynamic DNS updating active? */
2395
	if (is_array($config['dnsupdates']['dnsupdate'])) {
2396
		$notify_text = "";
2397
		$gwgroups = return_gateway_groups_array();
2398
		foreach ($config['dnsupdates']['dnsupdate'] as $i => $dnsupdate) {
2399
			if (!isset($dnsupdate['enable'])) {
2400
				continue;
2401
			}
2402
			if (!empty($int) && ($int != $dnsupdate['interface']) && (!is_array($gwgroups[$dnsupdate['interface']]))) {
2403
				continue;
2404
			}
2405
			if (!empty($updatehost) && ($updatehost != $dnsupdate['host'])) {
2406
				continue;
2407
			}
2408

    
2409
			/* determine interface name */
2410
			$if = get_failover_interface($dnsupdate['interface']);
2411

    
2412
			if (isset($dnsupdate['usepublicip'])) {
2413
				$wanip = dyndnsCheckIP($if);
2414
			} else {
2415
				$wanip = get_interface_ip($if);
2416
			}
2417

    
2418
			$wanipv6 = get_interface_ipv6($if);
2419
			$cacheFile = "{$g['conf_path']}/dyndns_{$dnsupdate['interface']}_rfc2136_" . escapeshellarg($dnsupdate['host']) . "_{$dnsupdate['server']}.cache";
2420
			$currentTime = time();
2421

    
2422
			if ($wanip || $wanipv6) {
2423
				$keyname = $dnsupdate['keyname'];
2424
				/* trailing dot */
2425
				if (substr($keyname, -1) != ".") {
2426
					$keyname .= ".";
2427
				}
2428

    
2429
				$hostname = $dnsupdate['host'];
2430
				/* trailing dot */
2431
				if (substr($hostname, -1) != ".") {
2432
					$hostname .= ".";
2433
				}
2434

    
2435
				/* write private key file
2436
				   this is dumb - public and private keys are the same for HMAC-MD5,
2437
				   but nsupdate insists on having both */
2438
				$fd = fopen("{$g['varetc_path']}/K{$i}{$keyname}+157+00000.private", "w");
2439
				$privkey = <<<EOD
2440
Private-key-format: v1.2
2441
Algorithm: 157 (HMAC)
2442
Key: {$dnsupdate['keydata']}
2443

    
2444
EOD;
2445
				fwrite($fd, $privkey);
2446
				fclose($fd);
2447

    
2448
				/* write public key file */
2449
				if ($dnsupdate['keytype'] == "zone") {
2450
					$flags = 257;
2451
					$proto = 3;
2452
				} else if ($dnsupdate['keytype'] == "host") {
2453
					$flags = 513;
2454
					$proto = 3;
2455
				} else if ($dnsupdate['keytype'] == "user") {
2456
					$flags = 0;
2457
					$proto = 2;
2458
				}
2459

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

    
2464
				/* generate update instructions */
2465
				$upinst = "";
2466
				if (!empty($dnsupdate['server'])) {
2467
					$upinst .= "server {$dnsupdate['server']}\n";
2468
				}
2469

    
2470
				if (file_exists($cacheFile)) {
2471
					list($cachedipv4, $cacheTimev4) = explode("|", file_get_contents($cacheFile));
2472
				}
2473
				if (file_exists("{$cacheFile}.ipv6")) {
2474
					list($cachedipv6, $cacheTimev6) = explode("|", file_get_contents("{$cacheFile}.ipv6"));
2475
				}
2476

    
2477
				// 25 Days
2478
				$maxCacheAgeSecs = 25 * 24 * 60 * 60;
2479
				$need_update = false;
2480

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

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

    
2515
				$upinst .= "\n";	/* mind that trailing newline! */
2516

    
2517
				if ($need_update) {
2518
					@file_put_contents("{$g['varetc_path']}/nsupdatecmds{$i}", $upinst);
2519
					unset($upinst);
2520
					/* invoke nsupdate */
2521
					$cmd = "/usr/local/bin/nsupdate -k {$g['varetc_path']}/K{$i}{$keyname}+157+00000.key";
2522
					if (isset($dnsupdate['usetcp'])) {
2523
						$cmd .= " -v";
2524
					}
2525
					$cmd .= " {$g['varetc_path']}/nsupdatecmds{$i}";
2526
					mwexec_bg($cmd);
2527
					unset($cmd);
2528
				}
2529
			}
2530
		}
2531
		if (!empty($notify_text)) {
2532
			notify_all_remote($notify_text);
2533
		}
2534
	}
2535

    
2536
	return 0;
2537
}
2538

    
2539
/* configure cron service */
2540
function configure_cron() {
2541
	global $g, $config;
2542

    
2543
	conf_mount_rw();
2544
	/* preserve existing crontab entries */
2545
	$crontab_contents = file("/etc/crontab", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2546

    
2547
	for ($i = 0; $i < count($crontab_contents); $i++) {
2548
		$cron_item =& $crontab_contents[$i];
2549
		if (strpos($cron_item, "# pfSense specific crontab entries") !== false) {
2550
			array_splice($crontab_contents, $i - 1);
2551
			break;
2552
		}
2553
	}
2554
	$crontab_contents = implode("\n", $crontab_contents) . "\n";
2555

    
2556

    
2557
	if (is_array($config['cron']['item'])) {
2558
		$crontab_contents .= "#\n";
2559
		$crontab_contents .= "# " . gettext("pfSense specific crontab entries") . "\n";
2560
		$crontab_contents .= "# " .gettext("Created:") . " " . date("F j, Y, g:i a") . "\n";
2561
		$crontab_contents .= "#\n";
2562

    
2563
		if (isset($config['system']['proxyurl']) && !empty($config['system']['proxyurl'])) {
2564
			$http_proxy = $config['system']['proxyurl'];
2565
			if (isset($config['system']['proxyport']) && !empty($config['system']['proxyport'])) {
2566
				$http_proxy .= ':' . $config['system']['proxyport'];
2567
			}
2568
			$crontab_contents .= "HTTP_PROXY={$http_proxy}";
2569
		}
2570

    
2571
		foreach ($config['cron']['item'] as $item) {
2572
			$crontab_contents .= "\n{$item['minute']}\t";
2573
			$crontab_contents .= "{$item['hour']}\t";
2574
			$crontab_contents .= "{$item['mday']}\t";
2575
			$crontab_contents .= "{$item['month']}\t";
2576
			$crontab_contents .= "{$item['wday']}\t";
2577
			$crontab_contents .= "{$item['who']}\t";
2578
			$crontab_contents .= "{$item['command']}";
2579
		}
2580

    
2581
		$crontab_contents .= "\n#\n";
2582
		$crontab_contents .= "# " . gettext("If possible do not add items to this file manually.") . "\n";
2583
		$crontab_contents .= "# " . gettext("If you do so, this file must be terminated with a blank line (e.g. new line)") . "\n";
2584
		$crontab_contents .= "#\n\n";
2585
	}
2586

    
2587
	/* please maintain the newline at the end of file */
2588
	file_put_contents("/etc/crontab", $crontab_contents);
2589
	unset($crontab_contents);
2590

    
2591
	/* make sure that cron is running and start it if it got killed somehow */
2592
	if (!is_process_running("cron")) {
2593
		exec("cd /tmp && /usr/sbin/cron -s 2>/dev/null");
2594
	} else {
2595
	/* do a HUP kill to force sync changes */
2596
		sigkillbypid("{$g['varrun_path']}/cron.pid", "HUP");
2597
	}
2598

    
2599
	conf_mount_ro();
2600
}
2601

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

    
2626
function upnp_start() {
2627
	global $config;
2628

    
2629
	if (!isset($config['installedpackages']['miniupnpd']['config'])) {
2630
		return;
2631
	}
2632

    
2633
	if ($config['installedpackages']['miniupnpd']['config'][0]['enable']) {
2634
		echo gettext("Starting UPnP service... ");
2635
		require_once('/usr/local/pkg/miniupnpd.inc');
2636
		sync_package_miniupnpd();
2637
		echo "done.\n";
2638
	}
2639
}
2640

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

    
2644
	$is_installed = false;
2645
	$cron_changed = true;
2646

    
2647
	if (!is_array($config['cron'])) {
2648
		$config['cron'] = array();
2649
	}
2650
	if (!is_array($config['cron']['item'])) {
2651
		$config['cron']['item'] = array();
2652
	}
2653

    
2654
	$x = 0;
2655
	foreach ($config['cron']['item'] as $item) {
2656
		if (strstr($item['command'], $command)) {
2657
			$is_installed = true;
2658
			break;
2659
		}
2660
		$x++;
2661
	}
2662

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

    
2691
	if ($cron_changed) {
2692
		configure_cron();
2693
	}
2694
}
2695

    
2696
?>
(49-49/65)