Project

General

Profile

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

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

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

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

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

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

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

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

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

    
48
	if (isset($config['system']['developerspew'])) {
49
		$mt = microtime();
50
		echo "services_radvd_configure() being called $mt\n";
51
	}
52

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
516
	$dhcpdconf = <<<EOD
517

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

    
531
EOD;
532

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

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

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

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

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

    
562
		interfaces_staticarp_configure($dhcpif);
563

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

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

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

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

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

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

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

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

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

    
651
		$dnscfg = "";
652

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

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

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

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

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

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

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

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

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

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

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

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

    
803
			$pdnscfg = "";
804

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
959
EOD;
960

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

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

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

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

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

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

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

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

    
997
				$smdnscfg = "";
998

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1094
	return 0;
1095
}
1096

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

    
1106
	return $dhcpdconf;
1107
}
1108

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

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

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

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

    
1159
	return $dhcpdconf;
1160
}
1161

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

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

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

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

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

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

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

    
1210

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

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

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

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

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

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

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

    
1278
	$dhcpdv6conf = <<<EOD
1279

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

    
1292
EOD;
1293

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

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

    
1302
	$dhcpdv6ifs = array();
1303

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

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

    
1309
		$ddns_zones = array();
1310

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

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

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

    
1326
		$dnscfgv6 = "";
1327

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

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

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

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

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

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

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

    
1379
EOD;
1380

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1519
	/* fire up dhcpd in a chroot */
1520
	if (count($dhcpdv6ifs) > 0) {
1521
		mwexec("/usr/local/sbin/dhcpd -6 -user dhcpd -group _dhcp -chroot {$g['dhcpd_chroot_path']} -cf /etc/dhcpdv6.conf -pf {$g['varrun_path']}/dhcpdv6.pid " .
1522
			join(" ", $dhcpdv6ifs));
1523
		mwexec("/usr/local/sbin/dhcpleases6 -c \"/usr/local/bin/php-cgi -f /usr/local/sbin/prefixes.php|/bin/sh\" -l {$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases");
1524
	}
1525
	if (platform_booting()) {
1526
		print gettext("done.") . "\n";
1527
	}
1528

    
1529
	return 0;
1530
}
1531

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

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

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

    
1542
	$iflist = get_configured_interface_list();
1543

    
1544
	$igmpconf = <<<EOD
1545

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

    
1551
EOD;
1552

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

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

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

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

    
1590
	return 0;
1591
}
1592

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

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

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

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

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

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

    
1617
	$iflist = get_configured_interface_list();
1618

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

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

    
1631
	$srvips = explode(",", $dhcrelaycfg['server']);
1632
	if (!is_array($srvips)) {
1633
		log_error("No destination IP has been configured!");
1634
		return;
1635
	}
1636

    
1637
	$dhcrelayifs = array_unique($dhcrelayifs);
1638

    
1639
	/* fire up dhcrelay */
1640
	if (empty($dhcrelayifs)) {
1641
		log_error("No suitable interface found for running dhcrelay!");
1642
		return; /* XXX */
1643
	}
1644

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

    
1647
	if (isset($dhcrelaycfg['agentoption'])) {
1648
		$cmd .= " -a -m replace";
1649
	}
1650

    
1651
	$cmd .= " " . implode(" ", $srvips);
1652
	mwexec($cmd);
1653
	unset($cmd);
1654

    
1655
	return 0;
1656
}
1657

    
1658
function services_dhcrelay6_configure() {
1659
	global $config, $g;
1660

    
1661
	if (isset($config['system']['developerspew'])) {
1662
		$mt = microtime();
1663
		echo "services_dhcrelay6_configure() being called $mt\n";
1664
	}
1665

    
1666
	/* kill any running dhcrelay */
1667
	killbypid("{$g['varrun_path']}/dhcrelay6.pid");
1668

    
1669
	$dhcrelaycfg =& $config['dhcrelay6'];
1670

    
1671
	/* DHCPv6 Relay enabled on any interfaces? */
1672
	if (!isset($dhcrelaycfg['enable'])) {
1673
		return 0;
1674
	}
1675

    
1676
	if (platform_booting()) {
1677
		echo gettext("Starting DHCPv6 relay service...");
1678
	} else {
1679
		sleep(1);
1680
	}
1681

    
1682
	$iflist = get_configured_interface_list();
1683

    
1684
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1685
	foreach ($dhcifaces as $dhcrelayif) {
1686
		if (!isset($iflist[$dhcrelayif]) ||
1687
		    link_interface_to_bridge($dhcrelayif)) {
1688
			continue;
1689
		}
1690

    
1691
		if (is_ipaddrv6(get_interface_ipv6($dhcrelayif))) {
1692
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1693
		}
1694
	}
1695
	$dhcrelayifs = array_unique($dhcrelayifs);
1696

    
1697
	$srvips = explode(",", $dhcrelaycfg['server']);
1698
	if (!is_array($srvips)) {
1699
		log_error("No destination IP has been configured!");
1700
		return;
1701
	}
1702

    
1703
	/* fire up dhcrelay */
1704
	if (empty($dhcrelayifs) || empty($srvifaces)) {
1705
		log_error("No suitable interface found for running dhcrelay -6!");
1706
		return; /* XXX */
1707
	}
1708

    
1709
	$cmd = "/usr/local/sbin/dhcrelay -6 -pf \"{$g['varrun_path']}/dhcrelay6.pid\"";
1710
	foreach ($dhcrelayifs as $dhcrelayif) {
1711
		$cmd .= " -l {$dhcrelayif}";
1712
	}
1713
	foreach ($srvifaces as $srviface) {
1714
		$cmd .= " -u \"{$srviface}\"";
1715
	}
1716
	mwexec($cmd);
1717
	unset($cmd);
1718

    
1719
	return 0;
1720
}
1721

    
1722
function services_dyndns_configure_client($conf) {
1723

    
1724
	if (!isset($conf['enable'])) {
1725
		return;
1726
	}
1727

    
1728
	/* load up the dyndns.class */
1729
	require_once("dyndns.class");
1730

    
1731
	$dns = new updatedns($dnsService = $conf['type'],
1732
		$dnsHost = $conf['host'],
1733
		$dnsUser = $conf['username'],
1734
		$dnsPass = $conf['password'],
1735
		$dnsWildcard = $conf['wildcard'],
1736
		$dnsMX = $conf['mx'],
1737
		$dnsIf = "{$conf['interface']}",
1738
		$dnsBackMX = NULL,
1739
		$dnsServer = NULL,
1740
		$dnsPort = NULL,
1741
		$dnsUpdateURL = "{$conf['updateurl']}",
1742
		$forceUpdate = $conf['force'],
1743
		$dnsZoneID = $conf['zoneid'],
1744
		$dnsTTL = $conf['ttl'],
1745
		$dnsResultMatch = "{$conf['resultmatch']}",
1746
		$dnsRequestIf = "{$conf['requestif']}",
1747
		$dnsID = "{$conf['id']}",
1748
		$dnsVerboseLog = $conf['verboselog'],
1749
		$curlIpresolveV4 = $conf['curl_ipresolve_v4'],
1750
		$curlSslVerifypeer = $conf['curl_ssl_verifypeer']);
1751
}
1752

    
1753
function services_dyndns_configure($int = "") {
1754
	global $config, $g;
1755
	if (isset($config['system']['developerspew'])) {
1756
		$mt = microtime();
1757
		echo "services_dyndns_configure() being called $mt\n";
1758
	}
1759

    
1760
	$dyndnscfg = $config['dyndnses']['dyndns'];
1761
	$gwgroups = return_gateway_groups_array();
1762
	if (is_array($dyndnscfg)) {
1763
		if (platform_booting()) {
1764
			echo gettext("Starting DynDNS clients...");
1765
		}
1766

    
1767
		foreach ($dyndnscfg as $dyndns) {
1768
			if ((empty($int)) || ($int == $dyndns['interface']) || (is_array($gwgroups[$dyndns['interface']]))) {
1769
				$dyndns['verboselog'] = isset($dyndns['verboselog']);
1770
				$dyndns['curl_ipresolve_v4'] = isset($dyndns['curl_ipresolve_v4']);
1771
				$dyndns['curl_ssl_verifypeer'] = isset($dyndns['curl_ssl_verifypeer']);
1772
				services_dyndns_configure_client($dyndns);
1773
				sleep(1);
1774
			}
1775
		}
1776

    
1777
		if (platform_booting()) {
1778
			echo gettext("done.") . "\n";
1779
		}
1780
	}
1781

    
1782
	return 0;
1783
}
1784

    
1785
function dyndnsCheckIP($int) {
1786
	global $config;
1787
	$ip_address = get_interface_ip($int);
1788
	if (is_private_ip($ip_address)) {
1789
		$gateways_status = return_gateways_status(true);
1790
		// If the gateway for this interface is down, then the external check cannot work.
1791
		// Avoid the long wait for the external check to timeout.
1792
		if (stristr($gateways_status[$config['interfaces'][$int]['gateway']]['status'], "down")) {
1793
			return "down";
1794
		}
1795
		$hosttocheck = "http://checkip.dyndns.org";
1796
		$ip_ch = curl_init($hosttocheck);
1797
		curl_setopt($ip_ch, CURLOPT_RETURNTRANSFER, 1);
1798
		curl_setopt($ip_ch, CURLOPT_SSL_VERIFYPEER, FALSE);
1799
		curl_setopt($ip_ch, CURLOPT_INTERFACE, 'host!' . $ip_address);
1800
		curl_setopt($ip_ch, CURLOPT_CONNECTTIMEOUT, '30');
1801
		curl_setopt($ip_ch, CURLOPT_TIMEOUT, 120);
1802
		curl_setopt($ip_ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1803
		$ip_result_page = curl_exec($ip_ch);
1804
		curl_close($ip_ch);
1805
		$ip_result_decoded = urldecode($ip_result_page);
1806
		preg_match('=Current IP Address: (.*)</body>=siU', $ip_result_decoded, $matches);
1807
		$ip_address = trim($matches[1]);
1808
	}
1809
	return $ip_address;
1810
}
1811

    
1812
function services_dnsmasq_configure() {
1813
	global $config, $g;
1814
	$return = 0;
1815

    
1816
	// hard coded args: will be removed to avoid duplication if specified in custom_options
1817
	$standard_args = array(
1818
		"dns-forward-max" => "--dns-forward-max=5000",
1819
		"cache-size" => "--cache-size=10000",
1820
		"local-ttl" => "--local-ttl=1"
1821
	);
1822

    
1823

    
1824
	if (isset($config['system']['developerspew'])) {
1825
		$mt = microtime();
1826
		echo "services_dnsmasq_configure() being called $mt\n";
1827
	}
1828

    
1829
	/* kill any running dnsmasq */
1830
	if (file_exists("{$g['varrun_path']}/dnsmasq.pid")) {
1831
		sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM");
1832
	}
1833

    
1834
	if (isset($config['dnsmasq']['enable'])) {
1835

    
1836
		if (platform_booting()) {
1837
			echo gettext("Starting DNS forwarder...");
1838
		} else {
1839
			sleep(1);
1840
		}
1841

    
1842
		/* generate hosts file */
1843
		if (system_hosts_generate() != 0) {
1844
			$return = 1;
1845
		}
1846

    
1847
		$args = "";
1848

    
1849
		if (isset($config['dnsmasq']['regdhcp'])) {
1850
			$args .= " --dhcp-hostsfile={$g['varetc_path']}/hosts ";
1851
		}
1852

    
1853
		/* Setup listen port, if non-default */
1854
		if (is_port($config['dnsmasq']['port'])) {
1855
			$args .= " --port={$config['dnsmasq']['port']} ";
1856
		}
1857

    
1858
		$listen_addresses = "";
1859
		if (isset($config['dnsmasq']['interface'])) {
1860
			$interfaces = explode(",", $config['dnsmasq']['interface']);
1861
			foreach ($interfaces as $interface) {
1862
				if (is_ipaddrv4($interface)) {
1863
					$listen_addresses .= " --listen-address={$interface} ";
1864
				} else if (is_ipaddrv6($interface)) {
1865
					/*
1866
					 * XXX: Since dnsmasq does not support link-local address
1867
					 * with scope specified. These checks are being done.
1868
					 */
1869
					if (is_linklocal($interface) && strstr($interface, "%")) {
1870
						$tmpaddrll6 = explode("%", $interface);
1871
						$listen_addresses .= " --listen-address={$tmpaddrll6[0]} ";
1872
					} else {
1873
						$listen_addresses .= " --listen-address={$interface} ";
1874
					}
1875
				} else if (strstr($interface, "_vip")) {
1876
					$laddr = get_configured_carp_interface_list($interface);
1877
					if (is_ipaddr($laddr)) {
1878
						$listen_addresses .= " --listen-address={$laddr} ";
1879
					}
1880
				} else {
1881
					$if = get_real_interface($interface);
1882
					if (does_interface_exist($if)) {
1883
						$laddr = get_interface_ip($interface);
1884
						if (is_ipaddrv4($laddr)) {
1885
							$listen_addresses .= " --listen-address={$laddr} ";
1886
						}
1887
						$laddr6 = get_interface_ipv6($interface);
1888
						if (is_ipaddrv6($laddr6) && !isset($config['dnsmasq']['strictbind'])) {
1889
							/*
1890
							 * XXX: Since dnsmasq does not support link-local address
1891
							 * with scope specified. These checks are being done.
1892
							 */
1893
							if (is_linklocal($laddr6) && strstr($laddr6, "%")) {
1894
								$tmpaddrll6 = explode("%", $laddr6);
1895
								$listen_addresses .= " --listen-address={$tmpaddrll6[0]} ";
1896
							} else {
1897
								$listen_addresses .= " --listen-address={$laddr6} ";
1898
							}
1899
						}
1900
					}
1901
				}
1902
			}
1903
			if (!empty($listen_addresses)) {
1904
				$args .= " {$listen_addresses} ";
1905
				if (isset($config['dnsmasq']['strictbind'])) {
1906
					$args .= " --bind-interfaces ";
1907
				}
1908
			}
1909
		}
1910

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

    
1918
			// Build an array of domain overrides to help in checking for matches.
1919
			$override_a = array();
1920
			if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
1921
				foreach ($config['dnsmasq']['domainoverrides'] as $override) {
1922
					$override_a[$override['domain']] = "y";
1923
				}
1924
			}
1925

    
1926
			// Build an array of the private reverse lookup domain names
1927
			$reverse_domain_a = array("10.in-addr.arpa", "168.192.in-addr.arpa");
1928
			// Unfortunately the 172.16.0.0/12 range does not map nicely to the in-addr.arpa scheme.
1929
			for ($subnet_num = 16; $subnet_num < 32; $subnet_num++) {
1930
				$reverse_domain_a[] = "$subnet_num.172.in-addr.arpa";
1931
			}
1932

    
1933
			// Set the --server parameter to nowhere for each reverse domain name that was not specifically specified in a domain override.
1934
			foreach ($reverse_domain_a as $reverse_domain) {
1935
				if (!isset($override_a[$reverse_domain])) {
1936
					$args .= " --server=/$reverse_domain/ ";
1937
				}
1938
			}
1939
			unset($override_a);
1940
			unset($reverse_domain_a);
1941
		}
1942

    
1943
		/* Setup forwarded domains */
1944
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
1945
			foreach ($config['dnsmasq']['domainoverrides'] as $override) {
1946
				if ($override['ip'] == "!") {
1947
					$override[ip] = "";
1948
				}
1949
				$args .= ' --server=/' . $override['domain'] . '/' . $override['ip'];
1950
			}
1951
		}
1952

    
1953
		/* Allow DNS Rebind for forwarded domains */
1954
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
1955
			if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
1956
				foreach ($config['dnsmasq']['domainoverrides'] as $override) {
1957
					$args .= ' --rebind-domain-ok=/' . $override['domain'] . '/ ';
1958
				}
1959
			}
1960
		}
1961

    
1962
		if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
1963
			$dns_rebind = "--rebind-localhost-ok --stop-dns-rebind";
1964
		}
1965

    
1966
		if (isset($config['dnsmasq']['strict_order'])) {
1967
			$args .= " --strict-order ";
1968
		}
1969

    
1970
		if (isset($config['dnsmasq']['domain_needed'])) {
1971
			$args .= " --domain-needed ";
1972
		}
1973

    
1974
		if ($config['dnsmasq']['custom_options']) {
1975
			foreach (preg_split('/\s+/', $config['dnsmasq']['custom_options']) as $c) {
1976
				$args .= " " . escapeshellarg("--{$c}");
1977
				$p = explode('=', $c);
1978
				if (array_key_exists($p[0], $standard_args)) {
1979
					unset($standard_args[$p[0]]);
1980
				}
1981
			}
1982
		}
1983
		$args .= ' ' . implode(' ', array_values($standard_args));
1984

    
1985
		/* run dnsmasq */
1986
		$cmd = "/usr/local/sbin/dnsmasq --all-servers {$dns_rebind} {$args}";
1987
		//log_error("dnsmasq command: {$cmd}");
1988
		mwexec_bg($cmd);
1989
		unset($args);
1990

    
1991
		system_dhcpleases_configure();
1992

    
1993
		if (platform_booting()) {
1994
			echo gettext("done.") . "\n";
1995
		}
1996
	}
1997

    
1998
	if (!platform_booting()) {
1999
		if (services_dhcpd_configure() != 0) {
2000
			$return = 1;
2001
		}
2002
	}
2003

    
2004
	return $return;
2005
}
2006

    
2007
function services_unbound_configure() {
2008
	global $config, $g;
2009
	$return = 0;
2010

    
2011
	if (isset($config['system']['developerspew'])) {
2012
		$mt = microtime();
2013
		echo "services_unbound_configure() being called $mt\n";
2014
	}
2015

    
2016
	// kill any running Unbound instance
2017
	if (file_exists("{$g['varrun_path']}/unbound.pid")) {
2018
		sigkillbypid("{$g['varrun_path']}/unbound.pid", "TERM");
2019
	}
2020

    
2021
	if (isset($config['unbound']['enable'])) {
2022
		if (platform_booting()) {
2023
			echo gettext("Starting DNS Resolver...");
2024
		} else {
2025
			sleep(1);
2026
		}
2027

    
2028
		/* generate hosts file */
2029
		if (system_hosts_generate() != 0) {
2030
			$return = 1;
2031
		}
2032

    
2033
		require_once('/etc/inc/unbound.inc');
2034
		sync_unbound_service();
2035
		if (platform_booting()) {
2036
			echo gettext("done.") . "\n";
2037
		}
2038

    
2039
		system_dhcpleases_configure();
2040
	}
2041

    
2042
	if (!platform_booting()) {
2043
		if (services_dhcpd_configure() != 0) {
2044
			$return = 1;
2045
		}
2046
	}
2047

    
2048
	return $return;
2049
}
2050

    
2051
function services_snmpd_configure() {
2052
	global $config, $g;
2053
	if (isset($config['system']['developerspew'])) {
2054
		$mt = microtime();
2055
		echo "services_snmpd_configure() being called $mt\n";
2056
	}
2057

    
2058
	/* kill any running snmpd */
2059
	sigkillbypid("{$g['varrun_path']}/snmpd.pid", "TERM");
2060
	sleep(2);
2061
	if (is_process_running("bsnmpd")) {
2062
		mwexec("/usr/bin/killall bsnmpd", true);
2063
	}
2064

    
2065
	if (isset($config['snmpd']['enable'])) {
2066

    
2067
		if (platform_booting()) {
2068
			echo gettext("Starting SNMP daemon... ");
2069
		}
2070

    
2071
		/* generate snmpd.conf */
2072
		$fd = fopen("{$g['varetc_path']}/snmpd.conf", "w");
2073
		if (!$fd) {
2074
			printf(gettext("Error: cannot open snmpd.conf in services_snmpd_configure().%s"), "\n");
2075
			return 1;
2076
		}
2077

    
2078

    
2079
		$snmpdconf = <<<EOD
2080
location := "{$config['snmpd']['syslocation']}"
2081
contact := "{$config['snmpd']['syscontact']}"
2082
read := "{$config['snmpd']['rocommunity']}"
2083

    
2084
EOD;
2085

    
2086
/* No docs on what write strings do there for disable for now.
2087
		if (isset($config['snmpd']['rwenable']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2088
			$snmpdconf .= <<<EOD
2089
# write string
2090
write := "{$config['snmpd']['rwcommunity']}"
2091

    
2092
EOD;
2093
		}
2094
*/
2095

    
2096

    
2097
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2098
			$snmpdconf .= <<<EOD
2099
# SNMP Trap support.
2100
traphost := {$config['snmpd']['trapserver']}
2101
trapport := {$config['snmpd']['trapserverport']}
2102
trap := "{$config['snmpd']['trapstring']}"
2103

    
2104

    
2105
EOD;
2106
		}
2107

    
2108
		$platform = trim(file_get_contents('/etc/platform'));
2109
		if (($platform == "pfSense") && ($g['product_name'] != "pfSense")) {
2110
			$platform = $g['product_name'];
2111
		}
2112
		$sysDescr = "{$g['product_name']} " . php_uname("n") .
2113
			" {$g['product_version']} {$platform} " . php_uname("s") .
2114
			" " . php_uname("r") . " " . php_uname("m");
2115

    
2116
		$snmpdconf .= <<<EOD
2117
system := 1     # pfSense
2118
%snmpd
2119
sysDescr			= "{$sysDescr}"
2120
begemotSnmpdDebugDumpPdus       = 2
2121
begemotSnmpdDebugSyslogPri      = 7
2122
begemotSnmpdCommunityString.0.1 = $(read)
2123

    
2124
EOD;
2125

    
2126
/* No docs on what write strings do there for disable for now.
2127
		if (isset($config['snmpd']['rwcommunity']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2128
			$snmpdconf .= <<<EOD
2129
begemotSnmpdCommunityString.0.2 = $(write)
2130

    
2131
EOD;
2132
		}
2133
*/
2134

    
2135

    
2136
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2137
			$snmpdconf .= <<<EOD
2138
begemotTrapSinkStatus.[$(traphost)].$(trapport) = 4
2139
begemotTrapSinkVersion.[$(traphost)].$(trapport) = 2
2140
begemotTrapSinkComm.[$(traphost)].$(trapport) = $(trap)
2141

    
2142
EOD;
2143
		}
2144

    
2145

    
2146
		$snmpdconf .= <<<EOD
2147
begemotSnmpdCommunityDisable    = 1
2148

    
2149
EOD;
2150

    
2151
		if (isset($config['snmpd']['bindlan'])) {
2152
			$config['snmpd']['bindip'] = 'lan';
2153
			unset($config['snmpd']['bindlan']);
2154
		}
2155
		$bind_to_ip = "0.0.0.0";
2156
		if (isset($config['snmpd']['bindip'])) {
2157
			if (is_ipaddr($config['snmpd']['bindip'])) {
2158
				$bind_to_ip = $config['snmpd']['bindip'];
2159
			} else {
2160
				$if = get_real_interface($config['snmpd']['bindip']);
2161
				if (does_interface_exist($if)) {
2162
					$bind_to_ip = get_interface_ip($config['snmpd']['bindip']);
2163
				}
2164
			}
2165
		}
2166

    
2167
		if (is_port($config['snmpd']['pollport'])) {
2168
			$snmpdconf .= <<<EOD
2169
begemotSnmpdPortStatus.{$bind_to_ip}.{$config['snmpd']['pollport']} = 1
2170

    
2171
EOD;
2172

    
2173
		}
2174

    
2175
		$snmpdconf .= <<<EOD
2176
begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1
2177
begemotSnmpdLocalPortType."/var/run/snmpd.sock" = 4
2178

    
2179
# These are bsnmp macros not php vars.
2180
sysContact      = $(contact)
2181
sysLocation     = $(location)
2182
sysObjectId     = 1.3.6.1.4.1.12325.1.1.2.1.$(system)
2183

    
2184
snmpEnableAuthenTraps = 2
2185

    
2186
EOD;
2187

    
2188
		if (is_array($config['snmpd']['modules'])) {
2189
			if (isset($config['snmpd']['modules']['mibii'])) {
2190
			$snmpdconf .= <<<EOD
2191
begemotSnmpdModulePath."mibII"  = "/usr/lib/snmp_mibII.so"
2192

    
2193
EOD;
2194
			}
2195

    
2196
			if (isset($config['snmpd']['modules']['netgraph'])) {
2197
				$snmpdconf .= <<<EOD
2198
begemotSnmpdModulePath."netgraph" = "/usr/lib/snmp_netgraph.so"
2199
%netgraph
2200
begemotNgControlNodeName = "snmpd"
2201

    
2202
EOD;
2203
			}
2204

    
2205
			if (isset($config['snmpd']['modules']['pf'])) {
2206
				$snmpdconf .= <<<EOD
2207
begemotSnmpdModulePath."pf"     = "/usr/lib/snmp_pf.so"
2208

    
2209
EOD;
2210
			}
2211

    
2212
			if (isset($config['snmpd']['modules']['hostres'])) {
2213
				/* XXX: hostres module crashes APU - ticket #4403 */
2214
				$specplatform = system_identify_specific_platform();
2215
				if ($specplatform['name'] == 'APU') {
2216
					log_error("'Host Resources' SNMP module was ignored because it can potentially crash system on APU boards");
2217
				} else {
2218
					$snmpdconf .= <<<EOD
2219
begemotSnmpdModulePath."hostres"     = "/usr/lib/snmp_hostres.so"
2220

    
2221
EOD;
2222
				}
2223
				unset($specplatform);
2224
			}
2225

    
2226
			if (isset($config['snmpd']['modules']['bridge'])) {
2227
				$snmpdconf .= <<<EOD
2228
begemotSnmpdModulePath."bridge"     = "/usr/lib/snmp_bridge.so"
2229
# config must end with blank line
2230

    
2231
EOD;
2232
			}
2233
			if (isset($config['snmpd']['modules']['ucd'])) {
2234
				$snmpdconf .= <<<EOD
2235
begemotSnmpdModulePath."ucd"     = "/usr/local/lib/snmp_ucd.so"
2236

    
2237
EOD;
2238
			}
2239
			if (isset($config['snmpd']['modules']['regex'])) {
2240
				$snmpdconf .= <<<EOD
2241
begemotSnmpdModulePath."regex"     = "/usr/local/lib/snmp_regex.so"
2242

    
2243
EOD;
2244
			}
2245
		}
2246

    
2247
		fwrite($fd, $snmpdconf);
2248
		fclose($fd);
2249
		unset($snmpdconf);
2250

    
2251
		if (isset($config['snmpd']['bindlan'])) {
2252
			$bindlan = "";
2253
		}
2254

    
2255
		/* run bsnmpd */
2256
		mwexec("/usr/sbin/bsnmpd -c {$g['varetc_path']}/snmpd.conf" .
2257
			"{$bindlan} -p {$g['varrun_path']}/snmpd.pid");
2258

    
2259
		if (platform_booting()) {
2260
			echo gettext("done.") . "\n";
2261
		}
2262
	}
2263

    
2264
	return 0;
2265
}
2266

    
2267
function services_dnsupdate_process($int = "", $updatehost = "", $forced = false) {
2268
	global $config, $g;
2269
	if (isset($config['system']['developerspew'])) {
2270
		$mt = microtime();
2271
		echo "services_dnsupdate_process() being called $mt\n";
2272
	}
2273

    
2274
	/* Dynamic DNS updating active? */
2275
	if (is_array($config['dnsupdates']['dnsupdate'])) {
2276
		$notify_text = "";
2277
		foreach ($config['dnsupdates']['dnsupdate'] as $i => $dnsupdate) {
2278
			if (!isset($dnsupdate['enable'])) {
2279
				continue;
2280
			}
2281
			if (!empty($int) && $int != $dnsupdate['interface']) {
2282
				continue;
2283
			}
2284
			if (!empty($updatehost) && ($updatehost != $dnsupdate['host'])) {
2285
				continue;
2286
			}
2287

    
2288
			/* determine interface name */
2289
			$if = get_real_interface($dnsupdate['interface']);
2290

    
2291
			if (isset($dnsupdate['usepublicip'])) {
2292
				$wanip = dyndnsCheckIP($dnsupdate['interface']);
2293
			} else {
2294
				$wanip = get_interface_ip($dnsupdate['interface']);
2295
			}
2296

    
2297
			$wanipv6 = get_interface_ipv6($dnsupdate['interface']);
2298
			$cacheFile = "{$g['conf_path']}/dyndns_{$dnsupdate['interface']}_rfc2136_" . escapeshellarg($dnsupdate['host']) . "_{$dnsupdate['server']}.cache";
2299
			$currentTime = time();
2300

    
2301
			if ($wanip || $wanipv6) {
2302
				$keyname = $dnsupdate['keyname'];
2303
				/* trailing dot */
2304
				if (substr($keyname, -1) != ".") {
2305
					$keyname .= ".";
2306
				}
2307

    
2308
				$hostname = $dnsupdate['host'];
2309
				/* trailing dot */
2310
				if (substr($hostname, -1) != ".") {
2311
					$hostname .= ".";
2312
				}
2313

    
2314
				/* write private key file
2315
				   this is dumb - public and private keys are the same for HMAC-MD5,
2316
				   but nsupdate insists on having both */
2317
				$fd = fopen("{$g['varetc_path']}/K{$i}{$keyname}+157+00000.private", "w");
2318
				$privkey = <<<EOD
2319
Private-key-format: v1.2
2320
Algorithm: 157 (HMAC)
2321
Key: {$dnsupdate['keydata']}
2322

    
2323
EOD;
2324
				fwrite($fd, $privkey);
2325
				fclose($fd);
2326

    
2327
				/* write public key file */
2328
				if ($dnsupdate['keytype'] == "zone") {
2329
					$flags = 257;
2330
					$proto = 3;
2331
				} else if ($dnsupdate['keytype'] == "host") {
2332
					$flags = 513;
2333
					$proto = 3;
2334
				} else if ($dnsupdate['keytype'] == "user") {
2335
					$flags = 0;
2336
					$proto = 2;
2337
				}
2338

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

    
2343
				/* generate update instructions */
2344
				$upinst = "";
2345
				if (!empty($dnsupdate['server'])) {
2346
					$upinst .= "server {$dnsupdate['server']}\n";
2347
				}
2348

    
2349
				if (file_exists($cacheFile)) {
2350
					list($cachedipv4, $cacheTimev4) = explode("|", file_get_contents($cacheFile));
2351
				}
2352
				if (file_exists("{$cacheFile}.ipv6")) {
2353
					list($cachedipv6, $cacheTimev6) = explode("|", file_get_contents("{$cacheFile}.ipv6"));
2354
				}
2355

    
2356
				// 25 Days
2357
				$maxCacheAgeSecs = 25 * 24 * 60 * 60;
2358
				$need_update = false;
2359

    
2360
				conf_mount_rw();
2361
				/* Update IPv4 if we have it. */
2362
				if (is_ipaddrv4($wanip) && $dnsupdate['recordtype'] != "AAAA") {
2363
					if (($wanip != $cachedipv4) || (($currentTime - $cacheTimev4) > $maxCacheAgeSecs) || $forced) {
2364
						$upinst .= "update delete {$dnsupdate['host']}. A\n";
2365
						$upinst .= "update add {$dnsupdate['host']}. {$dnsupdate['ttl']} A {$wanip}\n";
2366
						$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";
2367
						@file_put_contents($cacheFile, "{$wanip}|{$currentTime}");
2368
						log_error("phpDynDNS: updating cache file {$cacheFile}: {$wanip}");
2369
						$need_update = true;
2370
					} else {
2371
						log_error("phpDynDNS: Not updating {$dnsupdate['host']} A record because the IP address has not changed.");
2372
					}
2373
				} else {
2374
					@unlink($cacheFile);
2375
				}
2376

    
2377
				/* Update IPv6 if we have it. */
2378
				if (is_ipaddrv6($wanipv6) && $dnsupdate['recordtype'] != "A") {
2379
					if (($wanipv6 != $cachedipv6) || (($currentTime - $cacheTimev6) > $maxCacheAgeSecs) || $forced) {
2380
						$upinst .= "update delete {$dnsupdate['host']}. AAAA\n";
2381
						$upinst .= "update add {$dnsupdate['host']}. {$dnsupdate['ttl']} AAAA {$wanipv6}\n";
2382
						$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";
2383
						@file_put_contents("{$cacheFile}.ipv6", "{$wanipv6}|{$currentTime}");
2384
						log_error("phpDynDNS: updating cache file {$cacheFile}.ipv6: {$wanipv6}");
2385
						$need_update = true;
2386
					} else {
2387
						log_error("phpDynDNS: Not updating {$dnsupdate['host']} AAAA record because the IPv6 address has not changed.");
2388
					}
2389
				} else {
2390
					@unlink("{$cacheFile}.ipv6");
2391
				}
2392
				conf_mount_ro();
2393

    
2394
				$upinst .= "\n";	/* mind that trailing newline! */
2395

    
2396
				if ($need_update) {
2397
					@file_put_contents("{$g['varetc_path']}/nsupdatecmds{$i}", $upinst);
2398
					unset($upinst);
2399
					/* invoke nsupdate */
2400
					$cmd = "/usr/local/bin/nsupdate -k {$g['varetc_path']}/K{$i}{$keyname}+157+00000.key";
2401
					if (isset($dnsupdate['usetcp'])) {
2402
						$cmd .= " -v";
2403
					}
2404
					$cmd .= " {$g['varetc_path']}/nsupdatecmds{$i}";
2405
					mwexec_bg($cmd);
2406
					unset($cmd);
2407
				}
2408
			}
2409
		}
2410
		if (!empty($notify_text)) {
2411
			notify_all_remote($notify_text);
2412
		}
2413
	}
2414

    
2415
	return 0;
2416
}
2417

    
2418
/* configure cron service */
2419
function configure_cron() {
2420
	global $g, $config;
2421

    
2422
	conf_mount_rw();
2423
	/* preserve existing crontab entries */
2424
	$crontab_contents = file("/etc/crontab", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2425

    
2426
	for ($i = 0; $i < count($crontab_contents); $i++) {
2427
		$cron_item =& $crontab_contents[$i];
2428
		if (strpos($cron_item, "# pfSense specific crontab entries") !== false) {
2429
			array_splice($crontab_contents, $i - 1);
2430
			break;
2431
		}
2432
	}
2433
	$crontab_contents = implode("\n", $crontab_contents) . "\n";
2434

    
2435

    
2436
	if (is_array($config['cron']['item'])) {
2437
		$crontab_contents .= "#\n";
2438
		$crontab_contents .= "# " . gettext("pfSense specific crontab entries") . "\n";
2439
		$crontab_contents .= "# " .gettext("Created:") . " " . date("F j, Y, g:i a") . "\n";
2440
		$crontab_contents .= "#\n";
2441

    
2442
		if (isset($config['system']['proxyurl']) && !empty($config['system']['proxyurl'])) {
2443
			$http_proxy = $config['system']['proxyurl'];
2444
			if (isset($config['system']['proxyport']) && !empty($config['system']['proxyport'])) {
2445
				$http_proxy .= ':' . $config['system']['proxyport'];
2446
			}
2447
			$crontab_contents .= "HTTP_PROXY={$http_proxy}";
2448
		}
2449

    
2450
		foreach ($config['cron']['item'] as $item) {
2451
			$crontab_contents .= "\n{$item['minute']}\t";
2452
			$crontab_contents .= "{$item['hour']}\t";
2453
			$crontab_contents .= "{$item['mday']}\t";
2454
			$crontab_contents .= "{$item['month']}\t";
2455
			$crontab_contents .= "{$item['wday']}\t";
2456
			$crontab_contents .= "{$item['who']}\t";
2457
			$crontab_contents .= "{$item['command']}";
2458
		}
2459

    
2460
		$crontab_contents .= "\n#\n";
2461
		$crontab_contents .= "# " . gettext("If possible do not add items to this file manually.") . "\n";
2462
		$crontab_contents .= "# " . gettext("If you do so, this file must be terminated with a blank line (e.g. new line)") . "\n";
2463
		$crontab_contents .= "#\n\n";
2464
	}
2465

    
2466
	/* please maintain the newline at the end of file */
2467
	file_put_contents("/etc/crontab", $crontab_contents);
2468
	unset($crontab_contents);
2469

    
2470
	/* make sure that cron is running and start it if it got killed somehow */
2471
	if (!is_process_running("cron")) {
2472
		exec("cd /tmp && /usr/sbin/cron -s 2>/dev/null");
2473
	} else {
2474
	/* do a HUP kill to force sync changes */
2475
		sigkillbypid("{$g['varrun_path']}/cron.pid", "HUP");
2476
	}
2477

    
2478
	conf_mount_ro();
2479
}
2480

    
2481
function upnp_action ($action) {
2482
	global $g, $config;
2483
	switch ($action) {
2484
		case "start":
2485
			if (file_exists('/var/etc/miniupnpd.conf')) {
2486
				@unlink("{$g['varrun_path']}/miniupnpd.pid");
2487
				mwexec_bg("/usr/local/sbin/miniupnpd -f /var/etc/miniupnpd.conf -P {$g['varrun_path']}/miniupnpd.pid");
2488
			}
2489
			break;
2490
		case "stop":
2491
			killbypid("{$g['varrun_path']}/miniupnpd.pid");
2492
			while ((int)exec("/bin/pgrep -a miniupnpd | wc -l") > 0) {
2493
				mwexec('killall miniupnpd 2>/dev/null', true);
2494
			}
2495
			mwexec('/sbin/pfctl -aminiupnpd -Fr 2>&1 >/dev/null');
2496
			mwexec('/sbin/pfctl -aminiupnpd -Fn 2>&1 >/dev/null');
2497
			break;
2498
		case "restart":
2499
			upnp_action('stop');
2500
			upnp_action('start');
2501
			break;
2502
	}
2503
}
2504

    
2505
function upnp_start() {
2506
	global $config;
2507

    
2508
	if (!isset($config['installedpackages']['miniupnpd']['config'])) {
2509
		return;
2510
	}
2511

    
2512
	if ($config['installedpackages']['miniupnpd']['config'][0]['enable']) {
2513
		echo gettext("Starting UPnP service... ");
2514
		require_once('/usr/local/pkg/miniupnpd.inc');
2515
		sync_package_miniupnpd();
2516
		echo "done.\n";
2517
	}
2518
}
2519

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

    
2523
	$is_installed = false;
2524
	$cron_changed = true;
2525

    
2526
	if (!is_array($config['cron'])) {
2527
		$config['cron'] = array();
2528
	}
2529
	if (!is_array($config['cron']['item'])) {
2530
		$config['cron']['item'] = array();
2531
	}
2532

    
2533
	$x = 0;
2534
	foreach ($config['cron']['item'] as $item) {
2535
		if (strstr($item['command'], $command)) {
2536
			$is_installed = true;
2537
			break;
2538
		}
2539
		$x++;
2540
	}
2541

    
2542
	if ($active) {
2543
		$cron_item = array();
2544
		$cron_item['minute'] = $minute;
2545
		$cron_item['hour'] = $hour;
2546
		$cron_item['mday'] = $monthday;
2547
		$cron_item['month'] = $month;
2548
		$cron_item['wday'] = $weekday;
2549
		$cron_item['who'] = $who;
2550
		$cron_item['command'] = $command;
2551
		if (!$is_installed) {
2552
			$config['cron']['item'][] = $cron_item;
2553
			write_config(sprintf(gettext("Installed cron job for %s"), $command));
2554
		} else {
2555
			if ($config['cron']['item'][$x] == $cron_item) {
2556
				$cron_changed = false;
2557
				log_error(sprintf(gettext("Checked cron job for %s, no change needed"), $command));
2558
			} else {
2559
				$config['cron']['item'][$x] = $cron_item;
2560
				write_config(sprintf(gettext("Updated cron job for %s"), $command));
2561
			}
2562
		}
2563
	} else {
2564
		if ($is_installed == true) {
2565
			unset($config['cron']['item'][$x]);
2566
			write_config(sprintf(gettext("Removed cron job for %s"), $command));
2567
		}
2568
	}
2569

    
2570
	if ($cron_changed) {
2571
		configure_cron();
2572
	}
2573
}
2574

    
2575
?>
(50-50/68)