Project

General

Profile

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
130
		$radvdconf .= "# Generated for DHCPv6 Server $dhcpv6if\n";
131
		$radvdconf .= "interface {$realif} {\n";
132
		if (strstr($realif, "ovpn")) {
133
			$radvdconf .= "\tUnicastOnly on;\n";
134
		}
135
		$radvdconf .= "\tAdvSendAdvert on;\n";
136
		$radvdconf .= "\tMinRtrAdvInterval 5;\n";
137
		$radvdconf .= "\tMaxRtrAdvInterval 20;\n";
138
		$mtu = get_interface_mtu($realif);
139
		if (is_numeric($mtu)) {
140
			$radvdconf .= "\tAdvLinkMTU {$mtu};\n";
141
		} else {
142
			$radvdconf .= "\tAdvLinkMTU 1280;\n";
143
<<<<<<< HEAD
144
		}
145
		// $radvdconf .= "\tDeprecatePrefix on;\n";
146
		switch ($dhcpv6ifconf['rapriority']) {
147
=======
148

    
149
		switch($dhcpv6ifconf['rapriority']) {
150
>>>>>>> 38253ce... Include additional subnets for RAs in radvd.conf. Ticket #4468
151
			case "low":
152
				$radvdconf .= "\tAdvDefaultPreference low;\n";
153
				break;
154
			case "high":
155
				$radvdconf .= "\tAdvDefaultPreference high;\n";
156
				break;
157
			default:
158
				$radvdconf .= "\tAdvDefaultPreference medium;\n";
159
				break;
160
		}
161
		switch ($dhcpv6ifconf['ramode']) {
162
			case "managed":
163
			case "assist":
164
				$radvdconf .= "\tAdvManagedFlag on;\n";
165
				$radvdconf .= "\tAdvOtherConfigFlag on;\n";
166
				break;
167
		}
168
		$radvdconf .= "\tprefix {$subnetv6}/{$ifcfgsnv6} {\n";
169
		if ($carpif == true) {
170
			$radvdconf .= "\t\tDeprecatePrefix off;\n";
171
		} else {
172
			$radvdconf .= "\t\tDeprecatePrefix on;\n";
173
		}
174
		switch ($dhcpv6ifconf['ramode']) {
175
			case "managed":
176
				$radvdconf .= "\t\tAdvOnLink on;\n";
177
				$radvdconf .= "\t\tAdvAutonomous off;\n";
178
				$radvdconf .= "\t\tAdvRouterAddr on;\n";
179
				break;
180
			case "router":
181
				$radvdconf .= "\t\tAdvOnLink off;\n";
182
				$radvdconf .= "\t\tAdvAutonomous off;\n";
183
				$radvdconf .= "\t\tAdvRouterAddr on;\n";
184
				break;
185
			case "assist":
186
				$radvdconf .= "\t\tAdvOnLink on;\n";
187
				$radvdconf .= "\t\tAdvAutonomous on;\n";
188
				$radvdconf .= "\t\tAdvRouterAddr on;\n";
189
				break;
190
			case "unmanaged":
191
				$radvdconf .= "\t\tAdvOnLink on;\n";
192
				$radvdconf .= "\t\tAdvAutonomous on;\n";
193
				$radvdconf .= "\t\tAdvRouterAddr on;\n";
194
				break;
195
		}
196
		$radvdconf .= "\t};\n";
197

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
520
	$dhcpdconf = <<<EOD
521

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

    
535
EOD;
536

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

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

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

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

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

    
566
		interfaces_staticarp_configure($dhcpif);
567

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

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

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

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

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

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

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

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

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

    
655
		$dnscfg = "";
656

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

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

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

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

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

    
726
		$dhcpdconf .= "subnet {$subnet} netmask {$subnetmask} {\n";
727

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

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

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

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

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

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

    
778
			$pdnscfg = "";
779

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

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

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

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

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

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

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

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

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

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

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

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

    
861
		$dhcpdconf .= <<<EOD
862
$dnscfg
863

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

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

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

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

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

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

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

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

    
929
		$dhcpdconf .= <<<EOD
930
}
931

    
932
EOD;
933

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

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

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

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

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

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

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

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

    
970
				$smdnscfg = "";
971

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

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

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

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

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

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

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

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

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

    
1018
				$dhcpdconf .= "}\n";
1019
				$i++;
1020
			}
1021
		}
1022

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

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

    
1036
		$dhcpdconf .= dhcpdkey($dhcpifconf);
1037
		$dhcpdconf .= dhcpdzones($ddns_zones, $dhcpifconf);
1038
	}
1039

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

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

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

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

    
1063
	if (platform_booting()) {
1064
		print "done.\n";
1065
	}
1066

    
1067
	return 0;
1068
}
1069

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

    
1080
	return $dhcpdconf;
1081
}
1082

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

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

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

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

    
1134
	return $dhcpdconf;
1135
}
1136

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

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

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

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

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

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

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

    
1185

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

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

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

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

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

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

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

    
1253
	$dhcpdv6conf = <<<EOD
1254

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

    
1267
EOD;
1268

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

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

    
1277
	$dhcpdv6ifs = array();
1278

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

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

    
1284
		$ddns_zones = array();
1285

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

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

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

    
1301
		$dnscfgv6 = "";
1302

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

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

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

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

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

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

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

    
1354
EOD;
1355

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1504
	return 0;
1505
}
1506

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

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

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

    
1517
	$iflist = get_configured_interface_list();
1518

    
1519
	$igmpconf = <<<EOD
1520

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

    
1526
EOD;
1527

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

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

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

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

    
1565
	return 0;
1566
}
1567

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

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

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

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

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

    
1594
	$iflist = get_configured_interface_list();
1595

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

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

    
1608
	/*
1609
	 * In order for the relay to work, it needs to be active
1610
	 * on the interface in which the destination server sits.
1611
	 */
1612
	$srvips = explode(",", $dhcrelaycfg['server']);
1613
	foreach ($srvips as $srcidx => $srvip) {
1614
		unset($destif);
1615
		foreach ($iflist as $ifname) {
1616
			$subnet = get_interface_ip($ifname);
1617
			if (!is_ipaddr($subnet)) {
1618
				continue;
1619
			}
1620
			$subnet .=  "/" . get_interface_subnet($ifname);
1621
			if (ip_in_subnet($srvip, $subnet)) {
1622
				$destif = get_real_interface($ifname);
1623
				break;
1624
			}
1625
		}
1626
		if (!isset($destif)) {
1627
			foreach (get_staticroutes() as $rtent) {
1628
				if (ip_in_subnet($srvip, $rtent['network'])) {
1629
					$a_gateways = return_gateways_array(true);
1630
					$destif = $a_gateways[$rtent['gateway']]['interface'];
1631
					break;
1632
				}
1633
			}
1634
		}
1635

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

    
1661
		if (!isset($destif)) {
1662
			if (is_array($config['gateways']['gateway_item'])) {
1663
				foreach ($config['gateways']['gateway_item'] as $gateway) {
1664
					if (isset($gateway['defaultgw'])) {
1665
						$destif = get_real_interface($gateway['interface']);
1666
						break;
1667
					}
1668
				}
1669
			} else {
1670
				$destif = get_real_interface("wan");
1671
			}
1672
		}
1673

    
1674
		if (!empty($destif)) {
1675
			$dhcrelayifs[] = $destif;
1676
		}
1677
	}
1678
	$dhcrelayifs = array_unique($dhcrelayifs);
1679

    
1680
	/* fire up dhcrelay */
1681
	if (empty($dhcrelayifs)) {
1682
		log_error("No suitable interface found for running dhcrelay!");
1683
		return; /* XXX */
1684
	}
1685

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

    
1688
	if (isset($dhcrelaycfg['agentoption'])) {
1689
		$cmd .=  " -a -m replace";
1690
	}
1691

    
1692
	$cmd .= " " . implode(" ", $srvips);
1693
	mwexec($cmd);
1694
	unset($cmd);
1695

    
1696
	return 0;
1697
}
1698

    
1699
function services_dhcrelay6_configure() {
1700
	global $config, $g;
1701
	if ($g['platform'] == 'jail') {
1702
		return;
1703
	}
1704
	if (isset($config['system']['developerspew'])) {
1705
		$mt = microtime();
1706
		echo "services_dhcrelay6_configure() being called $mt\n";
1707
	}
1708

    
1709
	/* kill any running dhcrelay */
1710
	killbypid("{$g['varrun_path']}/dhcrelay6.pid");
1711

    
1712
	$dhcrelaycfg =& $config['dhcrelay6'];
1713

    
1714
	/* DHCPv6 Relay enabled on any interfaces? */
1715
	if (!isset($dhcrelaycfg['enable'])) {
1716
		return 0;
1717
	}
1718

    
1719
	if (platform_booting()) {
1720
		echo gettext("Starting DHCPv6 relay service...");
1721
	} else {
1722
		sleep(1);
1723
	}
1724

    
1725
	$iflist = get_configured_interface_list();
1726

    
1727
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1728
	foreach ($dhcifaces as $dhcrelayif) {
1729
		if (!isset($iflist[$dhcrelayif]) ||
1730
		    link_interface_to_bridge($dhcrelayif))
1731
			continue;
1732

    
1733
		if (is_ipaddrv6(get_interface_ipv6($dhcrelayif))) {
1734
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1735
		}
1736
	}
1737
	$dhcrelayifs = array_unique($dhcrelayifs);
1738

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

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

    
1787
		if (!isset($destif)) {
1788
			if (is_array($config['gateways']['gateway_item'])) {
1789
				foreach ($config['gateways']['gateway_item'] as $gateway) {
1790
					if (isset($gateway['defaultgw'])) {
1791
						$destif = get_real_interface($gateway['interface']);
1792
						break;
1793
					}
1794
				}
1795
			} else {
1796
				$destif = get_real_interface("wan");
1797
			}
1798
		}
1799

    
1800
		if (!empty($destif)) {
1801
			$srvifaces[] = "{$srvip}%{$destif}";
1802
		}
1803
	}
1804

    
1805
	/* fire up dhcrelay */
1806
	if (empty($dhcrelayifs) || empty($srvifaces)) {
1807
		log_error("No suitable interface found for running dhcrelay -6!");
1808
		return; /* XXX */
1809
	}
1810

    
1811
	$cmd = "/usr/local/sbin/dhcrelay -6 -pf \"{$g['varrun_path']}/dhcrelay6.pid\"";
1812
	foreach ($dhcrelayifs as $dhcrelayif) {
1813
		$cmd .= " -l {$dhcrelayif}";
1814
	}
1815
	foreach ($srvifaces as $srviface) {
1816
		$cmd .= " -u \"{$srviface}\"";
1817
	}
1818
	mwexec($cmd);
1819
	unset($cmd);
1820

    
1821
	return 0;
1822
}
1823

    
1824
function services_dyndns_configure_client($conf) {
1825

    
1826
	if (!isset($conf['enable'])) {
1827
		return;
1828
	}
1829

    
1830
	/* load up the dyndns.class */
1831
	require_once("dyndns.class");
1832

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

    
1855
function services_dyndns_configure($int = "") {
1856
	global $config, $g;
1857
	if (isset($config['system']['developerspew'])) {
1858
		$mt = microtime();
1859
		echo "services_dyndns_configure() being called $mt\n";
1860
	}
1861

    
1862
	$dyndnscfg = $config['dyndnses']['dyndns'];
1863
	$gwgroups = return_gateway_groups_array();
1864
	if (is_array($dyndnscfg)) {
1865
		if (platform_booting()) {
1866
			echo gettext("Starting DynDNS clients...");
1867
		}
1868

    
1869
		foreach ($dyndnscfg as $dyndns) {
1870
			if ((empty($int)) || ($int == $dyndns['interface']) || (is_array($gwgroups[$dyndns['interface']]))) {
1871
				$dyndns['verboselog'] = isset($dyndns['verboselog']);
1872
				$dyndns['curl_ipresolve_v4'] = isset($dyndns['curl_ipresolve_v4']);
1873
				$dyndns['curl_ssl_verifypeer'] = isset($dyndns['curl_ssl_verifypeer']);
1874
				services_dyndns_configure_client($dyndns);
1875
				sleep(1);
1876
			}
1877
		}
1878

    
1879
		if (platform_booting()) {
1880
			echo gettext("done.") . "\n";
1881
		}
1882
	}
1883

    
1884
	return 0;
1885
}
1886

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

    
1914
function services_dnsmasq_configure() {
1915
	global $config, $g;
1916
	$return = 0;
1917

    
1918
	// hard coded args: will be removed to avoid duplication if specified in custom_options
1919
	$standard_args = array(
1920
		"dns-forward-max" => "--dns-forward-max=5000",
1921
		"cache-size" => "--cache-size=10000",
1922
		"local-ttl" => "--local-ttl=1"
1923
	);
1924

    
1925

    
1926
	if (isset($config['system']['developerspew'])) {
1927
		$mt = microtime();
1928
		echo "services_dnsmasq_configure() being called $mt\n";
1929
	}
1930

    
1931
	/* kill any running dnsmasq */
1932
	if (file_exists("{$g['varrun_path']}/dnsmasq.pid")) {
1933
		sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM");
1934
	}
1935

    
1936
	if (isset($config['dnsmasq']['enable'])) {
1937

    
1938
		if (platform_booting()) {
1939
			echo gettext("Starting DNS forwarder...");
1940
		} else {
1941
			sleep(1);
1942
		}
1943

    
1944
		/* generate hosts file */
1945
		if (system_hosts_generate()!=0) {
1946
			$return = 1;
1947
		}
1948

    
1949
		$args = "";
1950

    
1951
		if (isset($config['dnsmasq']['regdhcp'])) {
1952
			$args .= " --dhcp-hostsfile={$g['varetc_path']}/hosts ";
1953
		}
1954

    
1955
		/* Setup listen port, if non-default */
1956
		if (is_port($config['dnsmasq']['port'])) {
1957
			$args .= " --port={$config['dnsmasq']['port']} ";
1958
		}
1959

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

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

    
2020
			// Build an array of domain overrides to help in checking for matches.
2021
			$override_a = array();
2022
			if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2023
				foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2024
					$override_a[$override['domain']] = "y";
2025
				}
2026
			}
2027

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

    
2035
			// Set the --server parameter to nowhere for each reverse domain name that was not specifically specified in a domain override.
2036
			foreach ($reverse_domain_a as $reverse_domain) {
2037
				if (!isset($override_a[$reverse_domain])) {
2038
					$args .= " --server=/$reverse_domain/ ";
2039
				}
2040
			}
2041
			unset($override_a);
2042
			unset($reverse_domain_a);
2043
		}
2044

    
2045
		/* Setup forwarded domains */
2046
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2047
			foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2048
				if ($override['ip'] == "!") {
2049
					$override[ip] = "";
2050
				}
2051
				$args .= ' --server=/' . $override['domain'] . '/' . $override['ip'];
2052
			}
2053
		}
2054

    
2055
		/* Allow DNS Rebind for forwarded domains */
2056
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2057
			if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
2058
				foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2059
					$args .= ' --rebind-domain-ok=/' . $override['domain'] . '/ ';
2060
				}
2061
			}
2062
		}
2063

    
2064
		if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
2065
			$dns_rebind = "--rebind-localhost-ok --stop-dns-rebind";
2066
		}
2067

    
2068
		if (isset($config['dnsmasq']['strict_order'])) {
2069
			$args .= " --strict-order ";
2070
		}
2071

    
2072
		if (isset($config['dnsmasq']['domain_needed'])) {
2073
			$args .= " --domain-needed ";
2074
		}
2075

    
2076
		if ($config['dnsmasq']['custom_options']) {
2077
			foreach (preg_split('/\s+/', $config['dnsmasq']['custom_options']) as $c) {
2078
				$args .= " " . escapeshellarg("--{$c}");
2079
				$p = explode('=', $c);
2080
				if (array_key_exists($p[0], $standard_args)) {
2081
					unset($standard_args[$p[0]]);
2082
				}
2083
			}
2084
		}
2085
		$args .= ' ' . implode(' ', array_values($standard_args));
2086

    
2087
		/* run dnsmasq */
2088
		$cmd = "/usr/local/sbin/dnsmasq --all-servers {$dns_rebind} {$args}";
2089
		//log_error("dnsmasq command: {$cmd}");
2090
		mwexec_bg($cmd);
2091
		unset($args);
2092

    
2093
		system_dhcpleases_configure();
2094

    
2095
		if (platform_booting()) {
2096
			echo gettext("done.") . "\n";
2097
		}
2098
	}
2099

    
2100
	if (!platform_booting()) {
2101
		if (services_dhcpd_configure()!=0) {
2102
			$return = 1;
2103
		}
2104
	}
2105

    
2106
	return $return;
2107
}
2108

    
2109
function services_unbound_configure() {
2110
	global $config, $g;
2111
	$return = 0;
2112

    
2113
	if (isset($config['system']['developerspew'])) {
2114
		$mt = microtime();
2115
		echo "services_unbound_configure() being called $mt\n";
2116
	}
2117

    
2118
	// kill any running Unbound instance
2119
	if (file_exists("{$g['varrun_path']}/unbound.pid")) {
2120
		sigkillbypid("{$g['varrun_path']}/unbound.pid", "TERM");
2121
	}
2122

    
2123
	if (isset($config['unbound']['enable'])) {
2124
		if (platform_booting()) {
2125
			echo gettext("Starting DNS Resolver...");
2126
		} else {
2127
			sleep(1);
2128
		}
2129

    
2130
		/* generate hosts file */
2131
		if (system_hosts_generate()!=0) {
2132
			$return = 1;
2133
		}
2134

    
2135
		require_once('/etc/inc/unbound.inc');
2136
		sync_unbound_service();
2137
		if (platform_booting()) {
2138
			echo gettext("done.") . "\n";
2139
		}
2140

    
2141
		system_dhcpleases_configure();
2142
	}
2143

    
2144
	if (!platform_booting()) {
2145
		if (services_dhcpd_configure()!=0) {
2146
			$return = 1;
2147
		}
2148
	}
2149

    
2150
	return $return;
2151
}
2152

    
2153
function services_snmpd_configure() {
2154
	global $config, $g;
2155
	if (isset($config['system']['developerspew'])) {
2156
		$mt = microtime();
2157
		echo "services_snmpd_configure() being called $mt\n";
2158
	}
2159

    
2160
	/* kill any running snmpd */
2161
	sigkillbypid("{$g['varrun_path']}/snmpd.pid", "TERM");
2162
	sleep(2);
2163
	if (is_process_running("bsnmpd")) {
2164
		mwexec("/usr/bin/killall bsnmpd", true);
2165
	}
2166

    
2167
	if (isset($config['snmpd']['enable'])) {
2168

    
2169
		if (platform_booting()) {
2170
			echo gettext("Starting SNMP daemon... ");
2171
		}
2172

    
2173
		/* generate snmpd.conf */
2174
		$fd = fopen("{$g['varetc_path']}/snmpd.conf", "w");
2175
		if (!$fd) {
2176
			printf(gettext("Error: cannot open snmpd.conf in services_snmpd_configure().%s"),"\n");
2177
			return 1;
2178
		}
2179

    
2180

    
2181
		$snmpdconf = <<<EOD
2182
location := "{$config['snmpd']['syslocation']}"
2183
contact := "{$config['snmpd']['syscontact']}"
2184
read := "{$config['snmpd']['rocommunity']}"
2185

    
2186
EOD;
2187

    
2188
/* No docs on what write strings do there for disable for now.
2189
		if (isset($config['snmpd']['rwenable']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2190
			$snmpdconf .= <<<EOD
2191
# write string
2192
write := "{$config['snmpd']['rwcommunity']}"
2193

    
2194
EOD;
2195
		}
2196
*/
2197

    
2198

    
2199
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2200
			$snmpdconf .= <<<EOD
2201
# SNMP Trap support.
2202
traphost := {$config['snmpd']['trapserver']}
2203
trapport := {$config['snmpd']['trapserverport']}
2204
trap := "{$config['snmpd']['trapstring']}"
2205

    
2206

    
2207
EOD;
2208
		}
2209

    
2210
		$version = trim(file_get_contents('/etc/version'));
2211
		$platform = trim(file_get_contents('/etc/platform'));
2212
		if (($platform == "pfSense") && ($g['product_name'] != "pfSense")) {
2213
			$platform = $g['product_name'];
2214
		}
2215
		$sysDescr = "{$g['product_name']} " . php_uname("n") .
2216
			" {$version} {$platform} " . php_uname("s") .
2217
			" " . php_uname("r") . " " . php_uname("m");
2218

    
2219
		$snmpdconf .= <<<EOD
2220
system := 1     # pfSense
2221
%snmpd
2222
sysDescr			= "{$sysDescr}"
2223
begemotSnmpdDebugDumpPdus       = 2
2224
begemotSnmpdDebugSyslogPri      = 7
2225
begemotSnmpdCommunityString.0.1 = $(read)
2226

    
2227
EOD;
2228

    
2229
/* No docs on what write strings do there for disable for now.
2230
		if (isset($config['snmpd']['rwcommunity']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2231
			$snmpdconf .= <<<EOD
2232
begemotSnmpdCommunityString.0.2 = $(write)
2233

    
2234
EOD;
2235
		}
2236
*/
2237

    
2238

    
2239
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2240
			$snmpdconf .= <<<EOD
2241
begemotTrapSinkStatus.[$(traphost)].$(trapport) = 4
2242
begemotTrapSinkVersion.[$(traphost)].$(trapport) = 2
2243
begemotTrapSinkComm.[$(traphost)].$(trapport) = $(trap)
2244

    
2245
EOD;
2246
		}
2247

    
2248

    
2249
		$snmpdconf .= <<<EOD
2250
begemotSnmpdCommunityDisable    = 1
2251

    
2252
EOD;
2253

    
2254
		if (isset($config['snmpd']['bindlan'])) {
2255
			$config['snmpd']['bindip'] = 'lan';
2256
			unset($config['snmpd']['bindlan']);
2257
		}
2258
		$bind_to_ip = "0.0.0.0";
2259
		if (isset($config['snmpd']['bindip'])) {
2260
			if (is_ipaddr($config['snmpd']['bindip'])) {
2261
				$bind_to_ip = $config['snmpd']['bindip'];
2262
			} else {
2263
				$if = get_real_interface($config['snmpd']['bindip']);
2264
				if (does_interface_exist($if)) {
2265
					$bind_to_ip = get_interface_ip($config['snmpd']['bindip']);
2266
				}
2267
			}
2268
		}
2269

    
2270
		if (is_port($config['snmpd']['pollport'])) {
2271
			$snmpdconf .= <<<EOD
2272
begemotSnmpdPortStatus.{$bind_to_ip}.{$config['snmpd']['pollport']} = 1
2273

    
2274
EOD;
2275

    
2276
		}
2277

    
2278
		$snmpdconf .= <<<EOD
2279
begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1
2280
begemotSnmpdLocalPortType."/var/run/snmpd.sock" = 4
2281

    
2282
# These are bsnmp macros not php vars.
2283
sysContact      = $(contact)
2284
sysLocation     = $(location)
2285
sysObjectId     = 1.3.6.1.4.1.12325.1.1.2.1.$(system)
2286

    
2287
snmpEnableAuthenTraps = 2
2288

    
2289
EOD;
2290

    
2291
		if (is_array($config['snmpd']['modules'])) {
2292
			if (isset($config['snmpd']['modules']['mibii'])) {
2293
			$snmpdconf .= <<<EOD
2294
begemotSnmpdModulePath."mibII"  = "/usr/lib/snmp_mibII.so"
2295

    
2296
EOD;
2297
			}
2298

    
2299
			if (isset($config['snmpd']['modules']['netgraph'])) {
2300
				$snmpdconf .= <<<EOD
2301
begemotSnmpdModulePath."netgraph" = "/usr/lib/snmp_netgraph.so"
2302
%netgraph
2303
begemotNgControlNodeName = "snmpd"
2304

    
2305
EOD;
2306
			}
2307

    
2308
			if (isset($config['snmpd']['modules']['pf'])) {
2309
				$snmpdconf .= <<<EOD
2310
begemotSnmpdModulePath."pf"     = "/usr/lib/snmp_pf.so"
2311

    
2312
EOD;
2313
			}
2314

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

    
2324
EOD;
2325
				}
2326
				unset($specplatform);
2327
			}
2328

    
2329
			if (isset($config['snmpd']['modules']['bridge'])) {
2330
				$snmpdconf .= <<<EOD
2331
begemotSnmpdModulePath."bridge"     = "/usr/lib/snmp_bridge.so"
2332
# config must end with blank line
2333

    
2334
EOD;
2335
			}
2336
			if (isset($config['snmpd']['modules']['ucd'])) {
2337
				$snmpdconf .= <<<EOD
2338
begemotSnmpdModulePath."ucd"     = "/usr/local/lib/snmp_ucd.so"
2339

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

    
2346
EOD;
2347
			}
2348
		}
2349

    
2350
		fwrite($fd, $snmpdconf);
2351
		fclose($fd);
2352
		unset($snmpdconf);
2353

    
2354
		if (isset($config['snmpd']['bindlan'])) {
2355
			$bindlan = "";
2356
		}
2357

    
2358
		/* run bsnmpd */
2359
		mwexec("/usr/sbin/bsnmpd -c {$g['varetc_path']}/snmpd.conf" .
2360
			"{$bindlan} -p {$g['varrun_path']}/snmpd.pid");
2361

    
2362
		if (platform_booting()) {
2363
			echo gettext("done.") . "\n";
2364
		}
2365
	}
2366

    
2367
	return 0;
2368
}
2369

    
2370
function services_dnsupdate_process($int = "", $updatehost = "", $forced = false) {
2371
	global $config, $g;
2372
	if (isset($config['system']['developerspew'])) {
2373
		$mt = microtime();
2374
		echo "services_dnsupdate_process() being called $mt\n";
2375
	}
2376

    
2377
	/* Dynamic DNS updating active? */
2378
	if (is_array($config['dnsupdates']['dnsupdate'])) {
2379
		$notify_text = "";
2380
		foreach ($config['dnsupdates']['dnsupdate'] as $i => $dnsupdate) {
2381
			if (!isset($dnsupdate['enable'])) {
2382
				continue;
2383
			}
2384
			if (!empty($int) && $int != $dnsupdate['interface']) {
2385
				continue;
2386
			}
2387
			if (!empty($updatehost) && ($updatehost != $dnsupdate['host'])) {
2388
				continue;
2389
			}
2390

    
2391
			/* determine interface name */
2392
			$if = get_real_interface($dnsupdate['interface']);
2393

    
2394
			if (isset($dnsupdate['usepublicip'])) {
2395
				$wanip = dyndnsCheckIP($dnsupdate['interface']);
2396
			} else {
2397
				$wanip = get_interface_ip($dnsupdate['interface']);
2398
			}
2399

    
2400
			$wanipv6 = get_interface_ipv6($dnsupdate['interface']);
2401
			$cacheFile = "{$g['conf_path']}/dyndns_{$dnsupdate['interface']}_rfc2136_" . escapeshellarg($dnsupdate['host']) . "_{$dnsupdate['server']}.cache";
2402
			$currentTime = time();
2403

    
2404
			if ($wanip || $wanipv6) {
2405
				$keyname = $dnsupdate['keyname'];
2406
				/* trailing dot */
2407
				if (substr($keyname, -1) != ".") {
2408
					$keyname .= ".";
2409
				}
2410

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

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

    
2426
EOD;
2427
				fwrite($fd, $privkey);
2428
				fclose($fd);
2429

    
2430
				/* write public key file */
2431
				if ($dnsupdate['keytype'] == "zone") {
2432
					$flags = 257;
2433
					$proto = 3;
2434
				} else if ($dnsupdate['keytype'] == "host") {
2435
					$flags = 513;
2436
					$proto = 3;
2437
				} else if ($dnsupdate['keytype'] == "user") {
2438
					$flags = 0;
2439
					$proto = 2;
2440
				}
2441

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

    
2446
				/* generate update instructions */
2447
				$upinst = "";
2448
				if (!empty($dnsupdate['server'])) {
2449
					$upinst .= "server {$dnsupdate['server']}\n";
2450
				}
2451

    
2452
				if (file_exists($cacheFile)) {
2453
					list($cachedipv4, $cacheTimev4) = explode("|", file_get_contents($cacheFile));
2454
				}
2455
				if (file_exists("{$cacheFile}.ipv6")) {
2456
					list($cachedipv6, $cacheTimev6) = explode("|", file_get_contents("{$cacheFile}.ipv6"));
2457
				}
2458

    
2459
				// 25 Days
2460
				$maxCacheAgeSecs = 25 * 24 * 60 * 60;
2461
				$need_update = false;
2462

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

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

    
2497
				$upinst .= "\n";	/* mind that trailing newline! */
2498

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

    
2518
	return 0;
2519
}
2520

    
2521
/* configure cron service */
2522
function configure_cron() {
2523
	global $g, $config;
2524

    
2525
	conf_mount_rw();
2526
	/* preserve existing crontab entries */
2527
	$crontab_contents = file("/etc/crontab", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2528

    
2529
	for ($i = 0; $i < count($crontab_contents); $i++) {
2530
		$cron_item =& $crontab_contents[$i];
2531
		if (strpos($cron_item, "# pfSense specific crontab entries") !== false) {
2532
			array_splice($crontab_contents, $i - 1);
2533
			break;
2534
		}
2535
	}
2536
	$crontab_contents = implode("\n", $crontab_contents) . "\n";
2537

    
2538

    
2539
	if (is_array($config['cron']['item'])) {
2540
		$crontab_contents .= "#\n";
2541
		$crontab_contents .= "# " . gettext("pfSense specific crontab entries") . "\n";
2542
		$crontab_contents .= "# " .gettext("Created:") . " " . date("F j, Y, g:i a") . "\n";
2543
		$crontab_contents .= "#\n";
2544

    
2545
		if (isset($config['system']['proxyurl']) && !empty($config['system']['proxyurl'])) {
2546
			$http_proxy = $config['system']['proxyurl'];
2547
			if (isset($config['system']['proxyport']) && !empty($config['system']['proxyport'])) {
2548
				$http_proxy .= ':' . $config['system']['proxyport'];
2549
			}
2550
			$crontab_contents .= "HTTP_PROXY={$http_proxy}";
2551
		}
2552

    
2553
		foreach ($config['cron']['item'] as $item) {
2554
			$crontab_contents .= "\n{$item['minute']}\t";
2555
			$crontab_contents .= "{$item['hour']}\t";
2556
			$crontab_contents .= "{$item['mday']}\t";
2557
			$crontab_contents .= "{$item['month']}\t";
2558
			$crontab_contents .= "{$item['wday']}\t";
2559
			$crontab_contents .= "{$item['who']}\t";
2560
			$crontab_contents .= "{$item['command']}";
2561
		}
2562

    
2563
		$crontab_contents .= "\n#\n";
2564
		$crontab_contents .= "# " . gettext("If possible do not add items to this file manually.") . "\n";
2565
		$crontab_contents .= "# " . gettext("If you do so, this file must be terminated with a blank line (e.g. new line)") . "\n";
2566
		$crontab_contents .= "#\n\n";
2567
	}
2568

    
2569
	/* please maintain the newline at the end of file */
2570
	file_put_contents("/etc/crontab", $crontab_contents);
2571
	unset($crontab_contents);
2572

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

    
2576
	conf_mount_ro();
2577
}
2578

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

    
2603
function upnp_start() {
2604
	global $config;
2605

    
2606
	if (!isset($config['installedpackages']['miniupnpd']['config'])) {
2607
		return;
2608
	}
2609

    
2610
	if ($config['installedpackages']['miniupnpd']['config'][0]['enable']) {
2611
		echo gettext("Starting UPnP service... ");
2612
		require_once('/usr/local/pkg/miniupnpd.inc');
2613
		sync_package_miniupnpd();
2614
		echo "done.\n";
2615
	}
2616
}
2617

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

    
2621
	$is_installed = false;
2622
	$cron_changed = true;
2623

    
2624
	if (!is_array($config['cron'])) {
2625
		$config['cron'] = array();
2626
	}
2627
	if (!is_array($config['cron']['item'])) {
2628
		$config['cron']['item'] = array();
2629
	}
2630

    
2631
	$x=0;
2632
	foreach ($config['cron']['item'] as $item) {
2633
		if (strstr($item['command'], $command)) {
2634
			$is_installed = true;
2635
			break;
2636
		}
2637
		$x++;
2638
	}
2639

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

    
2668
	if ($cron_changed) {
2669
		configure_cron();
2670
	}
2671
}
2672

    
2673
?>
(49-49/67)