Project

General

Profile

Download (84.5 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
	$carplist = get_configured_carp_interface_list();
77

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

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

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

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

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

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

    
109
		/* always start with the real parent, we override with the carp if later */
110
		$carpif = false;
111
		/* check if we need to listen on a CARP interface */
112
		if (!empty($dhcpv6ifconf['rainterface'])) {
113
			if (!empty($carplist[$dhcpv6ifconf['rainterface']])) {
114
				$dhcpv6if = $dhcpv6ifconf['rainterface'];
115
				$carpif = true;
116
			}
117
		}
118

    
119
		if (strstr($dhcpv6if, "_vip")) {
120
			// CARP IP, check if it's enabled and find parent
121
			if (!get_carp_status() || get_carp_interface_status($dhcpv6if) != "MASTER") {
122
				continue;
123
			}
124
			$ifparent = link_carp_interface_to_parent($dhcpv6if);
125
			$realif = convert_friendly_interface_to_real_interface_name($ifparent);
126
		} else {
127
			$realif = get_real_interface($dhcpv6if, "inet6");
128
		}
129

    
130
		if (isset($radvdifs[$realif])) {
131
			continue;
132
		}
133

    
134
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
135
		if (!is_ipaddrv6($ifcfgipv6)) {
136
			continue;
137
		}
138

    
139
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
140
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
141
		$radvdifs[$realif] = $realif;
142

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

    
210
		if (is_numericint($dhcpv6ifconf['ravalidlifetime'])) {
211
		  $radvdconf .= "\t\tAdvValidLifetime {$dhcpv6ifconf['ravalidlifetime']};\n";
212
		}
213

    
214
		if (is_numericint($dhcpv6ifconf['rapreferredlifetime'])) {
215
		  $radvdconf .= "\t\tAdvPreferredLifetime {$dhcpv6ifconf['rapreferredlifetime']};\n";
216
		}
217

    
218
		$radvdconf .= "\t};\n";
219

    
220
		if (is_array($dhcpv6ifconf['subnets']['item'])) {
221
			foreach ($dhcpv6ifconf['subnets']['item'] as $subnet) {
222
				if (is_subnetv6($subnet)) {
223
					$radvdconf .= "\tprefix {$subnet} {\n";
224
					if ($carpif == true) {
225
						$radvdconf .= "\t\tDeprecatePrefix off;\n";
226
					} else {
227
						$radvdconf .= "\t\tDeprecatePrefix on;\n";
228
					}
229
					switch ($dhcpv6ifconf['ramode']) {
230
						case "managed":
231
							$radvdconf .= "\t\tAdvOnLink on;\n";
232
							$radvdconf .= "\t\tAdvAutonomous off;\n";
233
							$radvdconf .= "\t\tAdvRouterAddr on;\n";
234
							break;
235
						case "router":
236
							$radvdconf .= "\t\tAdvOnLink off;\n";
237
							$radvdconf .= "\t\tAdvAutonomous off;\n";
238
							$radvdconf .= "\t\tAdvRouterAddr on;\n";
239
							break;
240
						case "assist":
241
							$radvdconf .= "\t\tAdvOnLink on;\n";
242
							$radvdconf .= "\t\tAdvAutonomous on;\n";
243
							$radvdconf .= "\t\tAdvRouterAddr on;\n";
244
							break;
245
						case "unmanaged":
246
							$radvdconf .= "\t\tAdvOnLink on;\n";
247
							$radvdconf .= "\t\tAdvAutonomous on;\n";
248
							$radvdconf .= "\t\tAdvRouterAddr on;\n";
249
							break;
250
					}
251
					$radvdconf .= "\t};\n";
252
				}
253
			}
254
		}
255
		if ($carpif === true) {
256
			$radvdconf .= "\troute ::/0 {\n";
257
			$radvdconf .= "\t\tRemoveRoute off;\n";
258
			$radvdconf .= "\t};\n";
259
		} else {
260
			$radvdconf .= "\troute ::/0 {\n";
261
			$radvdconf .= "\t\tRemoveRoute on;\n";
262
			$radvdconf .= "\t};\n";
263
		}
264

    
265
		/* add DNS servers */
266
		$dnslist = array();
267
		if (isset($dhcpv6ifconf['rasamednsasdhcp6']) && is_array($dhcpv6ifconf['dnsserver']) && !empty($dhcpv6ifconf['dnsserver'])) {
268
			foreach ($dhcpv6ifconf['dnsserver'] as $server) {
269
				if (is_ipaddrv6($server)) {
270
					$dnslist[] = $server;
271
				}
272
			}
273
		} elseif (!isset($dhcpv6ifconf['rasamednsasdhcp6']) && isset($dhcpv6ifconf['radnsserver']) && is_array($dhcpv6ifconf['radnsserver'])) {
274
			foreach ($dhcpv6ifconf['radnsserver'] as $server) {
275
				if (is_ipaddrv6($server)) {
276
					$dnslist[] = $server;
277
				}
278
			}
279
		} elseif (isset($config['dnsmasq']['enable']) || isset($config['unbound']['enable'])) {
280
			$dnslist[] = get_interface_ipv6($realif);
281
		} elseif (is_array($config['system']['dnsserver']) && !empty($config['system']['dnsserver'])) {
282
			foreach ($config['system']['dnsserver'] as $server) {
283
				if (is_ipaddrv6($server)) {
284
					$dnslist[] = $server;
285
				}
286
			}
287
		}
288
		if (count($dnslist) > 0) {
289
			$dnsstring = implode(" ", $dnslist);
290
			if ($dnsstring <> "") {
291
				$radvdconf .= "\tRDNSS {$dnsstring} { };\n";
292
			}
293
		}
294
		if (!empty($dhcpv6ifconf['domain'])) {
295
			$radvdconf .= "\tDNSSL {$dhcpv6ifconf['domain']} { };\n";
296
		} elseif (!empty($config['system']['domain'])) {
297
			$radvdconf .= "\tDNSSL {$config['system']['domain']} { };\n";
298
		}
299
		$radvdconf .= "};\n";
300
	}
301

    
302
	/* handle DHCP-PD prefixes and 6RD dynamic interfaces */
303
	foreach ($Iflist as $if => $ifdescr) {
304
		if (!isset($config['interfaces'][$if]['track6-interface']) ||
305
		    !isset($config['interfaces'][$if]['ipaddrv6']) ||
306
		    $config['interfaces'][$if]['ipaddrv6'] != 'track6') {
307
			continue;
308
		}
309
		if (!isset($config['interfaces'][$if]['enable'])) {
310
			continue;
311
		}
312
		/* Do not put in the config an interface which is down */
313
		if (isset($blacklist[$if])) {
314
			continue;
315
		}
316
		$trackif = $config['interfaces'][$if]['track6-interface'];
317
		if (empty($config['interfaces'][$trackif])) {
318
			continue;
319
		}
320

    
321
		if (strstr($if, "_vip")) {
322
			// CARP IP, find parent
323
			$ifparent = link_carp_interface_to_parent($if);
324
			$realif = convert_friendly_interface_to_real_interface_name($ifparent);
325
		} else {
326
			$realif = get_real_interface($if, "inet6");
327
		}
328

    
329
		/* prevent duplicate entries, manual overrides */
330
		if (isset($radvdifs[$realif])) {
331
			continue;
332
		}
333

    
334
		$ifcfgipv6 = get_interface_ipv6($if);
335
		if (!is_ipaddrv6($ifcfgipv6)) {
336
			$subnetv6 = "::";
337
			$ifcfgsnv6 = "64";
338
		} else {
339
			$ifcfgsnv6 = get_interface_subnetv6($if);
340
			$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
341
		}
342
		$radvdifs[$realif] = $realif;
343

    
344
		$autotype = $config['interfaces'][$trackif]['ipaddrv6'];
345

    
346
		if ($g['debug']) {
347
			log_error("configuring RA on {$if} for type {$autotype} radvd subnet {$subnetv6}/{$ifcfgsnv6}");
348
		}
349

    
350
		$radvdconf .= "# Generated config for {$autotype} delegation from {$trackif} on {$if}\n";
351
		$radvdconf .= "interface {$realif} {\n";
352
		$radvdconf .= "\tAdvSendAdvert on;\n";
353
		$radvdconf .= "\tMinRtrAdvInterval 3;\n";
354
		$radvdconf .= "\tMaxRtrAdvInterval 10;\n";
355
		$mtu = get_interface_mtu($realif);
356
		if (is_numeric($mtu)) {
357
			$radvdconf .= "\tAdvLinkMTU {$mtu};\n";
358
		} else {
359
			$radvdconf .= "\tAdvLinkMTU 1280;\n";
360
		}
361
		$radvdconf .= "\tAdvOtherConfigFlag on;\n";
362
		$radvdconf .= "\t\tprefix {$subnetv6}/{$ifcfgsnv6} {\n";
363
		$radvdconf .= "\t\tAdvOnLink on;\n";
364
		$radvdconf .= "\t\tAdvAutonomous on;\n";
365
		$radvdconf .= "\t\tAdvRouterAddr on;\n";
366
		$radvdconf .= "\t};\n";
367

    
368
		/* add DNS servers */
369
		$dnslist = array();
370
		if (isset($config['dnsmasq']['enable']) || isset($config['unbound']['enable'])) {
371
			$dnslist[] = $ifcfgipv6;
372
		} elseif (is_array($config['system']['dnsserver']) && !empty($config['system']['dnsserver'])) {
373
			foreach ($config['system']['dnsserver'] as $server) {
374
				if (is_ipaddrv6($server)) {
375
					$dnslist[] = $server;
376
				}
377
			}
378
		}
379
		if (count($dnslist) > 0) {
380
			$dnsstring = implode(" ", $dnslist);
381
			if (!empty($dnsstring)) {
382
				$radvdconf .= "\tRDNSS {$dnsstring} { };\n";
383
			}
384
		}
385
		if (!empty($config['system']['domain'])) {
386
			$radvdconf .= "\tDNSSL {$config['system']['domain']} { };\n";
387
		}
388
		$radvdconf .= "};\n";
389
	}
390

    
391
	/* write radvd.conf */
392
	if (!@file_put_contents("{$g['varetc_path']}/radvd.conf", $radvdconf)) {
393
		log_error("Error: cannot open radvd.conf in services_radvd_configure().\n");
394
		if (platform_booting()) {
395
			printf("Error: cannot open radvd.conf in services_radvd_configure().\n");
396
		}
397
	}
398
	unset($radvdconf);
399

    
400
	if (count($radvdifs) > 0) {
401
		if (isvalidpid("{$g['varrun_path']}/radvd.pid")) {
402
			sigkillbypid("{$g['varrun_path']}/radvd.pid", "HUP");
403
		} else {
404
			mwexec("/usr/local/sbin/radvd -p {$g['varrun_path']}/radvd.pid -C {$g['varetc_path']}/radvd.conf -m syslog");
405
		}
406
	} else {
407
		/* we need to shut down the radvd cleanly, it will send out the prefix
408
		 * information with a lifetime of 0 to notify clients of a (possible) new prefix */
409
		if (isvalidpid("{$g['varrun_path']}/radvd.pid")) {
410
			log_error("Shutting down Router Advertisment daemon cleanly");
411
			killbypid("{$g['varrun_path']}/radvd.pid");
412
			@unlink("{$g['varrun_path']}/radvd.pid");
413
		}
414
	}
415
	return 0;
416
}
417

    
418
function services_dhcpd_configure($family = "all", $blacklist = array()) {
419
	global $config, $g;
420

    
421
	/* configure DHCPD chroot once */
422
	$fd = fopen("{$g['tmp_path']}/dhcpd.sh", "w");
423
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}\n");
424
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/dev\n");
425
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/etc\n");
426
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/usr/local/sbin\n");
427
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/var/db\n");
428
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/var/run\n");
429
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/usr\n");
430
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/lib\n");
431
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/run\n");
432
	fwrite($fd, "/usr/sbin/chown -R dhcpd:_dhcp {$g['dhcpd_chroot_path']}/*\n");
433
	fwrite($fd, "/bin/cp -n /lib/libc.so.* {$g['dhcpd_chroot_path']}/lib/\n");
434
	fwrite($fd, "/bin/cp -n /usr/local/sbin/dhcpd {$g['dhcpd_chroot_path']}/usr/local/sbin/\n");
435
	fwrite($fd, "/bin/chmod a+rx {$g['dhcpd_chroot_path']}/usr/local/sbin/dhcpd\n");
436

    
437
	$status = `/sbin/mount | /usr/bin/grep -v grep | /usr/bin/grep "{$g['dhcpd_chroot_path']}/dev"`;
438
	if (!trim($status)) {
439
		fwrite($fd, "/sbin/mount -t devfs devfs {$g['dhcpd_chroot_path']}/dev\n");
440
	}
441
	fclose($fd);
442
	mwexec("/bin/sh {$g['tmp_path']}/dhcpd.sh");
443

    
444
	if ($family == "all" || $family == "inet") {
445
		services_dhcpdv4_configure();
446
	}
447
	if ($family == "all" || $family == "inet6") {
448
		services_dhcpdv6_configure($blacklist);
449
		services_radvd_configure($blacklist);
450
	}
451
}
452

    
453
function services_dhcpdv4_configure() {
454
	global $config, $g;
455
	$need_ddns_updates = false;
456
	$ddns_zones = array();
457

    
458
	if ($g['services_dhcp_server_enable'] == false) {
459
		return;
460
	}
461

    
462
	if (isset($config['system']['developerspew'])) {
463
		$mt = microtime();
464
		echo "services_dhcpdv4_configure($if) being called $mt\n";
465
	}
466

    
467
	/* kill any running dhcpd */
468
	if (isvalidpid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid")) {
469
		killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid");
470
	}
471

    
472
	/* DHCP enabled on any interfaces? */
473
	if (!is_dhcp_server_enabled()) {
474
		return 0;
475
	}
476

    
477
	/* if OLSRD is enabled, allow WAN to house DHCP. */
478
	if (!function_exists('is_package_installed')) {
479
		require_once('pkg-utils.inc');
480
	}
481
	if (is_package_installed('olsrd') && isset($config['installedpackages']['olsrd'])) {
482
		foreach ($config['installedpackages']['olsrd']['config'] as $olsrd) {
483
			if (isset($olsrd['enable']) && $olsrd['enable'] == "on") {
484
				$is_olsr_enabled = true;
485
				break;
486
			}
487
		}
488
	}
489

    
490
	if (platform_booting()) {
491
		/* restore the leases, if we have them */
492
		if (file_exists("{$g['cf_conf_path']}/dhcpleases.tgz")) {
493
			$dhcprestore = "";
494
			$dhcpreturn = "";
495
			exec("cd /;LANG=C /usr/bin/tar -xzf {$g['cf_conf_path']}/dhcpleases.tgz 2>&1", $dhcprestore, $dhcpreturn);
496
			$dhcprestore = implode(" ", $dhcprestore);
497
			if ($dhcpreturn <> 0) {
498
				log_error(sprintf(gettext('DHCP leases restore failed exited with %1$s, the error is: %2$s%3$s'), $dhcpreturn, $dhcprestore, "\n"));
499
			}
500
		}
501
		/* 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. */
502
		if (($g['platform'] == $g['product_name']) && !isset($config['system']['use_mfs_tmpvar'])) {
503
			unlink_if_exists("{$g['cf_conf_path']}/dhcpleases.tgz");
504
		}
505
	}
506

    
507
	$syscfg = $config['system'];
508
	if (!is_array($config['dhcpd'])) {
509
		$config['dhcpd'] = array();
510
	}
511
	$dhcpdcfg = $config['dhcpd'];
512
	$Iflist = get_configured_interface_list();
513

    
514
	/* Only consider DNS servers with IPv4 addresses for the IPv4 DHCP server. */
515
	$dns_arrv4 = array();
516
	if (is_array($syscfg['dnsserver'])) {
517
		foreach ($syscfg['dnsserver'] as $dnsserver) {
518
			if (is_ipaddrv4($dnsserver)) {
519
				$dns_arrv4[] = $dnsserver;
520
			}
521
		}
522
	}
523

    
524
	if (platform_booting()) {
525
		echo gettext("Starting DHCP service...");
526
	} else {
527
		sleep(1);
528
	}
529

    
530
	$custoptions = "";
531
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
532
		if (is_array($dhcpifconf['numberoptions']) && is_array($dhcpifconf['numberoptions']['item'])) {
533
			foreach ($dhcpifconf['numberoptions']['item'] as $itemidx => $item) {
534
				if (!empty($item['type'])) {
535
					$itemtype = $item['type'];
536
				} else {
537
					$itemtype = "text";
538
				}
539
				$custoptions .= "option custom-{$dhcpif}-{$itemidx} code {$item['number']} = {$itemtype};\n";
540
			}
541
		}
542
	}
543

    
544
	$dhcpdconf = <<<EOD
545

    
546
option domain-name "{$syscfg['domain']}";
547
option ldap-server code 95 = text;
548
option domain-search-list code 119 = text;
549
option arch code 93 = unsigned integer 16; # RFC4578
550
{$custoptions}
551
default-lease-time 7200;
552
max-lease-time 86400;
553
log-facility local7;
554
one-lease-per-client true;
555
deny duplicates;
556
ping-check true;
557
update-conflict-detection false;
558

    
559
EOD;
560

    
561
	if (!isset($dhcpifconf['disableauthoritative'])) {
562
		$dhcpdconf .= "authoritative;\n";
563
	}
564

    
565
	if (isset($dhcpifconf['alwaysbroadcast'])) {
566
		$dhcpdconf .= "always-broadcast on\n";
567
	}
568

    
569
	$dhcpdifs = array();
570
	$enable_add_routers = false;
571
	$gateways_arr = return_gateways_array();
572
	/* only add a routers line if the system has any IPv4 gateway at all */
573
	/* a static route has a gateway, manually overriding this field always works */
574
	foreach ($gateways_arr as $gwitem) {
575
		if ($gwitem['ipprotocol'] == "inet") {
576
			$enable_add_routers = true;
577
			break;
578
		}
579
	}
580

    
581
	/*    loop through and determine if we need to setup
582
	 *    failover peer "bleh" entries
583
	 */
584
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
585

    
586
		if (!isset($config['interfaces'][$dhcpif]['enable'])) {
587
			continue;
588
		}
589

    
590
		interfaces_staticarp_configure($dhcpif);
591

    
592
		if (!isset($dhcpifconf['enable'])) {
593
			continue;
594
		}
595

    
596
		if ($dhcpifconf['failover_peerip'] <> "") {
597
			$intip = get_interface_ip($dhcpif);
598
			/*
599
			 *    yep, failover peer is defined.
600
			 *    does it match up to a defined vip?
601
			 */
602
			$skew = 110;
603
			if (is_array($config['virtualip']['vip'])) {
604
				foreach ($config['virtualip']['vip'] as $vipent) {
605
					if ($vipent['interface'] == $dhcpif) {
606
						$carp_nw = gen_subnet($vipent['subnet'], $vipent['subnet_bits']);
607
						if (ip_in_subnet($dhcpifconf['failover_peerip'], "{$carp_nw}/{$vipent['subnet_bits']}")) {
608
							/* this is the interface! */
609
							if (is_numeric($vipent['advskew']) && (intval($vipent['advskew']) < 20)) {
610
								$skew = 0;
611
								break;
612
							}
613
						}
614
					}
615
				}
616
			} else {
617
				log_error(gettext("Warning!  DHCP Failover setup and no CARP virtual IPs defined!"));
618
			}
619
			if ($skew > 10) {
620
				$type = "secondary";
621
				$my_port = "520";
622
				$peer_port = "519";
623
			} else {
624
				$my_port = "519";
625
				$peer_port = "520";
626
				$type = "primary";
627
				$dhcpdconf_pri = "split 128;\n";
628
				$dhcpdconf_pri .= "  mclt 600;\n";
629
			}
630

    
631
			if (is_ipaddrv4($intip)) {
632
				$dhcpdconf .= <<<EOPP
633
failover peer "dhcp_{$dhcpif}" {
634
  {$type};
635
  address {$intip};
636
  port {$my_port};
637
  peer address {$dhcpifconf['failover_peerip']};
638
  peer port {$peer_port};
639
  max-response-delay 10;
640
  max-unacked-updates 10;
641
  {$dhcpdconf_pri}
642
  load balance max seconds 3;
643
}
644
\n
645
EOPP;
646
			}
647
		}
648
	}
649

    
650
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
651

    
652
		$newzone = array();
653
		$ifcfg = $config['interfaces'][$dhcpif];
654

    
655
		if (!isset($dhcpifconf['enable']) || !isset($Iflist[$dhcpif])) {
656
			continue;
657
		}
658
		$ifcfgip = get_interface_ip($dhcpif);
659
		$ifcfgsn = get_interface_subnet($dhcpif);
660
		$subnet = gen_subnet($ifcfgip, $ifcfgsn);
661
		$subnetmask = gen_subnet_mask($ifcfgsn);
662

    
663
		if (!is_ipaddr($subnet)) {
664
			continue;
665
		}
666

    
667
		if ($is_olsr_enabled == true) {
668
			if ($dhcpifconf['netmask']) {
669
				$subnetmask = gen_subnet_mask($dhcpifconf['netmask']);
670
			}
671
		}
672

    
673
		$all_pools = array();
674
		$all_pools[] = $dhcpifconf;
675
		if (is_array($dhcpifconf['pool'])) {
676
			$all_pools = array_merge($all_pools, $dhcpifconf['pool']);
677
		}
678

    
679
		$dnscfg = "";
680

    
681
		if ($dhcpifconf['domain']) {
682
			$dnscfg .= "	option domain-name \"{$dhcpifconf['domain']}\";\n";
683
		}
684

    
685
		if ($dhcpifconf['domainsearchlist'] <> "") {
686
			$dnscfg .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpifconf['domainsearchlist'])) . "\";\n";
687
		}
688

    
689
		if (isset($dhcpifconf['ddnsupdate'])) {
690
			$need_ddns_updates = true;
691
			$newzone = array();
692
			if ($dhcpifconf['ddnsdomain'] <> "") {
693
				$newzone['domain-name'] = $dhcpifconf['ddnsdomain'];
694
				$dnscfg .= "	ddns-domainname \"{$dhcpifconf['ddnsdomain']}\";\n";
695
			} else {
696
				$newzone['domain-name'] = $config['system']['domain'];
697
			}
698

    
699
			$revsubnet = array_reverse(explode('.',$subnet));
700

    
701
			/* Take care of full classes first */
702
			switch ($ifcfgsn) {
703
				case 8:
704
					$start_octet = 3;
705
					break;
706
				case 16:
707
					$start_octet = 2;
708
					break;
709
				case 24:
710
					$start_octet = 1;
711
					break;
712
				default:
713
					$start_octet = 0;
714
					/* Add subnet bitmask to first octet */
715
					$revsubnet[0] .= '-' . $ifcfgsn;
716
					break;
717

    
718
			}
719

    
720
			$ptr_domain = '';
721
			for ($octet = 0; $octet <= 3; $octet++) {
722
				if ($octet < $start_octet) {
723
					continue;
724
				}
725
				$ptr_domain .= (empty($ptr_domain) ? '' : '.');
726
				$ptr_domain .= $revsubnet[$octet];
727
			}
728
			$ptr_domain .= ".in-addr.arpa";
729
			$newzone['ptr-domain'] = $ptr_domain;
730
			unset($ptr_domain, $revsubnet, $start_octet);
731
		}
732

    
733
		if (is_array($dhcpifconf['dnsserver']) && ($dhcpifconf['dnsserver'][0])) {
734
			$dnscfg .= "	option domain-name-servers " . join(",", $dhcpifconf['dnsserver']) . ";";
735
			if ($newzone['domain-name']) {
736
				$newzone['dns-servers'] = $dhcpifconf['dnsserver'];
737
			}
738
		} else if (isset($config['dnsmasq']['enable'])) {
739
			$dnscfg .= "	option domain-name-servers {$ifcfgip};";
740
			if ($newzone['domain-name'] && is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
741
				$newzone['dns-servers'] = $syscfg['dnsserver'];
742
			}
743
		} else if (isset($config['unbound']['enable'])) {
744
			$dnscfg .= "	option domain-name-servers {$ifcfgip};";
745
		} else if (!empty($dns_arrv4)) {
746
			$dnscfg .= "	option domain-name-servers " . join(",", $dns_arrv4) . ";";
747
			if ($newzone['domain-name']) {
748
				$newzone['dns-servers'] = $dns_arrv4;
749
			}
750
		}
751

    
752
		/* Create classes - These all contain comma separated lists. Join them into one
753
		   big comma separated string then split them all up. */
754
		$all_mac_strings = array();
755
		if (is_array($dhcpifconf['pool'])) {
756
			foreach ($all_pools as $poolconf) {
757
				$all_mac_strings[] = $poolconf['mac_allow'];
758
				$all_mac_strings[] = $poolconf['mac_deny'];
759
			}
760
		}
761
		$all_mac_strings[] = $dhcpifconf['mac_allow'];
762
		$all_mac_strings[] = $dhcpifconf['mac_deny'];
763
		if (!empty($all_mac_strings)) {
764
			$all_mac_list = array_unique(explode(',', implode(',', $all_mac_strings)));
765
			foreach ($all_mac_list as $mac) {
766
				if (empty($mac)) {
767
					continue;
768
				}
769
				$dhcpdconf .= 'class "' . str_replace(':', '', $mac) . '" {' . "\n";
770
				// Skip the first octet of the MAC address - for media type, typically Ethernet ("01") and match the rest.
771
				$dhcpdconf .= '	match if substring (hardware, 1, ' . (substr_count($mac, ':') + 1) . ') = ' . $mac . ';' . "\n";
772
				$dhcpdconf .= '}' . "\n";
773
			}
774
		}
775

    
776
		$dhcpdconf .= "subnet {$subnet} netmask {$subnetmask} {\n";
777

    
778
		// Setup pool options
779
		foreach ($all_pools as $poolconf) {
780
			if (!(ip_in_subnet($poolconf['range']['from'], "{$subnet}/{$ifcfgsn}") && ip_in_subnet($poolconf['range']['to'], "{$subnet}/{$ifcfgsn}"))) {
781
				// If the user has changed the subnet from the interfaces page and applied,
782
				// but has not updated the DHCP range, then the range to/from of the pool can be outside the subnet.
783
				// This can also happen when implementing the batch of changes when the setup wizard reloads the new settings.
784
				$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);
785
				$do_file_notice = true;
786
				$conf_ipv4_address = $ifcfg['ipaddr'];
787
				$conf_ipv4_subnetmask = $ifcfg['subnet'];
788
				if (is_ipaddrv4($conf_ipv4_address) && is_subnet("{$conf_ipv4_address}/{$conf_ipv4_subnetmask}")) {
789
					$conf_subnet_base = gen_subnet($conf_ipv4_address, $conf_ipv4_subnetmask);
790
					if (ip_in_subnet($poolconf['range']['from'], "{$conf_subnet_base}/{$conf_ipv4_subnetmask}") &&
791
					    ip_in_subnet($poolconf['range']['to'], "{$conf_subnet_base}/{$conf_ipv4_subnetmask}")) {
792
						// Even though the running interface subnet does not match the pool range,
793
						// the interface subnet in the config file contains the pool range.
794
						// We are somewhere part-way through a settings reload, e.g. after running the setup wizard.
795
						// services_dhcpdv4_configure will be called again later when the new interface settings from
796
						// the config are applied and at that time everything will match up.
797
						// Ignore this pool on this interface for now and just log the error to the system log.
798
						log_error($error_msg);
799
						$do_file_notice = false;
800
					}
801
				}
802
				if ($do_file_notice) {
803
					file_notice("DHCP", $error_msg);
804
				}
805
				continue;
806
			}
807
			$dhcpdconf .= "	pool {\n";
808
			/* is failover dns setup? */
809
			if (is_array($poolconf['dnsserver']) && $poolconf['dnsserver'][0] <> "") {
810
				$dhcpdconf .= "		option domain-name-servers {$poolconf['dnsserver'][0]}";
811
				if ($poolconf['dnsserver'][1] <> "") {
812
					$dhcpdconf .= ",{$poolconf['dnsserver'][1]}";
813
				}
814
				if ($poolconf['dnsserver'][2] <> "") {
815
					$dhcpdconf .= ",{$poolconf['dnsserver'][2]}";
816
				}
817
				if ($poolconf['dnsserver'][3] <> "") {
818
					$dhcpdconf .= ",{$poolconf['dnsserver'][3]}";
819
				}
820
				$dhcpdconf .= ";\n";
821
			}
822

    
823
			/* allow/deny MACs */
824
			$mac_allow_list = array_unique(explode(',', $poolconf['mac_allow']));
825
			foreach ($mac_allow_list as $mac) {
826
				if (empty($mac)) {
827
					continue;
828
				}
829
				$dhcpdconf .= "		allow members of \"" . str_replace(':', '', $mac) . "\";\n";
830
			}
831
			$deny_action = "deny";
832
			if (isset($poolconf['nonak'])) {
833
				$deny_action = "ignore";
834
			}
835
			$mac_deny_list = array_unique(explode(',', $poolconf['mac_deny']));
836
			foreach ($mac_deny_list as $mac) {
837
				if (empty($mac)) {
838
					continue;
839
				}
840
				$dhcpdconf .= "		$deny_action members of \"" . str_replace(':', '', $mac) . "\";\n";
841
			}
842

    
843
			if ($poolconf['failover_peerip'] <> "") {
844
				$dhcpdconf .= "		$deny_action dynamic bootp clients;\n";
845
			}
846

    
847
			if (isset($poolconf['denyunknown'])) {
848
				$dhcpdconf .= "		$deny_action unknown-clients;\n";
849
			}
850

    
851
			if ($poolconf['gateway'] && $poolconf['gateway'] != "none" && ($poolconf['gateway'] != $dhcpifconf['gateway'])) {
852
				$dhcpdconf .= "		option routers {$poolconf['gateway']};\n";
853
			}
854

    
855
			if ($dhcpifconf['failover_peerip'] <> "") {
856
				$dhcpdconf .= "		failover peer \"dhcp_{$dhcpif}\";\n";
857
			}
858

    
859
			$pdnscfg = "";
860

    
861
			if ($poolconf['domain'] && ($poolconf['domain'] != $dhcpifconf['domain'])) {
862
				$pdnscfg .= "		option domain-name \"{$poolconf['domain']}\";\n";
863
			}
864

    
865
			if (!empty($poolconf['domainsearchlist']) && ($poolconf['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
866
				$pdnscfg .= "		option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $poolconf['domainsearchlist'])) . "\";\n";
867
			}
868

    
869
			if (isset($poolconf['ddnsupdate'])) {
870
				if (($poolconf['ddnsdomain'] <> "") && ($poolconf['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
871
					$pdnscfg .= "		ddns-domainname \"{$poolconf['ddnsdomain']}\";\n";
872
				}
873
				$pdnscfg .= "		ddns-update-style interim;\n";
874
			}
875

    
876
			if (is_array($poolconf['dnsserver']) && ($poolconf['dnsserver'][0]) && ($poolconf['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
877
				$pdnscfg .= "		option domain-name-servers " . join(",", $poolconf['dnsserver']) . ";\n";
878
			}
879
			$dhcpdconf .= "{$pdnscfg}";
880

    
881
			// default-lease-time
882
			if ($poolconf['defaultleasetime'] && ($poolconf['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) {
883
				$dhcpdconf .= "		default-lease-time {$poolconf['defaultleasetime']};\n";
884
			}
885

    
886
			// max-lease-time
887
			if ($poolconf['maxleasetime'] && ($poolconf['maxleasetime'] != $dhcpifconf['maxleasetime'])) {
888
				$dhcpdconf .= "		max-lease-time {$poolconf['maxleasetime']};\n";
889
			}
890

    
891
			// netbios-name*
892
			if (is_array($poolconf['winsserver']) && $poolconf['winsserver'][0] && ($poolconf['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
893
				$dhcpdconf .= "		option netbios-name-servers " . join(",", $poolconf['winsserver']) . ";\n";
894
				$dhcpdconf .= "		option netbios-node-type 8;\n";
895
			}
896

    
897
			// ntp-servers
898
			if (is_array($poolconf['ntpserver']) && $poolconf['ntpserver'][0] && ($poolconf['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
899
				$dhcpdconf .= "		option ntp-servers " . join(",", $poolconf['ntpserver']) . ";\n";
900
			}
901

    
902
			// tftp-server-name
903
			if (!empty($poolconf['tftp']) && ($poolconf['tftp'] != $dhcpifconf['tftp'])) {
904
				$dhcpdconf .= "		option tftp-server-name \"{$poolconf['tftp']}\";\n";
905
			}
906

    
907
			// ldap-server
908
			if (!empty($poolconf['ldap']) && ($poolconf['ldap'] != $dhcpifconf['ldap'])) {
909
				$dhcpdconf .= "		option ldap-server \"{$poolconf['ldap']}\";\n";
910
			}
911

    
912
			// net boot information
913
			if (isset($poolconf['netboot'])) {
914
				if (!empty($poolconf['nextserver']) && ($poolconf['nextserver'] != $dhcpifconf['nextserver'])) {
915
					$dhcpdconf .= "		next-server {$poolconf['nextserver']};\n";
916
				}
917
				if (!empty($poolconf['filename']) && ($poolconf['filename'] != $dhcpifconf['filename'])) {
918
					$dhcpdconf .= "		filename \"{$poolconf['filename']}\";\n";
919
				}
920
				if (!empty($poolconf['rootpath']) && ($poolconf['rootpath'] != $dhcpifconf['rootpath'])) {
921
					$dhcpdconf .= "		option root-path \"{$poolconf['rootpath']}\";\n";
922
				}
923
			}
924
			$dhcpdconf .= "		range {$poolconf['range']['from']} {$poolconf['range']['to']};\n";
925
			$dhcpdconf .= "	}\n\n";
926
		}
927
// End of settings inside pools
928

    
929
		if ($dhcpifconf['gateway'] && $dhcpifconf['gateway'] != "none") {
930
			$routers = $dhcpifconf['gateway'];
931
			$add_routers = true;
932
		} elseif ($dhcpifconf['gateway'] == "none") {
933
			$add_routers = false;
934
		} else {
935
			$add_routers = $enable_add_routers;
936
			$routers = $ifcfgip;
937
		}
938
		if ($add_routers) {
939
			$dhcpdconf .= "	option routers {$routers};\n";
940
		}
941

    
942
		$dhcpdconf .= <<<EOD
943
$dnscfg
944

    
945
EOD;
946
		// default-lease-time
947
		if ($dhcpifconf['defaultleasetime']) {
948
			$dhcpdconf .= "	default-lease-time {$dhcpifconf['defaultleasetime']};\n";
949
		}
950

    
951
		// max-lease-time
952
		if ($dhcpifconf['maxleasetime']) {
953
			$dhcpdconf .= "	max-lease-time {$dhcpifconf['maxleasetime']};\n";
954
		}
955

    
956
		// netbios-name*
957
		if (is_array($dhcpifconf['winsserver']) && $dhcpifconf['winsserver'][0]) {
958
			$dhcpdconf .= "	option netbios-name-servers " . join(",", $dhcpifconf['winsserver']) . ";\n";
959
			$dhcpdconf .= "	option netbios-node-type 8;\n";
960
		}
961

    
962
		// ntp-servers
963
		if (is_array($dhcpifconf['ntpserver']) && $dhcpifconf['ntpserver'][0]) {
964
			$dhcpdconf .= "	option ntp-servers " . join(",", $dhcpifconf['ntpserver']) . ";\n";
965
		}
966

    
967
		// tftp-server-name
968
		if ($dhcpifconf['tftp'] <> "") {
969
			$dhcpdconf .= "	option tftp-server-name \"{$dhcpifconf['tftp']}\";\n";
970
		}
971

    
972
		// Handle option, number rowhelper values
973
		$dhcpdconf .= "\n";
974
		if ($dhcpifconf['numberoptions']['item']) {
975
			foreach ($dhcpifconf['numberoptions']['item'] as $itemidx => $item) {
976
				$item_value = base64_decode($item['value']);
977
				if (empty($item['type']) || $item['type'] == "text") {
978
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} \"{$item_value}\";\n";
979
				} else {
980
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} {$item_value};\n";
981
				}
982
			}
983
		}
984

    
985
		// ldap-server
986
		if ($dhcpifconf['ldap'] <> "") {
987
			$dhcpdconf .= "	option ldap-server \"{$dhcpifconf['ldap']}\";\n";
988
		}
989

    
990
		// net boot information
991
		if (isset($dhcpifconf['netboot'])) {
992
			if ($dhcpifconf['nextserver'] <> "") {
993
				$dhcpdconf .= "	next-server {$dhcpifconf['nextserver']};\n";
994
			}
995
			if (!empty($dhcpifconf['filename']) && !empty($dhcpifconf['filename32']) && !empty($dhcpifconf['filename64'])) {
996
				$dhcpdconf .= "	if option arch = 00:06 {\n";
997
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename32']}\";\n";
998
				$dhcpdconf .= "	} else if option arch = 00:07 {\n";
999
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename64']}\";\n";
1000
				$dhcpdconf .= "	} else if option arch = 00:09 {\n";
1001
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename64']}\";\n";
1002
				$dhcpdconf .= "	} else {\n";
1003
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename']}\";\n";
1004
				$dhcpdconf .= "	}\n\n";
1005
			} elseif (!empty($dhcpifconf['filename'])) {
1006
				$dhcpdconf .= "	filename \"{$dhcpifconf['filename']}\";\n";
1007
			}
1008
			if (!empty($dhcpifconf['rootpath'])) {
1009
				$dhcpdconf .= "	option root-path \"{$dhcpifconf['rootpath']}\";\n";
1010
			}
1011
		}
1012

    
1013
		$dhcpdconf .= <<<EOD
1014
}
1015

    
1016
EOD;
1017

    
1018
		/* add static mappings */
1019
		if (is_array($dhcpifconf['staticmap'])) {
1020

    
1021
			$i = 0;
1022
			foreach ($dhcpifconf['staticmap'] as $sm) {
1023
				$dhcpdconf .= "host s_{$dhcpif}_{$i} {\n";
1024

    
1025
				if ($sm['mac']) {
1026
					$dhcpdconf .= "        hardware ethernet {$sm['mac']};\n";
1027
				}
1028

    
1029
				if ($sm['cid']) {
1030
					$dhcpdconf .= "        option dhcp-client-identifier \"{$sm['cid']}\";\n";
1031
				}
1032

    
1033
				if ($sm['ipaddr']) {
1034
					$dhcpdconf .= "	fixed-address {$sm['ipaddr']};\n";
1035
				}
1036

    
1037
				if ($sm['hostname']) {
1038
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
1039
					$dhhostname = str_replace(".", "_", $dhhostname);
1040
					$dhcpdconf .= "	option host-name \"{$dhhostname}\";\n";
1041
				}
1042
				if ($sm['filename']) {
1043
					$dhcpdconf .= "	filename \"{$sm['filename']}\";\n";
1044
				}
1045

    
1046
				if ($sm['rootpath']) {
1047
					$dhcpdconf .= "	option root-path \"{$sm['rootpath']}\";\n";
1048
				}
1049

    
1050
				if ($sm['gateway'] && ($sm['gateway'] != $dhcpifconf['gateway'])) {
1051
					$dhcpdconf .= "	option routers {$sm['gateway']};\n";
1052
				}
1053

    
1054
				$smdnscfg = "";
1055

    
1056
				if ($sm['domain'] && ($sm['domain'] != $dhcpifconf['domain'])) {
1057
					$smdnscfg .= "	option domain-name \"{$sm['domain']}\";\n";
1058
				}
1059

    
1060
				if (!empty($sm['domainsearchlist']) && ($sm['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
1061
					$smdnscfg .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $sm['domainsearchlist'])) . "\";\n";
1062
				}
1063

    
1064
				if (isset($sm['ddnsupdate'])) {
1065
					if (($sm['ddnsdomain'] <> "") && ($sm['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
1066
						$pdnscfg .= "		ddns-domainname \"{$sm['ddnsdomain']}\";\n";
1067
					}
1068
					$pdnscfg .= "		ddns-update-style interim;\n";
1069
				}
1070

    
1071
				if (is_array($sm['dnsserver']) && ($sm['dnsserver'][0]) && ($sm['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
1072
					$smdnscfg .= "	option domain-name-servers " . join(",", $sm['dnsserver']) . ";\n";
1073
				}
1074
				$dhcpdconf .= "{$smdnscfg}";
1075

    
1076
				// default-lease-time
1077
				if ($sm['defaultleasetime'] && ($sm['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) {
1078
					$dhcpdconf .= "	default-lease-time {$sm['defaultleasetime']};\n";
1079
				}
1080

    
1081
				// max-lease-time
1082
				if ($sm['maxleasetime'] && ($sm['maxleasetime'] != $dhcpifconf['maxleasetime'])) {
1083
					$dhcpdconf .= "	max-lease-time {$sm['maxleasetime']};\n";
1084
				}
1085

    
1086
				// netbios-name*
1087
				if (is_array($sm['winsserver']) && $sm['winsserver'][0] && ($sm['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
1088
					$dhcpdconf .= "	option netbios-name-servers " . join(",", $sm['winsserver']) . ";\n";
1089
					$dhcpdconf .= "	option netbios-node-type 8;\n";
1090
				}
1091

    
1092
				// ntp-servers
1093
				if (is_array($sm['ntpserver']) && $sm['ntpserver'][0] && ($sm['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
1094
					$dhcpdconf .= "	option ntp-servers " . join(",", $sm['ntpserver']) . ";\n";
1095
				}
1096

    
1097
				// tftp-server-name
1098
				if (!empty($sm['tftp']) && ($sm['tftp'] != $dhcpifconf['tftp'])) {
1099
					$dhcpdconf .= "	option tftp-server-name \"{$sm['tftp']}\";\n";
1100
				}
1101

    
1102
				$dhcpdconf .= "}\n";
1103
				$i++;
1104
			}
1105
		}
1106

    
1107
		$dhcpdifs[] = get_real_interface($dhcpif);
1108
		if ($newzone['domain-name']) {
1109
			if ($need_ddns_updates) {
1110
				$newzone['dns-servers'] = array($dhcpifconf['ddnsdomainprimary']);
1111
				$newzone['ddnsdomainkeyname'] = $dhcpifconf['ddnsdomainkeyname'];
1112
				$newzone['ddnsdomainkey'] = $dhcpifconf['ddnsdomainkey'];
1113
				$dhcpdconf .= dhcpdkey($dhcpifconf);
1114
			}
1115
			$ddns_zones[] = $newzone;
1116
		}
1117
	}
1118

    
1119
	if ($need_ddns_updates) {
1120
		$dhcpdconf .= "ddns-update-style interim;\n";
1121
		$dhcpdconf .= "update-static-leases on;\n";
1122

    
1123
		$dhcpdconf .= dhcpdzones($ddns_zones);
1124
	}
1125

    
1126
	/* write dhcpd.conf */
1127
	if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpd.conf", $dhcpdconf)) {
1128
		printf(gettext("Error: cannot open dhcpd.conf in services_dhcpdv4_configure().%s"), "\n");
1129
		unset($dhcpdconf);
1130
		return 1;
1131
	}
1132
	unset($dhcpdconf);
1133

    
1134
	/* create an empty leases database */
1135
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases")) {
1136
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases");
1137
	}
1138

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

    
1143
	/* fire up dhcpd in a chroot */
1144
	if (count($dhcpdifs) > 0) {
1145
		mwexec("/usr/local/sbin/dhcpd -user dhcpd -group _dhcp -chroot {$g['dhcpd_chroot_path']} -cf /etc/dhcpd.conf -pf {$g['varrun_path']}/dhcpd.pid " .
1146
			join(" ", $dhcpdifs));
1147
	}
1148

    
1149
	if (platform_booting()) {
1150
		print "done.\n";
1151
	}
1152

    
1153
	return 0;
1154
}
1155

    
1156
function dhcpdkey($dhcpifconf) {
1157
	$dhcpdconf = "";
1158
	if ($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "") {
1159
		$dhcpdconf .= "key {$dhcpifconf['ddnsdomainkeyname']} {\n";
1160
		$dhcpdconf .= "	algorithm hmac-md5;\n";
1161
		$dhcpdconf .= "	secret {$dhcpifconf['ddnsdomainkey']};\n";
1162
		$dhcpdconf .= "}\n";
1163
	}
1164

    
1165
	return $dhcpdconf;
1166
}
1167

    
1168
function dhcpdzones($ddns_zones) {
1169
	$dhcpdconf = "";
1170

    
1171
	if (is_array($ddns_zones)) {
1172
		$added_zones = array();
1173
		foreach ($ddns_zones as $zone) {
1174
			if (!is_array($zone) || empty($zone) || !is_array($zone['dns-servers'])) {
1175
				continue;
1176
			}
1177
			$primary = $zone['dns-servers'][0];
1178
			$secondary = empty($zone['dns-servers'][1]) ? "" : $zone['dns-servers'][1];
1179

    
1180
			// Make sure we aren't using any invalid or IPv6 DNS servers.
1181
			if (!is_ipaddrv4($primary)) {
1182
				if (is_ipaddrv4($secondary)) {
1183
					$primary = $secondary;
1184
					$secondary = "";
1185
				} else {
1186
					continue;
1187
				}
1188
			}
1189

    
1190
			// We don't need to add zones multiple times.
1191
			if ($zone['domain-name'] && !in_array($zone['domain-name'], $added_zones)) {
1192
				$dhcpdconf .= "zone {$zone['domain-name']}. {\n";
1193
				$dhcpdconf .= "	primary {$primary};\n";
1194
				if (is_ipaddrv4($secondary)) {
1195
					$dhcpdconf .= "	secondary {$secondary};\n";
1196
				}
1197
				if ($zone['ddnsdomainkeyname'] <> "" && $zone['ddnsdomainkey'] <> "") {
1198
					$dhcpdconf .= "	key {$zone['ddnsdomainkeyname']};\n";
1199
				}
1200
				$dhcpdconf .= "}\n";
1201
				$added_zones[] = $zone['domain-name'];
1202
			}
1203
			if ($zone['ptr-domain'] && !in_array($zone['ptr-domain'], $added_zones)) {
1204
				$dhcpdconf .= "zone {$zone['ptr-domain']} {\n";
1205
				$dhcpdconf .= "	primary {$primary};\n";
1206
				if (is_ipaddrv4($secondary)) {
1207
					$dhcpdconf .= "	secondary {$secondary};\n";
1208
				}
1209
				if ($zone['ddnsdomainkeyname'] <> "" && $zone['ddnsdomainkey'] <> "") {
1210
					$dhcpdconf .= "	key {$zone['ddnsdomainkeyname']};\n";
1211
				}
1212
				$dhcpdconf .= "}\n";
1213
				$added_zones[] = $zone['ptr-domain'];
1214
			}
1215
		}
1216
	}
1217

    
1218
	return $dhcpdconf;
1219
}
1220

    
1221
function services_dhcpdv6_configure($blacklist = array()) {
1222
	global $config, $g;
1223

    
1224
	if ($g['services_dhcp_server_enable'] == false) {
1225
		return;
1226
	}
1227

    
1228
	if (isset($config['system']['developerspew'])) {
1229
		$mt = microtime();
1230
		echo "services_dhcpd_configure($if) being called $mt\n";
1231
	}
1232

    
1233
	/* kill any running dhcpd */
1234
	if (isvalidpid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid")) {
1235
		killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid");
1236
	}
1237
	if (isvalidpid("{$g['varrun_path']}/dhcpleases6.pid")) {
1238
		killbypid("{$g['varrun_path']}/dhcpleases6.pid");
1239
	}
1240

    
1241
	/* DHCP enabled on any interfaces? */
1242
	if (!is_dhcpv6_server_enabled()) {
1243
		return 0;
1244
	}
1245

    
1246
	if (platform_booting()) {
1247
		if ($g['platform'] != $g['product_name']) {
1248
			/* restore the leases, if we have them */
1249
			if (file_exists("{$g['cf_conf_path']}/dhcp6leases.tgz")) {
1250
				$dhcprestore = "";
1251
				$dhcpreturn = "";
1252
				exec("cd /;LANG=C /usr/bin/tar -xzf {$g['cf_conf_path']}/dhcp6leases.tgz 2>&1", $dhcprestore, $dhcpreturn);
1253
				$dhcprestore = implode(" ", $dhcprestore);
1254
				if ($dhcpreturn <> 0) {
1255
					log_error("DHCP leases v6 restore failed exited with $dhcpreturn, the error is: $dhcprestore\n");
1256
				}
1257
			}
1258
		}
1259
	}
1260

    
1261
	$syscfg = $config['system'];
1262
	if (!is_array($config['dhcpdv6'])) {
1263
		$config['dhcpdv6'] = array();
1264
	}
1265
	$dhcpdv6cfg = $config['dhcpdv6'];
1266
	$Iflist = get_configured_interface_list();
1267
	$Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces());
1268

    
1269

    
1270
	if (platform_booting()) {
1271
		echo "Starting DHCPv6 service...";
1272
	} else {
1273
		sleep(1);
1274
	}
1275

    
1276
	/* we add a fake entry for interfaces that are set to track6 another WAN */
1277
	foreach ($Iflist as $ifname) {
1278
		/* Do not put in the config an interface which is down */
1279
		if (isset($blacklist[$ifname])) {
1280
			continue;
1281
		}
1282
		if (!empty($config['interfaces'][$ifname]['track6-interface'])) {
1283
			$realif = get_real_interface($ifname, "inet6");
1284
			$ifcfgipv6 = get_interface_ipv6($ifname);
1285
			if (!is_ipaddrv6($ifcfgipv6)) {
1286
				continue;
1287
			}
1288
			$ifcfgipv6 = Net_IPv6::getNetmask($ifcfgipv6, 64);
1289
			$trackifname = $config['interfaces'][$ifname]['track6-interface'];
1290
			$trackcfg = $config['interfaces'][$trackifname];
1291
			$pdlen = calculate_ipv6_delegation_length($trackifname);
1292
			$ifcfgipv6arr =explode(":", $ifcfgipv6);
1293
			$dhcpdv6cfg[$ifname] = array();
1294
			$dhcpdv6cfg[$ifname]['enable'] = true;
1295
			/* range */
1296
			$ifcfgipv6arr[7] = "1000";
1297
			$dhcpdv6cfg[$ifname]['range'] = array();
1298
			$dhcpdv6cfg[$ifname]['range']['from'] = Net_IPv6::compress(implode(":", $ifcfgipv6arr));
1299
			$ifcfgipv6arr[7] = "2000";
1300
			$dhcpdv6cfg[$ifname]['range']['to'] = Net_IPv6::compress(implode(":", $ifcfgipv6arr));
1301
			/* prefix length > 0? We can add dhcp6 prefix delegation server */
1302
			if ($pdlen > 2) {
1303
				$pdlenmax = $pdlen;
1304
				$pdlenhalf = $pdlenmax -1;
1305
				$pdlenmin = (64 - ceil($pdlenhalf / 4));
1306
				$dhcpdv6cfg[$ifname]['prefixrange'] = array();
1307
				$dhcpdv6cfg[$ifname]['prefixrange']['prefixlength'] = $pdlenmin;
1308

    
1309
				/* set the delegation start to half the current address block */
1310
				$range = Net_IPv6::parseAddress($ifcfgipv6, (64 - $pdlenmax));
1311
				$range['start'] = Net_IPv6::getNetmask($range['end'], (64 - $pdlenhalf));
1312

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

    
1317
				$dhcpdv6cfg[$ifname]['prefixrange']['from'] = Net_IPv6::compress($range['start']);
1318
				$dhcpdv6cfg[$ifname]['prefixrange']['to'] = Net_IPv6::compress($range['end']);
1319
			}
1320
			$dhcpdv6cfg[$ifname]['dns6ip'] = get_interface_ipv6($ifname);
1321
		}
1322
	}
1323

    
1324
	$custoptionsv6 = "";
1325
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1326
		if (is_array($dhcpv6ifconf['numberoptions']) && is_array($dhcpv6ifconf['numberoptions']['item'])) {
1327
			foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
1328
				$custoptionsv6 .= "option custom-{$dhcpv6if}-{$itemv6idx} code {$itemv6['number']} = text;\n";
1329
			}
1330
		}
1331
	}
1332

    
1333
	if (isset($dhcpv6ifconf['netboot']) && !empty($dhcpv6ifconf['bootfile_url'])) {
1334
		$custoptionsv6 .= "option dhcp6.bootfile-url code 59 = string;\n";
1335
	}
1336

    
1337
	$dhcpdv6conf = <<<EOD
1338

    
1339
option domain-name "{$syscfg['domain']}";
1340
option ldap-server code 95 = text;
1341
option domain-search-list code 119 = text;
1342
{$custoptionsv6}
1343
default-lease-time 7200;
1344
max-lease-time 86400;
1345
log-facility local7;
1346
one-lease-per-client true;
1347
deny duplicates;
1348
ping-check true;
1349
update-conflict-detection false;
1350

    
1351
EOD;
1352

    
1353
	if (!isset($dhcpv6ifconf['disableauthoritative'])) {
1354
		$dhcpdv6conf .= "authoritative;\n";
1355
	}
1356

    
1357
	if (isset($dhcpv6ifconf['alwaysbroadcast'])) {
1358
		$dhcpdv6conf .= "always-broadcast on\n";
1359
	}
1360

    
1361
	$dhcpdv6ifs = array();
1362

    
1363
	$dhcpv6num = 0;
1364
	$nsupdate = false;
1365

    
1366
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1367

    
1368
		$ddns_zones = array();
1369

    
1370
		$ifcfgv6 = $config['interfaces'][$dhcpv6if];
1371

    
1372
		if (!isset($dhcpv6ifconf['enable']) || !isset($Iflist[$dhcpv6if]) || !isset($ifcfgv6['enable'])) {
1373
			continue;
1374
		}
1375
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1376
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
1377
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1378

    
1379
		if ($is_olsr_enabled == true) {
1380
			if ($dhcpv6ifconf['netmask']) {
1381
				$subnetmask = gen_subnet_maskv6($dhcpv6ifconf['netmask']);
1382
			}
1383
		}
1384

    
1385
		$dnscfgv6 = "";
1386

    
1387
		if ($dhcpv6ifconf['domain']) {
1388
			$dnscfgv6 .= "	option domain-name \"{$dhcpv6ifconf['domain']}\";\n";
1389
		}
1390

    
1391
		if ($dhcpv6ifconf['domainsearchlist'] <> "") {
1392
			$dnscfgv6 .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpv6ifconf['domainsearchlist'])) . "\";\n";
1393
		}
1394

    
1395
		if (isset($dhcpv6ifconf['ddnsupdate'])) {
1396
			if ($dhcpv6ifconf['ddnsdomain'] <> "") {
1397
				$dnscfgv6 .= "	ddns-domainname \"{$dhcpv6ifconf['ddnsdomain']}\";\n";
1398
			}
1399
			$dnscfgv6 .= "	ddns-update-style interim;\n";
1400
			$nsupdate = true;
1401
		}
1402

    
1403
		if (is_array($dhcpv6ifconf['dnsserver']) && ($dhcpv6ifconf['dnsserver'][0])) {
1404
			$dnscfgv6 .= "	option dhcp6.name-servers " . join(",", $dhcpv6ifconf['dnsserver']) . ";";
1405
		} else if (((isset($config['dnsmasq']['enable'])) || isset($config['unbound']['enable'])) && (is_ipaddrv6($ifcfgipv6))) {
1406
			$dnscfgv6 .= "	option dhcp6.name-servers {$ifcfgipv6};";
1407
		} else if (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
1408
			$dns_arrv6 = array();
1409
			foreach ($syscfg['dnsserver'] as $dnsserver) {
1410
				if (is_ipaddrv6($dnsserver)) {
1411
					$dns_arrv6[] = $dnsserver;
1412
				}
1413
			}
1414
			if (!empty($dns_arrv6)) {
1415
				$dnscfgv6 .= "	option dhcp6.name-servers " . join(",", $dns_arrv6) . ";";
1416
			}
1417
		}
1418

    
1419
		if ($dhcpv6ifconf['domain']) {
1420
			$newzone = array();
1421
			$newzone['domain-name'] = $dhcpv6ifconf['domain'];
1422
			$newzone['dns-servers'][] = $dhcpv6ifconf['ddnsdomainprimary'];
1423
			$newzone['ddnsdomainkeyname'] = $dhcpv6ifconf['ddnsdomainkeyname'];
1424
			$newzone['ddnsdomainkey'] = $dhcpv6ifconf['ddnsdomainkey'];
1425
			$ddns_zones[] = $newzone;
1426
		}
1427

    
1428
		if (is_ipaddrv6($ifcfgipv6)) {
1429
			$dhcpdv6conf .= "subnet6 {$subnetv6}/{$ifcfgsnv6}";
1430
		} else {
1431
			$subnet6 = gen_subnetv6($dhcpv6ifconf['range']['from'], "64");
1432
			$dhcpdv6conf .= "subnet6 {$subnet6}/64";
1433
		}
1434
		$dhcpdv6conf .= " {\n";
1435

    
1436
		$dhcpdv6conf .= <<<EOD
1437
	range6 {$dhcpv6ifconf['range']['from']} {$dhcpv6ifconf['range']['to']};
1438
$dnscfgv6
1439

    
1440
EOD;
1441

    
1442
		if (is_ipaddrv6($dhcpv6ifconf['prefixrange']['from']) && is_ipaddrv6($dhcpv6ifconf['prefixrange']['to'])) {
1443
			$dhcpdv6conf .= "	prefix6 {$dhcpv6ifconf['prefixrange']['from']} {$dhcpv6ifconf['prefixrange']['to']} /{$dhcpv6ifconf['prefixrange']['prefixlength']};\n";
1444
		}
1445
		if (is_ipaddrv6($dhcpv6ifconf['dns6ip'])) {
1446
			$dhcpdv6conf .= "	option dhcp6.name-servers {$dhcpv6ifconf['dns6ip']};\n";
1447
		}
1448
		// default-lease-time
1449
		if ($dhcpv6ifconf['defaultleasetime']) {
1450
			$dhcpdv6conf .= "	default-lease-time {$dhcpv6ifconf['defaultleasetime']};\n";
1451
		}
1452

    
1453
		// max-lease-time
1454
		if ($dhcpv6ifconf['maxleasetime']) {
1455
			$dhcpdv6conf .= "	max-lease-time {$dhcpv6ifconf['maxleasetime']};\n";
1456
		}
1457

    
1458
		// ntp-servers
1459
		if (is_array($dhcpv6ifconf['ntpserver']) && $dhcpv6ifconf['ntpserver'][0]) {
1460
			$ntpservers = array();
1461
			foreach ($dhcpv6ifconf['ntpserver'] as $ntpserver) {
1462
				if (is_ipaddrv6($ntpserver)) {
1463
					$ntpservers[] = $ntpserver;
1464
				}
1465
			}
1466
			if (count($ntpservers) > 0) {
1467
				$dhcpdv6conf .= "       option dhcp6.sntp-servers " . join(",", $dhcpv6ifconf['ntpserver']) . ";\n";
1468
			}
1469
		}
1470
		// tftp-server-name
1471
		/* Needs ISC DHCPD support
1472
		 if ($dhcpv6ifconf['tftp'] <> "") {
1473
			$dhcpdv6conf .= "	option tftp-server-name \"{$dhcpv6ifconf['tftp']}\";\n";
1474
		 }
1475
		*/
1476

    
1477
		// Handle option, number rowhelper values
1478
		$dhcpdv6conf .= "\n";
1479
		if ($dhcpv6ifconf['numberoptions']['item']) {
1480
			foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
1481
				$itemv6_value = base64_decode($itemv6['value']);
1482
				$dhcpdv6conf .= "	option custom-{$dhcpv6if}-{$itemv6idx} \"{$itemv6_value}\";\n";
1483
			}
1484
		}
1485

    
1486
		// ldap-server
1487
		if ($dhcpv6ifconf['ldap'] <> "") {
1488
			$dhcpdv6conf .= "	option ldap-server \"{$dhcpv6ifconf['ldap']}\";\n";
1489
		}
1490

    
1491
		// net boot information
1492
		if (isset($dhcpv6ifconf['netboot'])) {
1493
			if (!empty($dhcpv6ifconf['bootfile_url'])) {
1494
				$dhcpdv6conf .= "	option dhcp6.bootfile-url \"{$dhcpv6ifconf['bootfile_url']}\";\n";
1495
			}
1496
		}
1497

    
1498
		$dhcpdv6conf .= "}\n";
1499

    
1500
		/* add static mappings */
1501
		/* Needs to use DUID */
1502
		if (is_array($dhcpv6ifconf['staticmap'])) {
1503
			$i = 0;
1504
			foreach ($dhcpv6ifconf['staticmap'] as $sm) {
1505
				$dhcpdv6conf .= <<<EOD
1506
host s_{$dhcpv6if}_{$i} {
1507
	host-identifier option dhcp6.client-id {$sm['duid']};
1508

    
1509
EOD;
1510
				if ($sm['ipaddrv6']) {
1511
					$dhcpdv6conf .= "	fixed-address6 {$sm['ipaddrv6']};\n";
1512
				}
1513

    
1514
				if ($sm['hostname']) {
1515
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
1516
					$dhhostname = str_replace(".", "_", $dhhostname);
1517
					$dhcpdv6conf .= "	option host-name {$dhhostname};\n";
1518
				}
1519
				if ($sm['filename']) {
1520
					$dhcpdv6conf .= "	filename \"{$sm['filename']}\";\n";
1521
				}
1522

    
1523
				if ($sm['rootpath']) {
1524
					$dhcpdv6conf .= "	option root-path \"{$sm['rootpath']}\";\n";
1525
				}
1526

    
1527
				$dhcpdv6conf .= "}\n";
1528
				$i++;
1529
			}
1530
		}
1531

    
1532
		if ($dhcpv6ifconf['domain']) {
1533
			$dhcpdv6conf .= dhcpdkey($dhcpv6ifconf);
1534
			$dhcpdv6conf .= dhcpdzones($ddns_zones);
1535
		}
1536

    
1537
		if ($config['dhcpdv6'][$dhcpv6if]['ramode'] <> "unmanaged" && isset($config['interfaces'][$dhcpv6if]['enable'])) {
1538
			if (preg_match("/poes/si", $dhcpv6if)) {
1539
				/* magic here */
1540
				$dhcpdv6ifs = array_merge($dhcpdv6ifs, get_pppoes_child_interfaces($dhcpv6if));
1541
			} else {
1542
				$realif = get_real_interface($dhcpv6if, "inet6");
1543
				if (stristr("$realif", "bridge")) {
1544
					$mac = get_interface_mac($realif);
1545
					$v6address = generate_ipv6_from_mac($mac);
1546
					/* Create link local address for bridges */
1547
					mwexec("/sbin/ifconfig {$realif} inet6 {$v6address}");
1548
				}
1549
				$realif = escapeshellcmd($realif);
1550
				$dhcpdv6ifs[] = $realif;
1551
			}
1552
		}
1553
	}
1554

    
1555
	if ($nsupdate) {
1556
		$dhcpdv6conf .= "ddns-update-style interim;\n";
1557
	} else {
1558
		$dhcpdv6conf .= "ddns-update-style none;\n";
1559
	}
1560

    
1561
	/* write dhcpdv6.conf */
1562
	if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf", $dhcpdv6conf)) {
1563
		log_error("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n");
1564
		if (platform_booting()) {
1565
			printf("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n");
1566
		}
1567
		unset($dhcpdv6conf);
1568
		return 1;
1569
	}
1570
	unset($dhcpdv6conf);
1571

    
1572
	/* create an empty leases v6 database */
1573
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases")) {
1574
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases");
1575
	}
1576

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

    
1581
	/* fire up dhcpd in a chroot */
1582
	if (count($dhcpdv6ifs) > 0) {
1583
		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 " .
1584
			join(" ", $dhcpdv6ifs));
1585
		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");
1586
	}
1587
	if (platform_booting()) {
1588
		print gettext("done.") . "\n";
1589
	}
1590

    
1591
	return 0;
1592
}
1593

    
1594
function services_igmpproxy_configure() {
1595
	global $config, $g;
1596

    
1597
	/* kill any running igmpproxy */
1598
	killbyname("igmpproxy");
1599

    
1600
	if (!is_array($config['igmpproxy']['igmpentry']) || (count($config['igmpproxy']['igmpentry']) == 0)) {
1601
		return 1;
1602
	}
1603

    
1604
	$iflist = get_configured_interface_list();
1605

    
1606
	$igmpconf = <<<EOD
1607

    
1608
##------------------------------------------------------
1609
## Enable Quickleave mode (Sends Leave instantly)
1610
##------------------------------------------------------
1611
quickleave
1612

    
1613
EOD;
1614

    
1615
	foreach ($config['igmpproxy']['igmpentry'] as $igmpcf) {
1616
		unset($iflist[$igmpcf['ifname']]);
1617
		$realif = get_real_interface($igmpcf['ifname']);
1618
		if (empty($igmpcf['threshold'])) {
1619
			$threshld = 1;
1620
		} else {
1621
			$threshld = $igmpcf['threshold'];
1622
		}
1623
		$igmpconf .= "phyint {$realif} {$igmpcf['type']} ratelimit 0 threshold {$threshld}\n";
1624

    
1625
		if ($igmpcf['address'] <> "") {
1626
			$item = explode(" ", $igmpcf['address']);
1627
			foreach ($item as $iww) {
1628
				$igmpconf .= "altnet {$iww}\n";
1629
			}
1630
		}
1631
		$igmpconf .= "\n";
1632
	}
1633
	foreach ($iflist as $ifn) {
1634
		$realif = get_real_interface($ifn);
1635
		$igmpconf .= "phyint {$realif} disabled\n";
1636
	}
1637
	$igmpconf .= "\n";
1638

    
1639
	$igmpfl = fopen($g['tmp_path'] . "/igmpproxy.conf", "w");
1640
	if (!$igmpfl) {
1641
		log_error(gettext("Could not write Igmpproxy configuration file!"));
1642
		return;
1643
	}
1644
	fwrite($igmpfl, $igmpconf);
1645
	fclose($igmpfl);
1646
	unset($igmpconf);
1647

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

    
1651
	return 0;
1652
}
1653

    
1654
function services_dhcrelay_configure() {
1655
	global $config, $g;
1656

    
1657
	if (isset($config['system']['developerspew'])) {
1658
		$mt = microtime();
1659
		echo "services_dhcrelay_configure() being called $mt\n";
1660
	}
1661

    
1662
	/* kill any running dhcrelay */
1663
	killbypid("{$g['varrun_path']}/dhcrelay.pid");
1664

    
1665
	$dhcrelaycfg =& $config['dhcrelay'];
1666

    
1667
	/* DHCPRelay enabled on any interfaces? */
1668
	if (!isset($dhcrelaycfg['enable'])) {
1669
		return 0;
1670
	}
1671

    
1672
	if (platform_booting()) {
1673
		echo gettext("Starting DHCP relay service...");
1674
	} else {
1675
		sleep(1);
1676
	}
1677

    
1678
	$iflist = get_configured_interface_list();
1679

    
1680
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1681
	foreach ($dhcifaces as $dhcrelayif) {
1682
		if (!isset($iflist[$dhcrelayif]) ||
1683
		    link_interface_to_bridge($dhcrelayif)) {
1684
			continue;
1685
		}
1686

    
1687
		if (is_ipaddr(get_interface_ip($dhcrelayif))) {
1688
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1689
		}
1690
	}
1691

    
1692
	/*
1693
	 * In order for the relay to work, it needs to be active
1694
	 * on the interface in which the destination server sits.
1695
	 */
1696
	$srvips = explode(",", $dhcrelaycfg['server']);
1697
	if (!is_array($srvips)) {
1698
		log_error("No destination IP has been configured!");
1699
		return;
1700
	}
1701

    
1702
	foreach ($srvips as $srcidx => $srvip) {
1703
		unset($destif);
1704
		foreach ($iflist as $ifname) {
1705
			$subnet = get_interface_ip($ifname);
1706
			if (!is_ipaddr($subnet)) {
1707
				continue;
1708
			}
1709
			$subnet .= "/" . get_interface_subnet($ifname);
1710
			if (ip_in_subnet($srvip, $subnet)) {
1711
				$destif = get_real_interface($ifname);
1712
				break;
1713
			}
1714
		}
1715
		if (!isset($destif)) {
1716
			foreach (get_staticroutes() as $rtent) {
1717
				if (ip_in_subnet($srvip, $rtent['network'])) {
1718
					$a_gateways = return_gateways_array(true);
1719
					$destif = $a_gateways[$rtent['gateway']]['interface'];
1720
					break;
1721
				}
1722
			}
1723
		}
1724

    
1725
		if (!isset($destif)) {
1726
			/* Create a array from the existing route table */
1727
			exec("/usr/bin/netstat -rnWf inet", $route_str);
1728
			array_shift($route_str);
1729
			array_shift($route_str);
1730
			array_shift($route_str);
1731
			array_shift($route_str);
1732
			$route_arr = array();
1733
			foreach ($route_str as $routeline) {
1734
				$items = preg_split("/[ ]+/i", $routeline);
1735
				if (is_subnetv4($items[0])) {
1736
					$subnet = $items[0];
1737
				} elseif (is_ipaddrv4($items[0])) {
1738
					$subnet = "{$items[0]}/32";
1739
				} else {
1740
					// Not a subnet or IP address, skip to the next line.
1741
					continue;
1742
				}
1743
				if (ip_in_subnet($srvip, $subnet)) {
1744
					$destif = trim($items[6]);
1745
					break;
1746
				}
1747
			}
1748
		}
1749

    
1750
		if (!isset($destif)) {
1751
			if (is_array($config['gateways']['gateway_item'])) {
1752
				foreach ($config['gateways']['gateway_item'] as $gateway) {
1753
					if (isset($gateway['defaultgw'])) {
1754
						$destif = get_real_interface($gateway['interface']);
1755
						break;
1756
					}
1757
				}
1758
			} else {
1759
				$destif = get_real_interface("wan");
1760
			}
1761
		}
1762

    
1763
		if (!empty($destif)) {
1764
			$dhcrelayifs[] = $destif;
1765
		}
1766
	}
1767
	$dhcrelayifs = array_unique($dhcrelayifs);
1768

    
1769
	/* fire up dhcrelay */
1770
	if (empty($dhcrelayifs)) {
1771
		log_error("No suitable interface found for running dhcrelay!");
1772
		return; /* XXX */
1773
	}
1774

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

    
1777
	if (isset($dhcrelaycfg['agentoption'])) {
1778
		$cmd .= " -a -m replace";
1779
	}
1780

    
1781
	$cmd .= " " . implode(" ", $srvips);
1782
	mwexec($cmd);
1783
	unset($cmd);
1784

    
1785
	return 0;
1786
}
1787

    
1788
function services_dhcrelay6_configure() {
1789
	global $config, $g;
1790

    
1791
	if (isset($config['system']['developerspew'])) {
1792
		$mt = microtime();
1793
		echo "services_dhcrelay6_configure() being called $mt\n";
1794
	}
1795

    
1796
	/* kill any running dhcrelay */
1797
	killbypid("{$g['varrun_path']}/dhcrelay6.pid");
1798

    
1799
	$dhcrelaycfg =& $config['dhcrelay6'];
1800

    
1801
	/* DHCPv6 Relay enabled on any interfaces? */
1802
	if (!isset($dhcrelaycfg['enable'])) {
1803
		return 0;
1804
	}
1805

    
1806
	if (platform_booting()) {
1807
		echo gettext("Starting DHCPv6 relay service...");
1808
	} else {
1809
		sleep(1);
1810
	}
1811

    
1812
	$iflist = get_configured_interface_list();
1813

    
1814
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1815
	foreach ($dhcifaces as $dhcrelayif) {
1816
		if (!isset($iflist[$dhcrelayif]) ||
1817
		    link_interface_to_bridge($dhcrelayif)) {
1818
			continue;
1819
		}
1820

    
1821
		if (is_ipaddrv6(get_interface_ipv6($dhcrelayif))) {
1822
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1823
		}
1824
	}
1825
	$dhcrelayifs = array_unique($dhcrelayifs);
1826

    
1827
	/*
1828
	 * In order for the relay to work, it needs to be active
1829
	 * on the interface in which the destination server sits.
1830
	 */
1831
	$srvips = explode(",", $dhcrelaycfg['server']);
1832
	$srvifaces = array();
1833
	foreach ($srvips as $srcidx => $srvip) {
1834
		unset($destif);
1835
		foreach ($iflist as $ifname) {
1836
			$subnet = get_interface_ipv6($ifname);
1837
			if (!is_ipaddrv6($subnet)) {
1838
				continue;
1839
			}
1840
			$subnet .= "/" . get_interface_subnetv6($ifname);
1841
			if (ip_in_subnet($srvip, $subnet)) {
1842
				$destif = get_real_interface($ifname);
1843
				break;
1844
			}
1845
		}
1846
		if (!isset($destif)) {
1847
			if (is_array($config['staticroutes']['route'])) {
1848
				foreach ($config['staticroutes']['route'] as $rtent) {
1849
					if (ip_in_subnet($srvip, $rtent['network'])) {
1850
						$a_gateways = return_gateways_array(true);
1851
						$destif = $a_gateways[$rtent['gateway']]['interface'];
1852
						break;
1853
					}
1854
				}
1855
			}
1856
		}
1857

    
1858
		if (!isset($destif)) {
1859
			/* Create a array from the existing route table */
1860
			exec("/usr/bin/netstat -rnWf inet6", $route_str);
1861
			array_shift($route_str);
1862
			array_shift($route_str);
1863
			array_shift($route_str);
1864
			array_shift($route_str);
1865
			$route_arr = array();
1866
			foreach ($route_str as $routeline) {
1867
				$items = preg_split("/[ ]+/i", $routeline);
1868
				if (ip_in_subnet($srvip, $items[0])) {
1869
					$destif = trim($items[6]);
1870
					break;
1871
				}
1872
			}
1873
		}
1874

    
1875
		if (!isset($destif)) {
1876
			if (is_array($config['gateways']['gateway_item'])) {
1877
				foreach ($config['gateways']['gateway_item'] as $gateway) {
1878
					if (isset($gateway['defaultgw'])) {
1879
						$destif = get_real_interface($gateway['interface']);
1880
						break;
1881
					}
1882
				}
1883
			} else {
1884
				$destif = get_real_interface("wan");
1885
			}
1886
		}
1887

    
1888
		if (!empty($destif)) {
1889
			$srvifaces[] = "{$srvip}%{$destif}";
1890
		}
1891
	}
1892

    
1893
	/* fire up dhcrelay */
1894
	if (empty($dhcrelayifs) || empty($srvifaces)) {
1895
		log_error("No suitable interface found for running dhcrelay -6!");
1896
		return; /* XXX */
1897
	}
1898

    
1899
	$cmd = "/usr/local/sbin/dhcrelay -6 -pf \"{$g['varrun_path']}/dhcrelay6.pid\"";
1900
	foreach ($dhcrelayifs as $dhcrelayif) {
1901
		$cmd .= " -l {$dhcrelayif}";
1902
	}
1903
	foreach ($srvifaces as $srviface) {
1904
		$cmd .= " -u \"{$srviface}\"";
1905
	}
1906
	mwexec($cmd);
1907
	unset($cmd);
1908

    
1909
	return 0;
1910
}
1911

    
1912
function services_dyndns_configure_client($conf) {
1913

    
1914
	if (!isset($conf['enable'])) {
1915
		return;
1916
	}
1917

    
1918
	/* load up the dyndns.class */
1919
	require_once("dyndns.class");
1920

    
1921
	$dns = new updatedns($dnsService = $conf['type'],
1922
		$dnsHost = $conf['host'],
1923
		$dnsDomain = $conf['domainname'],
1924
		$dnsUser = $conf['username'],
1925
		$dnsPass = $conf['password'],
1926
		$dnsWildcard = $conf['wildcard'],
1927
		$dnsMX = $conf['mx'],
1928
		$dnsIf = "{$conf['interface']}",
1929
		$dnsBackMX = NULL,
1930
		$dnsServer = NULL,
1931
		$dnsPort = NULL,
1932
		$dnsUpdateURL = "{$conf['updateurl']}",
1933
		$forceUpdate = $conf['force'],
1934
		$dnsZoneID = $conf['zoneid'],
1935
		$dnsTTL = $conf['ttl'],
1936
		$dnsResultMatch = "{$conf['resultmatch']}",
1937
		$dnsRequestIf = "{$conf['requestif']}",
1938
		$dnsID = "{$conf['id']}",
1939
		$dnsVerboseLog = $conf['verboselog'],
1940
		$curlIpresolveV4 = $conf['curl_ipresolve_v4'],
1941
		$curlSslVerifypeer = $conf['curl_ssl_verifypeer']);
1942
}
1943

    
1944
function services_dyndns_configure($int = "") {
1945
	global $config, $g;
1946
	if (isset($config['system']['developerspew'])) {
1947
		$mt = microtime();
1948
		echo "services_dyndns_configure() being called $mt\n";
1949
	}
1950

    
1951
	$dyndnscfg = $config['dyndnses']['dyndns'];
1952
	$gwgroups = return_gateway_groups_array();
1953
	if (is_array($dyndnscfg)) {
1954
		if (platform_booting()) {
1955
			echo gettext("Starting DynDNS clients...");
1956
		}
1957

    
1958
		foreach ($dyndnscfg as $dyndns) {
1959
			if ((empty($int)) || ($int == $dyndns['interface']) || (is_array($gwgroups[$dyndns['interface']]))) {
1960
				$dyndns['verboselog'] = isset($dyndns['verboselog']);
1961
				$dyndns['curl_ipresolve_v4'] = isset($dyndns['curl_ipresolve_v4']);
1962
				$dyndns['curl_ssl_verifypeer'] = isset($dyndns['curl_ssl_verifypeer']);
1963
				services_dyndns_configure_client($dyndns);
1964
				sleep(1);
1965
			}
1966
		}
1967

    
1968
		if (platform_booting()) {
1969
			echo gettext("done.") . "\n";
1970
		}
1971
	}
1972

    
1973
	return 0;
1974
}
1975

    
1976
function dyndnsCheckIP($int) {
1977
	global $config;
1978
	$ip_address = get_interface_ip($int);
1979
	if (is_private_ip($ip_address)) {
1980
		$gateways_status = return_gateways_status(true);
1981
		// If the gateway for this interface is down, then the external check cannot work.
1982
		// Avoid the long wait for the external check to timeout.
1983
		if (stristr($gateways_status[$config['interfaces'][$int]['gateway']]['status'], "down")) {
1984
			return "down";
1985
		}
1986
		$hosttocheck = "http://checkip.dyndns.org";
1987
		$ip_ch = curl_init($hosttocheck);
1988
		curl_setopt($ip_ch, CURLOPT_RETURNTRANSFER, 1);
1989
		curl_setopt($ip_ch, CURLOPT_SSL_VERIFYPEER, FALSE);
1990
		curl_setopt($ip_ch, CURLOPT_INTERFACE, 'host!' . $ip_address);
1991
		curl_setopt($ip_ch, CURLOPT_CONNECTTIMEOUT, '30');
1992
		curl_setopt($ip_ch, CURLOPT_TIMEOUT, 120);
1993
		curl_setopt($ip_ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1994
		$ip_result_page = curl_exec($ip_ch);
1995
		curl_close($ip_ch);
1996
		$ip_result_decoded = urldecode($ip_result_page);
1997
		preg_match('=Current IP Address: (.*)</body>=siU', $ip_result_decoded, $matches);
1998
		$ip_address = trim($matches[1]);
1999
	}
2000
	return $ip_address;
2001
}
2002

    
2003
function services_dnsmasq_configure() {
2004
	global $config, $g;
2005
	$return = 0;
2006

    
2007
	// hard coded args: will be removed to avoid duplication if specified in custom_options
2008
	$standard_args = array(
2009
		"dns-forward-max" => "--dns-forward-max=5000",
2010
		"cache-size" => "--cache-size=10000",
2011
		"local-ttl" => "--local-ttl=1"
2012
	);
2013

    
2014

    
2015
	if (isset($config['system']['developerspew'])) {
2016
		$mt = microtime();
2017
		echo "services_dnsmasq_configure() being called $mt\n";
2018
	}
2019

    
2020
	/* kill any running dnsmasq */
2021
	if (file_exists("{$g['varrun_path']}/dnsmasq.pid")) {
2022
		sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM");
2023
	}
2024

    
2025
	if (isset($config['dnsmasq']['enable'])) {
2026

    
2027
		if (platform_booting()) {
2028
			echo gettext("Starting DNS forwarder...");
2029
		} else {
2030
			sleep(1);
2031
		}
2032

    
2033
		/* generate hosts file */
2034
		if (system_hosts_generate() != 0) {
2035
			$return = 1;
2036
		}
2037

    
2038
		$args = "";
2039

    
2040
		if (isset($config['dnsmasq']['regdhcp'])) {
2041
			$args .= " --dhcp-hostsfile={$g['varetc_path']}/hosts ";
2042
		}
2043

    
2044
		/* Setup listen port, if non-default */
2045
		if (is_port($config['dnsmasq']['port'])) {
2046
			$args .= " --port={$config['dnsmasq']['port']} ";
2047
		}
2048

    
2049
		$listen_addresses = "";
2050
		if (isset($config['dnsmasq']['interface'])) {
2051
			$interfaces = explode(",", $config['dnsmasq']['interface']);
2052
			foreach ($interfaces as $interface) {
2053
				if (is_ipaddrv4($interface)) {
2054
					$listen_addresses .= " --listen-address={$interface} ";
2055
				} else if (is_ipaddrv6($interface)) {
2056
					/*
2057
					 * XXX: Since dnsmasq does not support link-local address
2058
					 * with scope specified. These checks are being done.
2059
					 */
2060
					if (is_linklocal($interface) && strstr($interface, "%")) {
2061
						$tmpaddrll6 = explode("%", $interface);
2062
						$listen_addresses .= " --listen-address={$tmpaddrll6[0]} ";
2063
					} else {
2064
						$listen_addresses .= " --listen-address={$interface} ";
2065
					}
2066
				} else if (strstr($interface, "_vip")) {
2067
					$laddr = get_configured_carp_interface_list($interface);
2068
					if (is_ipaddr($laddr)) {
2069
						$listen_addresses .= " --listen-address={$laddr} ";
2070
					}
2071
				} else {
2072
					$if = get_real_interface($interface);
2073
					if (does_interface_exist($if)) {
2074
						$laddr = get_interface_ip($interface);
2075
						if (is_ipaddrv4($laddr)) {
2076
							$listen_addresses .= " --listen-address={$laddr} ";
2077
						}
2078
						$laddr6 = get_interface_ipv6($interface);
2079
						if (is_ipaddrv6($laddr6) && !isset($config['dnsmasq']['strictbind'])) {
2080
							/*
2081
							 * XXX: Since dnsmasq does not support link-local address
2082
							 * with scope specified. These checks are being done.
2083
							 */
2084
							if (is_linklocal($laddr6) && strstr($laddr6, "%")) {
2085
								$tmpaddrll6 = explode("%", $laddr6);
2086
								$listen_addresses .= " --listen-address={$tmpaddrll6[0]} ";
2087
							} else {
2088
								$listen_addresses .= " --listen-address={$laddr6} ";
2089
							}
2090
						}
2091
					}
2092
				}
2093
			}
2094
			if (!empty($listen_addresses)) {
2095
				$args .= " {$listen_addresses} ";
2096
				if (isset($config['dnsmasq']['strictbind'])) {
2097
					$args .= " --bind-interfaces ";
2098
				}
2099
			}
2100
		}
2101

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

    
2109
			// Build an array of domain overrides to help in checking for matches.
2110
			$override_a = array();
2111
			if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2112
				foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2113
					$override_a[$override['domain']] = "y";
2114
				}
2115
			}
2116

    
2117
			// Build an array of the private reverse lookup domain names
2118
			$reverse_domain_a = array("10.in-addr.arpa", "168.192.in-addr.arpa");
2119
			// Unfortunately the 172.16.0.0/12 range does not map nicely to the in-addr.arpa scheme.
2120
			for ($subnet_num = 16; $subnet_num < 32; $subnet_num++) {
2121
				$reverse_domain_a[] = "$subnet_num.172.in-addr.arpa";
2122
			}
2123

    
2124
			// Set the --server parameter to nowhere for each reverse domain name that was not specifically specified in a domain override.
2125
			foreach ($reverse_domain_a as $reverse_domain) {
2126
				if (!isset($override_a[$reverse_domain])) {
2127
					$args .= " --server=/$reverse_domain/ ";
2128
				}
2129
			}
2130
			unset($override_a);
2131
			unset($reverse_domain_a);
2132
		}
2133

    
2134
		/* Setup forwarded domains */
2135
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2136
			foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2137
				if ($override['ip'] == "!") {
2138
					$override[ip] = "";
2139
				}
2140
				$args .= ' --server=/' . $override['domain'] . '/' . $override['ip'];
2141
			}
2142
		}
2143

    
2144
		/* Allow DNS Rebind for forwarded domains */
2145
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2146
			if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
2147
				foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2148
					$args .= ' --rebind-domain-ok=/' . $override['domain'] . '/ ';
2149
				}
2150
			}
2151
		}
2152

    
2153
		if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
2154
			$dns_rebind = "--rebind-localhost-ok --stop-dns-rebind";
2155
		}
2156

    
2157
		if (isset($config['dnsmasq']['strict_order'])) {
2158
			$args .= " --strict-order ";
2159
		}
2160

    
2161
		if (isset($config['dnsmasq']['domain_needed'])) {
2162
			$args .= " --domain-needed ";
2163
		}
2164

    
2165
		if ($config['dnsmasq']['custom_options']) {
2166
			foreach (preg_split('/\s+/', $config['dnsmasq']['custom_options']) as $c) {
2167
				$args .= " " . escapeshellarg("--{$c}");
2168
				$p = explode('=', $c);
2169
				if (array_key_exists($p[0], $standard_args)) {
2170
					unset($standard_args[$p[0]]);
2171
				}
2172
			}
2173
		}
2174
		$args .= ' ' . implode(' ', array_values($standard_args));
2175

    
2176
		/* run dnsmasq */
2177
		$cmd = "/usr/local/sbin/dnsmasq --all-servers {$dns_rebind} {$args}";
2178
		//log_error("dnsmasq command: {$cmd}");
2179
		mwexec_bg($cmd);
2180
		unset($args);
2181

    
2182
		system_dhcpleases_configure();
2183

    
2184
		if (platform_booting()) {
2185
			echo gettext("done.") . "\n";
2186
		}
2187
	}
2188

    
2189
	if (!platform_booting()) {
2190
		if (services_dhcpd_configure() != 0) {
2191
			$return = 1;
2192
		}
2193
	}
2194

    
2195
	return $return;
2196
}
2197

    
2198
function services_unbound_configure() {
2199
	global $config, $g;
2200
	$return = 0;
2201

    
2202
	if (isset($config['system']['developerspew'])) {
2203
		$mt = microtime();
2204
		echo "services_unbound_configure() being called $mt\n";
2205
	}
2206

    
2207
	// kill any running Unbound instance
2208
	if (file_exists("{$g['varrun_path']}/unbound.pid")) {
2209
		sigkillbypid("{$g['varrun_path']}/unbound.pid", "TERM");
2210
	}
2211

    
2212
	if (isset($config['unbound']['enable'])) {
2213
		if (platform_booting()) {
2214
			echo gettext("Starting DNS Resolver...");
2215
		} else {
2216
			sleep(1);
2217
		}
2218

    
2219
		/* generate hosts file */
2220
		if (system_hosts_generate() != 0) {
2221
			$return = 1;
2222
		}
2223

    
2224
		require_once('/etc/inc/unbound.inc');
2225
		sync_unbound_service();
2226
		if (platform_booting()) {
2227
			echo gettext("done.") . "\n";
2228
		}
2229

    
2230
		system_dhcpleases_configure();
2231
	}
2232

    
2233
	if (!platform_booting()) {
2234
		if (services_dhcpd_configure() != 0) {
2235
			$return = 1;
2236
		}
2237
	}
2238

    
2239
	return $return;
2240
}
2241

    
2242
function services_snmpd_configure() {
2243
	global $config, $g;
2244
	if (isset($config['system']['developerspew'])) {
2245
		$mt = microtime();
2246
		echo "services_snmpd_configure() being called $mt\n";
2247
	}
2248

    
2249
	/* kill any running snmpd */
2250
	sigkillbypid("{$g['varrun_path']}/snmpd.pid", "TERM");
2251
	sleep(2);
2252
	if (is_process_running("bsnmpd")) {
2253
		mwexec("/usr/bin/killall bsnmpd", true);
2254
	}
2255

    
2256
	if (isset($config['snmpd']['enable'])) {
2257

    
2258
		if (platform_booting()) {
2259
			echo gettext("Starting SNMP daemon... ");
2260
		}
2261

    
2262
		/* generate snmpd.conf */
2263
		$fd = fopen("{$g['varetc_path']}/snmpd.conf", "w");
2264
		if (!$fd) {
2265
			printf(gettext("Error: cannot open snmpd.conf in services_snmpd_configure().%s"), "\n");
2266
			return 1;
2267
		}
2268

    
2269

    
2270
		$snmpdconf = <<<EOD
2271
location := "{$config['snmpd']['syslocation']}"
2272
contact := "{$config['snmpd']['syscontact']}"
2273
read := "{$config['snmpd']['rocommunity']}"
2274

    
2275
EOD;
2276

    
2277
/* No docs on what write strings do there for disable for now.
2278
		if (isset($config['snmpd']['rwenable']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2279
			$snmpdconf .= <<<EOD
2280
# write string
2281
write := "{$config['snmpd']['rwcommunity']}"
2282

    
2283
EOD;
2284
		}
2285
*/
2286

    
2287

    
2288
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2289
			$snmpdconf .= <<<EOD
2290
# SNMP Trap support.
2291
traphost := {$config['snmpd']['trapserver']}
2292
trapport := {$config['snmpd']['trapserverport']}
2293
trap := "{$config['snmpd']['trapstring']}"
2294

    
2295

    
2296
EOD;
2297
		}
2298

    
2299
		$platform = trim(file_get_contents('/etc/platform'));
2300
		if (($platform == "pfSense") && ($g['product_name'] != "pfSense")) {
2301
			$platform = $g['product_name'];
2302
		}
2303
		$sysDescr = "{$g['product_name']} " . php_uname("n") .
2304
			" {$g['product_version']} {$platform} " . php_uname("s") .
2305
			" " . php_uname("r") . " " . php_uname("m");
2306

    
2307
		$snmpdconf .= <<<EOD
2308
system := 1     # pfSense
2309
%snmpd
2310
sysDescr			= "{$sysDescr}"
2311
begemotSnmpdDebugDumpPdus       = 2
2312
begemotSnmpdDebugSyslogPri      = 7
2313
begemotSnmpdCommunityString.0.1 = $(read)
2314

    
2315
EOD;
2316

    
2317
/* No docs on what write strings do there for disable for now.
2318
		if (isset($config['snmpd']['rwcommunity']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2319
			$snmpdconf .= <<<EOD
2320
begemotSnmpdCommunityString.0.2 = $(write)
2321

    
2322
EOD;
2323
		}
2324
*/
2325

    
2326

    
2327
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2328
			$snmpdconf .= <<<EOD
2329
begemotTrapSinkStatus.[$(traphost)].$(trapport) = 4
2330
begemotTrapSinkVersion.[$(traphost)].$(trapport) = 2
2331
begemotTrapSinkComm.[$(traphost)].$(trapport) = $(trap)
2332

    
2333
EOD;
2334
		}
2335

    
2336

    
2337
		$snmpdconf .= <<<EOD
2338
begemotSnmpdCommunityDisable    = 1
2339

    
2340
EOD;
2341

    
2342
		$bind_to_ip = "0.0.0.0";
2343
		if (isset($config['snmpd']['bindip'])) {
2344
			if (is_ipaddr($config['snmpd']['bindip'])) {
2345
				$bind_to_ip = $config['snmpd']['bindip'];
2346
			} else {
2347
				$if = get_real_interface($config['snmpd']['bindip']);
2348
				if (does_interface_exist($if)) {
2349
					$bind_to_ip = get_interface_ip($config['snmpd']['bindip']);
2350
				}
2351
			}
2352
		}
2353

    
2354
		if (is_port($config['snmpd']['pollport'])) {
2355
			$snmpdconf .= <<<EOD
2356
begemotSnmpdPortStatus.{$bind_to_ip}.{$config['snmpd']['pollport']} = 1
2357

    
2358
EOD;
2359

    
2360
		}
2361

    
2362
		$snmpdconf .= <<<EOD
2363
begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1
2364
begemotSnmpdLocalPortType."/var/run/snmpd.sock" = 4
2365

    
2366
# These are bsnmp macros not php vars.
2367
sysContact      = $(contact)
2368
sysLocation     = $(location)
2369
sysObjectId     = 1.3.6.1.4.1.12325.1.1.2.1.$(system)
2370

    
2371
snmpEnableAuthenTraps = 2
2372

    
2373
EOD;
2374

    
2375
		if (is_array($config['snmpd']['modules'])) {
2376
			if (isset($config['snmpd']['modules']['mibii'])) {
2377
			$snmpdconf .= <<<EOD
2378
begemotSnmpdModulePath."mibII"  = "/usr/lib/snmp_mibII.so"
2379

    
2380
EOD;
2381
			}
2382

    
2383
			if (isset($config['snmpd']['modules']['netgraph'])) {
2384
				$snmpdconf .= <<<EOD
2385
begemotSnmpdModulePath."netgraph" = "/usr/lib/snmp_netgraph.so"
2386
%netgraph
2387
begemotNgControlNodeName = "snmpd"
2388

    
2389
EOD;
2390
			}
2391

    
2392
			if (isset($config['snmpd']['modules']['pf'])) {
2393
				$snmpdconf .= <<<EOD
2394
begemotSnmpdModulePath."pf"     = "/usr/lib/snmp_pf.so"
2395

    
2396
EOD;
2397
			}
2398

    
2399
			if (isset($config['snmpd']['modules']['hostres'])) {
2400
				$snmpdconf .= <<<EOD
2401
begemotSnmpdModulePath."hostres"     = "/usr/lib/snmp_hostres.so"
2402

    
2403
EOD;
2404
			}
2405

    
2406
			if (isset($config['snmpd']['modules']['bridge'])) {
2407
				$snmpdconf .= <<<EOD
2408
begemotSnmpdModulePath."bridge"     = "/usr/lib/snmp_bridge.so"
2409
# config must end with blank line
2410

    
2411
EOD;
2412
			}
2413
			if (isset($config['snmpd']['modules']['ucd'])) {
2414
				$snmpdconf .= <<<EOD
2415
begemotSnmpdModulePath."ucd"     = "/usr/local/lib/snmp_ucd.so"
2416

    
2417
EOD;
2418
			}
2419
			if (isset($config['snmpd']['modules']['regex'])) {
2420
				$snmpdconf .= <<<EOD
2421
begemotSnmpdModulePath."regex"     = "/usr/local/lib/snmp_regex.so"
2422

    
2423
EOD;
2424
			}
2425
		}
2426

    
2427
		fwrite($fd, $snmpdconf);
2428
		fclose($fd);
2429
		unset($snmpdconf);
2430

    
2431
		/* run bsnmpd */
2432
		mwexec("/usr/sbin/bsnmpd -c {$g['varetc_path']}/snmpd.conf" .
2433
			" -p {$g['varrun_path']}/snmpd.pid");
2434

    
2435
		if (platform_booting()) {
2436
			echo gettext("done.") . "\n";
2437
		}
2438
	}
2439

    
2440
	return 0;
2441
}
2442

    
2443
function services_dnsupdate_process($int = "", $updatehost = "", $forced = false) {
2444
	global $config, $g;
2445
	if (isset($config['system']['developerspew'])) {
2446
		$mt = microtime();
2447
		echo "services_dnsupdate_process() being called $mt\n";
2448
	}
2449

    
2450
	/* Dynamic DNS updating active? */
2451
	if (is_array($config['dnsupdates']['dnsupdate'])) {
2452
		$notify_text = "";
2453
		foreach ($config['dnsupdates']['dnsupdate'] as $i => $dnsupdate) {
2454
			if (!isset($dnsupdate['enable'])) {
2455
				continue;
2456
			}
2457
			if (!empty($int) && $int != $dnsupdate['interface']) {
2458
				continue;
2459
			}
2460
			if (!empty($updatehost) && ($updatehost != $dnsupdate['host'])) {
2461
				continue;
2462
			}
2463

    
2464
			/* determine interface name */
2465
			$if = get_real_interface($dnsupdate['interface']);
2466

    
2467
			if (isset($dnsupdate['usepublicip'])) {
2468
				$wanip = dyndnsCheckIP($dnsupdate['interface']);
2469
			} else {
2470
				$wanip = get_interface_ip($dnsupdate['interface']);
2471
			}
2472

    
2473
			$wanipv6 = get_interface_ipv6($dnsupdate['interface']);
2474
			$cacheFile = "{$g['conf_path']}/dyndns_{$dnsupdate['interface']}_rfc2136_" . escapeshellarg($dnsupdate['host']) . "_{$dnsupdate['server']}.cache";
2475
			$currentTime = time();
2476

    
2477
			if ($wanip || $wanipv6) {
2478
				$keyname = $dnsupdate['keyname'];
2479
				/* trailing dot */
2480
				if (substr($keyname, -1) != ".") {
2481
					$keyname .= ".";
2482
				}
2483

    
2484
				$hostname = $dnsupdate['host'];
2485
				/* trailing dot */
2486
				if (substr($hostname, -1) != ".") {
2487
					$hostname .= ".";
2488
				}
2489

    
2490
				/* write private key file
2491
				   this is dumb - public and private keys are the same for HMAC-MD5,
2492
				   but nsupdate insists on having both */
2493
				$fd = fopen("{$g['varetc_path']}/K{$i}{$keyname}+157+00000.private", "w");
2494
				$privkey = <<<EOD
2495
Private-key-format: v1.2
2496
Algorithm: 157 (HMAC)
2497
Key: {$dnsupdate['keydata']}
2498

    
2499
EOD;
2500
				fwrite($fd, $privkey);
2501
				fclose($fd);
2502

    
2503
				/* write public key file */
2504
				if ($dnsupdate['keytype'] == "zone") {
2505
					$flags = 257;
2506
					$proto = 3;
2507
				} else if ($dnsupdate['keytype'] == "host") {
2508
					$flags = 513;
2509
					$proto = 3;
2510
				} else if ($dnsupdate['keytype'] == "user") {
2511
					$flags = 0;
2512
					$proto = 2;
2513
				}
2514

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

    
2519
				/* generate update instructions */
2520
				$upinst = "";
2521
				if (!empty($dnsupdate['server'])) {
2522
					$upinst .= "server {$dnsupdate['server']}\n";
2523
				}
2524

    
2525
				if (file_exists($cacheFile)) {
2526
					list($cachedipv4, $cacheTimev4) = explode("|", file_get_contents($cacheFile));
2527
				}
2528
				if (file_exists("{$cacheFile}.ipv6")) {
2529
					list($cachedipv6, $cacheTimev6) = explode("|", file_get_contents("{$cacheFile}.ipv6"));
2530
				}
2531

    
2532
				// 25 Days
2533
				$maxCacheAgeSecs = 25 * 24 * 60 * 60;
2534
				$need_update = false;
2535

    
2536
				conf_mount_rw();
2537
				/* Update IPv4 if we have it. */
2538
				if (is_ipaddrv4($wanip) && $dnsupdate['recordtype'] != "AAAA") {
2539
					if (($wanip != $cachedipv4) || (($currentTime - $cacheTimev4) > $maxCacheAgeSecs) || $forced) {
2540
						$upinst .= "update delete {$dnsupdate['host']}. A\n";
2541
						$upinst .= "update add {$dnsupdate['host']}. {$dnsupdate['ttl']} A {$wanip}\n";
2542
						$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";
2543
						@file_put_contents($cacheFile, "{$wanip}|{$currentTime}");
2544
						log_error("phpDynDNS: updating cache file {$cacheFile}: {$wanip}");
2545
						$need_update = true;
2546
					} else {
2547
						log_error("phpDynDNS: Not updating {$dnsupdate['host']} A record because the IP address has not changed.");
2548
					}
2549
				} else {
2550
					@unlink($cacheFile);
2551
				}
2552

    
2553
				/* Update IPv6 if we have it. */
2554
				if (is_ipaddrv6($wanipv6) && $dnsupdate['recordtype'] != "A") {
2555
					if (($wanipv6 != $cachedipv6) || (($currentTime - $cacheTimev6) > $maxCacheAgeSecs) || $forced) {
2556
						$upinst .= "update delete {$dnsupdate['host']}. AAAA\n";
2557
						$upinst .= "update add {$dnsupdate['host']}. {$dnsupdate['ttl']} AAAA {$wanipv6}\n";
2558
						$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";
2559
						@file_put_contents("{$cacheFile}.ipv6", "{$wanipv6}|{$currentTime}");
2560
						log_error("phpDynDNS: updating cache file {$cacheFile}.ipv6: {$wanipv6}");
2561
						$need_update = true;
2562
					} else {
2563
						log_error("phpDynDNS: Not updating {$dnsupdate['host']} AAAA record because the IPv6 address has not changed.");
2564
					}
2565
				} else {
2566
					@unlink("{$cacheFile}.ipv6");
2567
				}
2568
				conf_mount_ro();
2569

    
2570
				$upinst .= "\n";	/* mind that trailing newline! */
2571

    
2572
				if ($need_update) {
2573
					@file_put_contents("{$g['varetc_path']}/nsupdatecmds{$i}", $upinst);
2574
					unset($upinst);
2575
					/* invoke nsupdate */
2576
					$cmd = "/usr/local/bin/nsupdate -k {$g['varetc_path']}/K{$i}{$keyname}+157+00000.key";
2577
					if (isset($dnsupdate['usetcp'])) {
2578
						$cmd .= " -v";
2579
					}
2580
					$cmd .= " {$g['varetc_path']}/nsupdatecmds{$i}";
2581
					mwexec_bg($cmd);
2582
					unset($cmd);
2583
				}
2584
			}
2585
		}
2586
		if (!empty($notify_text)) {
2587
			notify_all_remote($notify_text);
2588
		}
2589
	}
2590

    
2591
	return 0;
2592
}
2593

    
2594
/* configure cron service */
2595
function configure_cron() {
2596
	global $g, $config;
2597

    
2598
	conf_mount_rw();
2599
	/* preserve existing crontab entries */
2600
	$crontab_contents = file("/etc/crontab", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2601

    
2602
	for ($i = 0; $i < count($crontab_contents); $i++) {
2603
		$cron_item =& $crontab_contents[$i];
2604
		if (strpos($cron_item, "# pfSense specific crontab entries") !== false) {
2605
			array_splice($crontab_contents, $i - 1);
2606
			break;
2607
		}
2608
	}
2609
	$crontab_contents = implode("\n", $crontab_contents) . "\n";
2610

    
2611

    
2612
	if (is_array($config['cron']['item'])) {
2613
		$crontab_contents .= "#\n";
2614
		$crontab_contents .= "# " . gettext("pfSense specific crontab entries") . "\n";
2615
		$crontab_contents .= "# " .gettext("Created:") . " " . date("F j, Y, g:i a") . "\n";
2616
		$crontab_contents .= "#\n";
2617

    
2618
		if (isset($config['system']['proxyurl']) && !empty($config['system']['proxyurl'])) {
2619
			$http_proxy = $config['system']['proxyurl'];
2620
			if (isset($config['system']['proxyport']) && !empty($config['system']['proxyport'])) {
2621
				$http_proxy .= ':' . $config['system']['proxyport'];
2622
			}
2623
			$crontab_contents .= "HTTP_PROXY={$http_proxy}";
2624
		}
2625

    
2626
		foreach ($config['cron']['item'] as $item) {
2627
			$crontab_contents .= "\n{$item['minute']}\t";
2628
			$crontab_contents .= "{$item['hour']}\t";
2629
			$crontab_contents .= "{$item['mday']}\t";
2630
			$crontab_contents .= "{$item['month']}\t";
2631
			$crontab_contents .= "{$item['wday']}\t";
2632
			$crontab_contents .= "{$item['who']}\t";
2633
			$crontab_contents .= "{$item['command']}";
2634
		}
2635

    
2636
		$crontab_contents .= "\n#\n";
2637
		$crontab_contents .= "# " . gettext("If possible do not add items to this file manually.") . "\n";
2638
		$crontab_contents .= "# " . gettext("If you do so, this file must be terminated with a blank line (e.g. new line)") . "\n";
2639
		$crontab_contents .= "#\n\n";
2640
	}
2641

    
2642
	/* please maintain the newline at the end of file */
2643
	file_put_contents("/etc/crontab", $crontab_contents);
2644
	unset($crontab_contents);
2645

    
2646
	/* make sure that cron is running and start it if it got killed somehow */
2647
	if (!is_process_running("cron")) {
2648
		exec("cd /tmp && /usr/sbin/cron -s 2>/dev/null");
2649
	} else {
2650
	/* do a HUP kill to force sync changes */
2651
		sigkillbypid("{$g['varrun_path']}/cron.pid", "HUP");
2652
	}
2653

    
2654
	conf_mount_ro();
2655
}
2656

    
2657
function upnp_action ($action) {
2658
	global $g, $config;
2659
	switch ($action) {
2660
		case "start":
2661
			if (file_exists('/var/etc/miniupnpd.conf')) {
2662
				@unlink("{$g['varrun_path']}/miniupnpd.pid");
2663
				mwexec_bg("/usr/local/sbin/miniupnpd -f /var/etc/miniupnpd.conf -P {$g['varrun_path']}/miniupnpd.pid");
2664
			}
2665
			break;
2666
		case "stop":
2667
			killbypid("{$g['varrun_path']}/miniupnpd.pid");
2668
			while ((int)exec("/bin/pgrep -a miniupnpd | wc -l") > 0) {
2669
				mwexec('/usr/bin/killall miniupnpd 2>/dev/null', true);
2670
			}
2671
			mwexec('/sbin/pfctl -aminiupnpd -Fr 2>&1 >/dev/null');
2672
			mwexec('/sbin/pfctl -aminiupnpd -Fn 2>&1 >/dev/null');
2673
			break;
2674
		case "restart":
2675
			upnp_action('stop');
2676
			upnp_action('start');
2677
			break;
2678
	}
2679
}
2680

    
2681
function upnp_start() {
2682
	global $config;
2683

    
2684
	if (!isset($config['installedpackages']['miniupnpd']['config'])) {
2685
		return;
2686
	}
2687

    
2688
	if ($config['installedpackages']['miniupnpd']['config'][0]['enable']) {
2689
		echo gettext("Starting UPnP service... ");
2690
		require_once('/usr/local/pkg/miniupnpd.inc');
2691
		sync_package_miniupnpd();
2692
		echo "done.\n";
2693
	}
2694
}
2695

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

    
2699
	$is_installed = false;
2700
	$cron_changed = true;
2701

    
2702
	if (!is_array($config['cron'])) {
2703
		$config['cron'] = array();
2704
	}
2705
	if (!is_array($config['cron']['item'])) {
2706
		$config['cron']['item'] = array();
2707
	}
2708

    
2709
	$x = 0;
2710
	foreach ($config['cron']['item'] as $item) {
2711
		if (strstr($item['command'], $command)) {
2712
			$is_installed = true;
2713
			break;
2714
		}
2715
		$x++;
2716
	}
2717

    
2718
	if ($active) {
2719
		$cron_item = array();
2720
		$cron_item['minute'] = $minute;
2721
		$cron_item['hour'] = $hour;
2722
		$cron_item['mday'] = $monthday;
2723
		$cron_item['month'] = $month;
2724
		$cron_item['wday'] = $weekday;
2725
		$cron_item['who'] = $who;
2726
		$cron_item['command'] = $command;
2727
		if (!$is_installed) {
2728
			$config['cron']['item'][] = $cron_item;
2729
			write_config(sprintf(gettext("Installed cron job for %s"), $command));
2730
		} else {
2731
			if ($config['cron']['item'][$x] == $cron_item) {
2732
				$cron_changed = false;
2733
				log_error(sprintf(gettext("Checked cron job for %s, no change needed"), $command));
2734
			} else {
2735
				$config['cron']['item'][$x] = $cron_item;
2736
				write_config(sprintf(gettext("Updated cron job for %s"), $command));
2737
			}
2738
		}
2739
	} else {
2740
		if ($is_installed == true) {
2741
			unset($config['cron']['item'][$x]);
2742
			write_config(sprintf(gettext("Removed cron job for %s"), $command));
2743
		}
2744
	}
2745

    
2746
	if ($cron_changed) {
2747
		configure_cron();
2748
	}
2749
}
2750

    
2751
?>
(49-49/65)