Project

General

Profile

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

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

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

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

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

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

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

    
41
define('DYNDNS_PROVIDER_VALUES', 'citynetwork cloudflare custom custom-v6 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
				// In that case, ignore the pool and post an error.
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
				file_notice("DHCP", $error_msg);
734
				continue;
735
			}
736
			$dhcpdconf .= "	pool {\n";
737
			/* is failover dns setup? */
738
			if (is_array($poolconf['dnsserver']) && $poolconf['dnsserver'][0] <> "") {
739
				$dhcpdconf .= "		option domain-name-servers {$poolconf['dnsserver'][0]}";
740
				if ($poolconf['dnsserver'][1] <> "") {
741
					$dhcpdconf .= ",{$poolconf['dnsserver'][1]}";
742
				}
743
				if ($poolconf['dnsserver'][2] <> "") {
744
					$dhcpdconf .= ",{$poolconf['dnsserver'][2]}";
745
				}
746
				if ($poolconf['dnsserver'][3] <> "") {
747
					$dhcpdconf .= ",{$poolconf['dnsserver'][3]}";
748
				}
749
				$dhcpdconf .= ";\n";
750
			}
751

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

    
768
			if ($poolconf['failover_peerip'] <> "") {
769
				$dhcpdconf .= "		deny dynamic bootp clients;\n";
770
			}
771

    
772
			if (isset($poolconf['denyunknown'])) {
773
			   $dhcpdconf .= "		deny unknown-clients;\n";
774
			}
775

    
776
			if ($poolconf['gateway'] && $poolconf['gateway'] != "none" && ($poolconf['gateway'] != $dhcpifconf['gateway'])) {
777
				$dhcpdconf .= "		option routers {$poolconf['gateway']};\n";
778
			}
779

    
780
			if ($dhcpifconf['failover_peerip'] <> "") {
781
				$dhcpdconf .= "		failover peer \"dhcp_{$dhcpif}\";\n";
782
			}
783

    
784
			$pdnscfg = "";
785

    
786
			if ($poolconf['domain'] && ($poolconf['domain'] != $dhcpifconf['domain'])) {
787
				$pdnscfg .= "		option domain-name \"{$poolconf['domain']}\";\n";
788
			}
789

    
790
			if (!empty($poolconf['domainsearchlist']) && ($poolconf['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
791
				$pdnscfg .= "		option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $poolconf['domainsearchlist'])) . "\";\n";
792
			}
793

    
794
			if (isset($poolconf['ddnsupdate'])) {
795
				if (($poolconf['ddnsdomain'] <> "") && ($poolconf['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
796
					$pdnscfg .= "		ddns-domainname \"{$poolconf['ddnsdomain']}\";\n";
797
				}
798
				$pdnscfg .= "		ddns-update-style interim;\n";
799
			}
800

    
801
			if (is_array($poolconf['dnsserver']) && ($poolconf['dnsserver'][0]) && ($poolconf['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
802
				$pdnscfg .= "		option domain-name-servers " . join(",", $poolconf['dnsserver']) . ";\n";
803
			}
804
			$dhcpdconf .= "{$pdnscfg}";
805

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

    
811
			// max-lease-time
812
			if ($poolconf['maxleasetime'] && ($poolconf['maxleasetime'] != $dhcpifconf['maxleasetime'])) {
813
				$dhcpdconf .= "		max-lease-time {$poolconf['maxleasetime']};\n";
814
			}
815

    
816
			// netbios-name*
817
			if (is_array($poolconf['winsserver']) && $poolconf['winsserver'][0] && ($poolconf['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
818
				$dhcpdconf .= "		option netbios-name-servers " . join(",", $poolconf['winsserver']) . ";\n";
819
				$dhcpdconf .= "		option netbios-node-type 8;\n";
820
			}
821

    
822
			// ntp-servers
823
			if (is_array($poolconf['ntpserver']) && $poolconf['ntpserver'][0] && ($poolconf['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
824
				$dhcpdconf .= "		option ntp-servers " . join(",", $poolconf['ntpserver']) . ";\n";
825
			}
826

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

    
832
			// ldap-server
833
			if (!empty($poolconf['ldap']) && ($poolconf['ldap'] != $dhcpifconf['ldap'])) {
834
				$dhcpdconf .= "		option ldap-server \"{$poolconf['ldap']}\";\n";
835
			}
836

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

    
854
		if ($dhcpifconf['gateway'] && $dhcpifconf['gateway'] != "none") {
855
			$routers = $dhcpifconf['gateway'];
856
			$add_routers = true;
857
		} elseif ($dhcpifconf['gateway'] == "none") {
858
			$add_routers = false;
859
		} else {
860
			$add_routers = $enable_add_routers;
861
			$routers = $ifcfgip;
862
		}
863
		if ($add_routers) {
864
			$dhcpdconf .= "	option routers {$routers};\n";
865
		}
866

    
867
		$dhcpdconf .= <<<EOD
868
$dnscfg
869

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

    
876
		// max-lease-time
877
		if ($dhcpifconf['maxleasetime']) {
878
			$dhcpdconf .= "	max-lease-time {$dhcpifconf['maxleasetime']};\n";
879
		}
880

    
881
		// netbios-name*
882
		if (is_array($dhcpifconf['winsserver']) && $dhcpifconf['winsserver'][0]) {
883
			$dhcpdconf .= "	option netbios-name-servers " . join(",", $dhcpifconf['winsserver']) . ";\n";
884
			$dhcpdconf .= "	option netbios-node-type 8;\n";
885
		}
886

    
887
		// ntp-servers
888
		if (is_array($dhcpifconf['ntpserver']) && $dhcpifconf['ntpserver'][0]) {
889
			$dhcpdconf .= "	option ntp-servers " . join(",", $dhcpifconf['ntpserver']) . ";\n";
890
		}
891

    
892
		// tftp-server-name
893
		if ($dhcpifconf['tftp'] <> "") {
894
			$dhcpdconf .= "	option tftp-server-name \"{$dhcpifconf['tftp']}\";\n";
895
		}
896

    
897
		// Handle option, number rowhelper values
898
		$dhcpdconf .= "\n";
899
		if ($dhcpifconf['numberoptions']['item']) {
900
			foreach ($dhcpifconf['numberoptions']['item'] as $itemidx => $item) {
901
				if (empty($item['type']) || $item['type'] == "text") {
902
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} \"{$item['value']}\";\n";
903
				} else {
904
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} {$item['value']};\n";
905
				}
906
			}
907
		}
908

    
909
		// ldap-server
910
		if ($dhcpifconf['ldap'] <> "") {
911
			$dhcpdconf .= "	option ldap-server \"{$dhcpifconf['ldap']}\";\n";
912
		}
913

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

    
937
		$dhcpdconf .= <<<EOD
938
}
939

    
940
EOD;
941

    
942
		/* add static mappings */
943
		if (is_array($dhcpifconf['staticmap'])) {
944

    
945
			$i = 0;
946
			foreach ($dhcpifconf['staticmap'] as $sm) {
947
				$dhcpdconf .= "host s_{$dhcpif}_{$i} {\n";
948

    
949
				if ($sm['mac']) {
950
					$dhcpdconf .= "        hardware ethernet {$sm['mac']};\n";
951
				}
952

    
953
				if ($sm['cid']) {
954
					$dhcpdconf .= "        option dhcp-client-identifier \"{$sm['cid']}\";\n";
955
				}
956

    
957
				if ($sm['ipaddr']) {
958
					$dhcpdconf .= "	fixed-address {$sm['ipaddr']};\n";
959
				}
960

    
961
				if ($sm['hostname']) {
962
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
963
					$dhhostname = str_replace(".", "_", $dhhostname);
964
					$dhcpdconf .= "	option host-name \"{$dhhostname}\";\n";
965
				}
966
				if ($sm['filename']) {
967
					$dhcpdconf .= "	filename \"{$sm['filename']}\";\n";
968
				}
969

    
970
				if ($sm['rootpath']) {
971
					$dhcpdconf .= "	option root-path \"{$sm['rootpath']}\";\n";
972
				}
973

    
974
				if ($sm['gateway'] && ($sm['gateway'] != $dhcpifconf['gateway'])) {
975
					$dhcpdconf .= "	option routers {$sm['gateway']};\n";
976
				}
977

    
978
				$smdnscfg = "";
979

    
980
				if ($sm['domain'] && ($sm['domain'] != $dhcpifconf['domain'])) {
981
					$smdnscfg .= "	option domain-name \"{$sm['domain']}\";\n";
982
				}
983

    
984
				if (!empty($sm['domainsearchlist']) && ($sm['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
985
					$smdnscfg .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $sm['domainsearchlist'])) . "\";\n";
986
				}
987

    
988
				if (isset($sm['ddnsupdate'])) {
989
					if (($sm['ddnsdomain'] <> "") && ($sm['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
990
						$pdnscfg .= "		ddns-domainname \"{$sm['ddnsdomain']}\";\n";
991
					}
992
					$pdnscfg .= "		ddns-update-style interim;\n";
993
				}
994

    
995
				if (is_array($sm['dnsserver']) && ($sm['dnsserver'][0]) && ($sm['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
996
					$smdnscfg .= "	option domain-name-servers " . join(",", $sm['dnsserver']) . ";\n";
997
				}
998
				$dhcpdconf .= "{$smdnscfg}";
999

    
1000
				// default-lease-time
1001
				if ($sm['defaultleasetime'] && ($sm['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) {
1002
					$dhcpdconf .= "	default-lease-time {$sm['defaultleasetime']};\n";
1003
				}
1004

    
1005
				// max-lease-time
1006
				if ($sm['maxleasetime'] && ($sm['maxleasetime'] != $dhcpifconf['maxleasetime'])) {
1007
					$dhcpdconf .= "	max-lease-time {$sm['maxleasetime']};\n";
1008
				}
1009

    
1010
				// netbios-name*
1011
				if (is_array($sm['winsserver']) && $sm['winsserver'][0] && ($sm['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
1012
					$dhcpdconf .= "	option netbios-name-servers " . join(",", $sm['winsserver']) . ";\n";
1013
					$dhcpdconf .= "	option netbios-node-type 8;\n";
1014
				}
1015

    
1016
				// ntp-servers
1017
				if (is_array($sm['ntpserver']) && $sm['ntpserver'][0] && ($sm['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
1018
					$dhcpdconf .= "	option ntp-servers " . join(",", $sm['ntpserver']) . ";\n";
1019
				}
1020

    
1021
				// tftp-server-name
1022
				if (!empty($sm['tftp']) && ($sm['tftp'] != $dhcpifconf['tftp'])) {
1023
					$dhcpdconf .= "	option tftp-server-name \"{$sm['tftp']}\";\n";
1024
				}
1025

    
1026
				$dhcpdconf .= "}\n";
1027
				$i++;
1028
			}
1029
		}
1030

    
1031
		$dhcpdifs[] = get_real_interface($dhcpif);
1032
		if ($newzone['domain-name']) {
1033
			if ($need_ddns_updates) {
1034
				$newzone['dns-servers'] = array($dhcpifconf['ddnsdomainprimary']);
1035
			}
1036
			$ddns_zones[] = $newzone;
1037
		}
1038
	}
1039

    
1040
	if ($need_ddns_updates) {
1041
		$dhcpdconf .= "ddns-update-style interim;\n";
1042
		$dhcpdconf .= "update-static-leases on;\n";
1043

    
1044
		$dhcpdconf .= dhcpdkey($dhcpifconf);
1045
		$dhcpdconf .= dhcpdzones($ddns_zones, $dhcpifconf);
1046
	}
1047

    
1048
	/* write dhcpd.conf */
1049
	if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpd.conf", $dhcpdconf)) {
1050
		printf(gettext("Error: cannot open dhcpd.conf in services_dhcpdv4_configure().%s"), "\n");
1051
		unset($dhcpdconf);
1052
		return 1;
1053
	}
1054
	unset($dhcpdconf);
1055

    
1056
	/* create an empty leases database */
1057
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases")) {
1058
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases");
1059
	}
1060

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

    
1065
	/* fire up dhcpd in a chroot */
1066
	if (count($dhcpdifs) > 0) {
1067
		mwexec("/usr/local/sbin/dhcpd -user dhcpd -group _dhcp -chroot {$g['dhcpd_chroot_path']} -cf /etc/dhcpd.conf -pf {$g['varrun_path']}/dhcpd.pid " .
1068
			join(" ", $dhcpdifs));
1069
	}
1070

    
1071
	if (platform_booting()) {
1072
		print "done.\n";
1073
	}
1074

    
1075
	return 0;
1076
}
1077

    
1078
function dhcpdkey($dhcpifconf) {
1079
	$dhcpdconf = "";
1080
	if ($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "") {
1081
		$dhcpdconf .= "key {$dhcpifconf['ddnsdomainkeyname']} {\n";
1082
		$dhcpdconf .= "	algorithm hmac-md5;\n";
1083
		$dhcpdconf .= "	secret {$dhcpifconf['ddnsdomainkey']};\n";
1084
		$dhcpdconf .= "}\n";
1085
	}
1086

    
1087
	return $dhcpdconf;
1088
}
1089

    
1090
function dhcpdzones($ddns_zones, $dhcpifconf) {
1091
	$dhcpdconf = "";
1092

    
1093
	if (is_array($ddns_zones)) {
1094
		$added_zones = array();
1095
		foreach ($ddns_zones as $zone) {
1096
			if (!is_array($zone) || empty($zone) || !is_array($zone['dns-servers'])) {
1097
				continue;
1098
			}
1099
			$primary = $zone['dns-servers'][0];
1100
			$secondary = empty($zone['dns-servers'][1]) ? "" : $zone['dns-servers'][1];
1101

    
1102
			// Make sure we aren't using any invalid or IPv6 DNS servers.
1103
			if (!is_ipaddrv4($primary)) {
1104
				if (is_ipaddrv4($secondary)) {
1105
					$primary = $secondary;
1106
					$secondary = "";
1107
				} else {
1108
					continue;
1109
				}
1110
			}
1111

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

    
1140
	return $dhcpdconf;
1141
}
1142

    
1143
function services_dhcpdv6_configure($blacklist = array()) {
1144
	global $config, $g;
1145

    
1146
	if ($g['services_dhcp_server_enable'] == false) {
1147
		return;
1148
	}
1149

    
1150
	if (isset($config['system']['developerspew'])) {
1151
		$mt = microtime();
1152
		echo "services_dhcpd_configure($if) being called $mt\n";
1153
	}
1154

    
1155
	/* kill any running dhcpd */
1156
	if (isvalidpid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid")) {
1157
		killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid");
1158
	}
1159
	if (isvalidpid("{$g['varrun_path']}/dhcpleases6.pid")) {
1160
		killbypid("{$g['varrun_path']}/dhcpleases6.pid");
1161
	}
1162

    
1163
	/* DHCP enabled on any interfaces? */
1164
	if (!is_dhcpv6_server_enabled()) {
1165
		return 0;
1166
	}
1167

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

    
1183
	$syscfg = $config['system'];
1184
	if (!is_array($config['dhcpdv6'])) {
1185
		$config['dhcpdv6'] = array();
1186
	}
1187
	$dhcpdv6cfg = $config['dhcpdv6'];
1188
	$Iflist = get_configured_interface_list();
1189
	$Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces());
1190

    
1191

    
1192
	if (platform_booting()) {
1193
		echo "Starting DHCPv6 service...";
1194
	} else {
1195
		sleep(1);
1196
	}
1197

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

    
1231
				/* set the delegation start to half the current address block */
1232
				$range = Net_IPv6::parseAddress($ifcfgipv6, (64 - $pdlenmax));
1233
				$range['start'] = Net_IPv6::getNetmask($range['end'], (64 - $pdlenhalf));
1234

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

    
1239
				$dhcpdv6cfg[$ifname]['prefixrange']['from'] = Net_IPv6::compress($range['start']);
1240
				$dhcpdv6cfg[$ifname]['prefixrange']['to'] = Net_IPv6::compress($range['end']);
1241
			}
1242
			$dhcpdv6cfg[$ifname]['dns6ip'] = get_interface_ipv6($ifname);
1243
		}
1244
	}
1245

    
1246
	$custoptionsv6 = "";
1247
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1248
		if (is_array($dhcpv6ifconf['numberoptions']) && is_array($dhcpv6ifconf['numberoptions']['item'])) {
1249
			foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
1250
				$custoptionsv6 .= "option custom-{$dhcpv6if}-{$itemv6idx} code {$itemv6['number']} = text;\n";
1251
			}
1252
		}
1253
	}
1254

    
1255
	if (isset($dhcpv6ifconf['netboot']) && !empty($dhcpv6ifconf['bootfile_url'])) {
1256
		$custoptionsv6 .= "option dhcp6.bootfile-url code 59 = string;\n";
1257
	}
1258

    
1259
	$dhcpdv6conf = <<<EOD
1260

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

    
1273
EOD;
1274

    
1275
	if (!isset($dhcpv6ifconf['disableauthoritative'])) {
1276
		$dhcpdv6conf .= "authoritative;\n";
1277
	}
1278

    
1279
	if (isset($dhcpv6ifconf['alwaysbroadcast'])) {
1280
		$dhcpdv6conf .= "always-broadcast on\n";
1281
	}
1282

    
1283
	$dhcpdv6ifs = array();
1284

    
1285
	$dhcpv6num = 0;
1286
	$nsupdate = false;
1287

    
1288
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1289

    
1290
		$ddns_zones = array();
1291

    
1292
		$ifcfgv6 = $config['interfaces'][$dhcpv6if];
1293

    
1294
		if (!isset($dhcpv6ifconf['enable']) || !isset($Iflist[$dhcpv6if]) || !isset($ifcfgv6['enable'])) {
1295
			continue;
1296
		}
1297
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1298
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
1299
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1300

    
1301
		if ($is_olsr_enabled == true) {
1302
			if ($dhcpv6ifconf['netmask']) {
1303
				$subnetmask = gen_subnet_maskv6($dhcpv6ifconf['netmask']);
1304
			}
1305
		}
1306

    
1307
		$dnscfgv6 = "";
1308

    
1309
		if ($dhcpv6ifconf['domain']) {
1310
			$dnscfgv6 .= "	option domain-name \"{$dhcpv6ifconf['domain']}\";\n";
1311
		}
1312

    
1313
		if ($dhcpv6ifconf['domainsearchlist'] <> "") {
1314
			$dnscfgv6 .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpv6ifconf['domainsearchlist'])) . "\";\n";
1315
		}
1316

    
1317
		if (isset($dhcpv6ifconf['ddnsupdate'])) {
1318
			if ($dhcpv6ifconf['ddnsdomain'] <> "") {
1319
				$dnscfgv6 .= "	ddns-domainname \"{$dhcpv6ifconf['ddnsdomain']}\";\n";
1320
			}
1321
			$dnscfgv6 .= "	ddns-update-style interim;\n";
1322
			$nsupdate = true;
1323
		}
1324

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

    
1341
		if ($dhcpv6ifconf['domain']) {
1342
			$newzone = array();
1343
			$newzone['domain-name'] = $dhcpv6ifconf['domain'];
1344
			$newzone['dns-servers'][] = $dhcpv6ifconf['ddnsdomainprimary'];
1345
			$ddns_zones[] = $newzone;
1346
		}
1347

    
1348
		if (is_ipaddrv6($ifcfgipv6)) {
1349
			$dhcpdv6conf .= "subnet6 {$subnetv6}/{$ifcfgsnv6}";
1350
		} else {
1351
			$subnet6 = gen_subnetv6($dhcpv6ifconf['range']['from'], "64");
1352
			$dhcpdv6conf .= "subnet6 {$subnet6}/64";
1353
		}
1354
		$dhcpdv6conf .= " {\n";
1355

    
1356
		$dhcpdv6conf .= <<<EOD
1357
	range6 {$dhcpv6ifconf['range']['from']} {$dhcpv6ifconf['range']['to']};
1358
$dnscfgv6
1359

    
1360
EOD;
1361

    
1362
		if (is_ipaddrv6($dhcpv6ifconf['prefixrange']['from']) && is_ipaddrv6($dhcpv6ifconf['prefixrange']['to'])) {
1363
			$dhcpdv6conf .= "	prefix6 {$dhcpv6ifconf['prefixrange']['from']} {$dhcpv6ifconf['prefixrange']['to']} /{$dhcpv6ifconf['prefixrange']['prefixlength']};\n";
1364
		}
1365
		if (is_ipaddrv6($dhcpv6ifconf['dns6ip'])) {
1366
			$dhcpdv6conf .= "	option dhcp6.name-servers {$dhcpv6ifconf['dns6ip']};\n";
1367
		}
1368
		// default-lease-time
1369
		if ($dhcpv6ifconf['defaultleasetime']) {
1370
			$dhcpdv6conf .= "	default-lease-time {$dhcpv6ifconf['defaultleasetime']};\n";
1371
		}
1372

    
1373
		// max-lease-time
1374
		if ($dhcpv6ifconf['maxleasetime']) {
1375
			$dhcpdv6conf .= "	max-lease-time {$dhcpv6ifconf['maxleasetime']};\n";
1376
		}
1377

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

    
1397
		// Handle option, number rowhelper values
1398
		$dhcpdv6conf .= "\n";
1399
		if ($dhcpv6ifconf['numberoptions']['item']) {
1400
			foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
1401
				$dhcpdv6conf .= "	option custom-{$dhcpv6if}-{$itemv6idx} \"{$itemv6['value']}\";\n";
1402
			}
1403
		}
1404

    
1405
		// ldap-server
1406
		if ($dhcpv6ifconf['ldap'] <> "") {
1407
			$dhcpdv6conf .= "	option ldap-server \"{$dhcpv6ifconf['ldap']}\";\n";
1408
		}
1409

    
1410
		// net boot information
1411
		if (isset($dhcpv6ifconf['netboot'])) {
1412
			if (!empty($dhcpv6ifconf['bootfile_url'])) {
1413
				$dhcpdv6conf .= "	option dhcp6.bootfile-url \"{$dhcpv6ifconf['bootfile_url']}\";\n";
1414
			}
1415
		}
1416

    
1417
		$dhcpdv6conf .= "}\n";
1418

    
1419
		/* add static mappings */
1420
		/* Needs to use DUID */
1421
		if (is_array($dhcpv6ifconf['staticmap'])) {
1422
			$i = 0;
1423
			foreach ($dhcpv6ifconf['staticmap'] as $sm) {
1424
				$dhcpdv6conf .= <<<EOD
1425
host s_{$dhcpv6if}_{$i} {
1426
	host-identifier option dhcp6.client-id {$sm['duid']};
1427

    
1428
EOD;
1429
				if ($sm['ipaddrv6']) {
1430
					$dhcpdv6conf .= "	fixed-address6 {$sm['ipaddrv6']};\n";
1431
				}
1432

    
1433
				if ($sm['hostname']) {
1434
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
1435
					$dhhostname = str_replace(".", "_", $dhhostname);
1436
					$dhcpdv6conf .= "	option host-name {$dhhostname};\n";
1437
				}
1438
				if ($sm['filename']) {
1439
					$dhcpdv6conf .= "	filename \"{$sm['filename']}\";\n";
1440
				}
1441

    
1442
				if ($sm['rootpath']) {
1443
					$dhcpdv6conf .= "	option root-path \"{$sm['rootpath']}\";\n";
1444
				}
1445

    
1446
				$dhcpdv6conf .= "}\n";
1447
				$i++;
1448
			}
1449
		}
1450

    
1451
		if ($dhcpv6ifconf['domain']) {
1452
			$dhcpdv6conf .= dhcpdkey($dhcpv6ifconf);
1453
			$dhcpdv6conf .= dhcpdzones($ddns_zones, $dhcpv6ifconf);
1454
		}
1455

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

    
1474
	if ($nsupdate) {
1475
		$dhcpdv6conf .= "ddns-update-style interim;\n";
1476
	} else {
1477
		$dhcpdv6conf .= "ddns-update-style none;\n";
1478
	}
1479

    
1480
	/* write dhcpdv6.conf */
1481
	if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf", $dhcpdv6conf)) {
1482
		log_error("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n");
1483
		if (platform_booting()) {
1484
			printf("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n");
1485
		}
1486
		unset($dhcpdv6conf);
1487
		return 1;
1488
	}
1489
	unset($dhcpdv6conf);
1490

    
1491
	/* create an empty leases v6 database */
1492
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases")) {
1493
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases");
1494
	}
1495

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

    
1500
	/* fire up dhcpd in a chroot */
1501
	if (count($dhcpdv6ifs) > 0) {
1502
		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 " .
1503
			join(" ", $dhcpdv6ifs));
1504
		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");
1505
	}
1506
	if (platform_booting()) {
1507
		print gettext("done.") . "\n";
1508
	}
1509

    
1510
	return 0;
1511
}
1512

    
1513
function services_igmpproxy_configure() {
1514
	global $config, $g;
1515

    
1516
	/* kill any running igmpproxy */
1517
	killbyname("igmpproxy");
1518

    
1519
	if (!is_array($config['igmpproxy']['igmpentry']) || (count($config['igmpproxy']['igmpentry']) == 0)) {
1520
		return 1;
1521
	}
1522

    
1523
	$iflist = get_configured_interface_list();
1524

    
1525
	$igmpconf = <<<EOD
1526

    
1527
##------------------------------------------------------
1528
## Enable Quickleave mode (Sends Leave instantly)
1529
##------------------------------------------------------
1530
quickleave
1531

    
1532
EOD;
1533

    
1534
	foreach ($config['igmpproxy']['igmpentry'] as $igmpcf) {
1535
		unset($iflist[$igmpcf['ifname']]);
1536
		$realif = get_real_interface($igmpcf['ifname']);
1537
		if (empty($igmpcf['threshold'])) {
1538
			$threshld = 1;
1539
		} else {
1540
			$threshld = $igmpcf['threshold'];
1541
		}
1542
		$igmpconf .= "phyint {$realif} {$igmpcf['type']} ratelimit 0 threshold {$threshld}\n";
1543

    
1544
		if ($igmpcf['address'] <> "") {
1545
			$item = explode(" ", $igmpcf['address']);
1546
			foreach ($item as $iww) {
1547
				$igmpconf .= "altnet {$iww}\n";
1548
			}
1549
		}
1550
		$igmpconf .= "\n";
1551
	}
1552
	foreach ($iflist as $ifn) {
1553
		$realif = get_real_interface($ifn);
1554
		$igmpconf .= "phyint {$realif} disabled\n";
1555
	}
1556
	$igmpconf .= "\n";
1557

    
1558
	$igmpfl = fopen($g['tmp_path'] . "/igmpproxy.conf", "w");
1559
	if (!$igmpfl) {
1560
		log_error(gettext("Could not write Igmpproxy configuration file!"));
1561
		return;
1562
	}
1563
	fwrite($igmpfl, $igmpconf);
1564
	fclose($igmpfl);
1565
	unset($igmpconf);
1566

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

    
1571
	return 0;
1572
}
1573

    
1574
function services_dhcrelay_configure() {
1575
	global $config, $g;
1576

    
1577
	if (isset($config['system']['developerspew'])) {
1578
		$mt = microtime();
1579
		echo "services_dhcrelay_configure() being called $mt\n";
1580
	}
1581

    
1582
	/* kill any running dhcrelay */
1583
	killbypid("{$g['varrun_path']}/dhcrelay.pid");
1584

    
1585
	$dhcrelaycfg =& $config['dhcrelay'];
1586

    
1587
	/* DHCPRelay enabled on any interfaces? */
1588
	if (!isset($dhcrelaycfg['enable'])) {
1589
		return 0;
1590
	}
1591

    
1592
	if (platform_booting()) {
1593
		echo gettext("Starting DHCP relay service...");
1594
	} else {
1595
		sleep(1);
1596
	}
1597

    
1598
	$iflist = get_configured_interface_list();
1599

    
1600
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1601
	foreach ($dhcifaces as $dhcrelayif) {
1602
		if (!isset($iflist[$dhcrelayif]) ||
1603
		    link_interface_to_bridge($dhcrelayif)) {
1604
			continue;
1605
		}
1606

    
1607
		if (is_ipaddr(get_interface_ip($dhcrelayif))) {
1608
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1609
		}
1610
	}
1611

    
1612
	$srvips = explode(",", $dhcrelaycfg['server']);
1613
	if (!is_array($srvips)) {
1614
		log_error("No destination IP has been configured!");
1615
		return;
1616
	}
1617

    
1618
	$dhcrelayifs = array_unique($dhcrelayifs);
1619

    
1620
	/* fire up dhcrelay */
1621
	if (empty($dhcrelayifs)) {
1622
		log_error("No suitable interface found for running dhcrelay!");
1623
		return; /* XXX */
1624
	}
1625

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

    
1628
	if (isset($dhcrelaycfg['agentoption'])) {
1629
		$cmd .= " -a -m replace";
1630
	}
1631

    
1632
	$cmd .= " " . implode(" ", $srvips);
1633
	mwexec($cmd);
1634
	unset($cmd);
1635

    
1636
	return 0;
1637
}
1638

    
1639
function services_dhcrelay6_configure() {
1640
	global $config, $g;
1641

    
1642
	if (isset($config['system']['developerspew'])) {
1643
		$mt = microtime();
1644
		echo "services_dhcrelay6_configure() being called $mt\n";
1645
	}
1646

    
1647
	/* kill any running dhcrelay */
1648
	killbypid("{$g['varrun_path']}/dhcrelay6.pid");
1649

    
1650
	$dhcrelaycfg =& $config['dhcrelay6'];
1651

    
1652
	/* DHCPv6 Relay enabled on any interfaces? */
1653
	if (!isset($dhcrelaycfg['enable'])) {
1654
		return 0;
1655
	}
1656

    
1657
	if (platform_booting()) {
1658
		echo gettext("Starting DHCPv6 relay service...");
1659
	} else {
1660
		sleep(1);
1661
	}
1662

    
1663
	$iflist = get_configured_interface_list();
1664

    
1665
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1666
	foreach ($dhcifaces as $dhcrelayif) {
1667
		if (!isset($iflist[$dhcrelayif]) ||
1668
		    link_interface_to_bridge($dhcrelayif)) {
1669
			continue;
1670
		}
1671

    
1672
		if (is_ipaddrv6(get_interface_ipv6($dhcrelayif))) {
1673
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1674
		}
1675
	}
1676
	$dhcrelayifs = array_unique($dhcrelayifs);
1677

    
1678
	$srvips = explode(",", $dhcrelaycfg['server']);
1679
	if (!is_array($srvips)) {
1680
		log_error("No destination IP has been configured!");
1681
		return;
1682
	}
1683

    
1684
	/* fire up dhcrelay */
1685
	if (empty($dhcrelayifs) || empty($srvifaces)) {
1686
		log_error("No suitable interface found for running dhcrelay -6!");
1687
		return; /* XXX */
1688
	}
1689

    
1690
	$cmd = "/usr/local/sbin/dhcrelay -6 -pf \"{$g['varrun_path']}/dhcrelay6.pid\"";
1691
	foreach ($dhcrelayifs as $dhcrelayif) {
1692
		$cmd .= " -l {$dhcrelayif}";
1693
	}
1694
	foreach ($srvifaces as $srviface) {
1695
		$cmd .= " -u \"{$srviface}\"";
1696
	}
1697
	mwexec($cmd);
1698
	unset($cmd);
1699

    
1700
	return 0;
1701
}
1702

    
1703
function services_dyndns_configure_client($conf) {
1704

    
1705
	if (!isset($conf['enable'])) {
1706
		return;
1707
	}
1708

    
1709
	/* load up the dyndns.class */
1710
	require_once("dyndns.class");
1711

    
1712
	$dns = new updatedns($dnsService = $conf['type'],
1713
		$dnsHost = $conf['host'],
1714
		$dnsUser = $conf['username'],
1715
		$dnsPass = $conf['password'],
1716
		$dnsWildcard = $conf['wildcard'],
1717
		$dnsMX = $conf['mx'],
1718
		$dnsIf = "{$conf['interface']}",
1719
		$dnsBackMX = NULL,
1720
		$dnsServer = NULL,
1721
		$dnsPort = NULL,
1722
		$dnsUpdateURL = "{$conf['updateurl']}",
1723
		$forceUpdate = $conf['force'],
1724
		$dnsZoneID = $conf['zoneid'],
1725
		$dnsTTL = $conf['ttl'],
1726
		$dnsResultMatch = "{$conf['resultmatch']}",
1727
		$dnsRequestIf = "{$conf['requestif']}",
1728
		$dnsID = "{$conf['id']}",
1729
		$dnsVerboseLog = $conf['verboselog'],
1730
		$curlIpresolveV4 = $conf['curl_ipresolve_v4'],
1731
		$curlSslVerifypeer = $conf['curl_ssl_verifypeer']);
1732
}
1733

    
1734
function services_dyndns_configure($int = "") {
1735
	global $config, $g;
1736
	if (isset($config['system']['developerspew'])) {
1737
		$mt = microtime();
1738
		echo "services_dyndns_configure() being called $mt\n";
1739
	}
1740

    
1741
	$dyndnscfg = $config['dyndnses']['dyndns'];
1742
	$gwgroups = return_gateway_groups_array();
1743
	if (is_array($dyndnscfg)) {
1744
		if (platform_booting()) {
1745
			echo gettext("Starting DynDNS clients...");
1746
		}
1747

    
1748
		foreach ($dyndnscfg as $dyndns) {
1749
			if ((empty($int)) || ($int == $dyndns['interface']) || (is_array($gwgroups[$dyndns['interface']]))) {
1750
				$dyndns['verboselog'] = isset($dyndns['verboselog']);
1751
				$dyndns['curl_ipresolve_v4'] = isset($dyndns['curl_ipresolve_v4']);
1752
				$dyndns['curl_ssl_verifypeer'] = isset($dyndns['curl_ssl_verifypeer']);
1753
				services_dyndns_configure_client($dyndns);
1754
				sleep(1);
1755
			}
1756
		}
1757

    
1758
		if (platform_booting()) {
1759
			echo gettext("done.") . "\n";
1760
		}
1761
	}
1762

    
1763
	return 0;
1764
}
1765

    
1766
function dyndnsCheckIP($int) {
1767
	global $config;
1768
	$ip_address = get_interface_ip($int);
1769
	if (is_private_ip($ip_address)) {
1770
		$gateways_status = return_gateways_status(true);
1771
		// If the gateway for this interface is down, then the external check cannot work.
1772
		// Avoid the long wait for the external check to timeout.
1773
		if (stristr($gateways_status[$config['interfaces'][$int]['gateway']]['status'], "down")) {
1774
			return "down";
1775
		}
1776
		$hosttocheck = "http://checkip.dyndns.org";
1777
		$ip_ch = curl_init($hosttocheck);
1778
		curl_setopt($ip_ch, CURLOPT_RETURNTRANSFER, 1);
1779
		curl_setopt($ip_ch, CURLOPT_SSL_VERIFYPEER, FALSE);
1780
		curl_setopt($ip_ch, CURLOPT_INTERFACE, 'host!' . $ip_address);
1781
		curl_setopt($ip_ch, CURLOPT_CONNECTTIMEOUT, '30');
1782
		curl_setopt($ip_ch, CURLOPT_TIMEOUT, 120);
1783
		curl_setopt($ip_ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1784
		$ip_result_page = curl_exec($ip_ch);
1785
		curl_close($ip_ch);
1786
		$ip_result_decoded = urldecode($ip_result_page);
1787
		preg_match('=Current IP Address: (.*)</body>=siU', $ip_result_decoded, $matches);
1788
		$ip_address = trim($matches[1]);
1789
	}
1790
	return $ip_address;
1791
}
1792

    
1793
function services_dnsmasq_configure() {
1794
	global $config, $g;
1795
	$return = 0;
1796

    
1797
	// hard coded args: will be removed to avoid duplication if specified in custom_options
1798
	$standard_args = array(
1799
		"dns-forward-max" => "--dns-forward-max=5000",
1800
		"cache-size" => "--cache-size=10000",
1801
		"local-ttl" => "--local-ttl=1"
1802
	);
1803

    
1804

    
1805
	if (isset($config['system']['developerspew'])) {
1806
		$mt = microtime();
1807
		echo "services_dnsmasq_configure() being called $mt\n";
1808
	}
1809

    
1810
	/* kill any running dnsmasq */
1811
	if (file_exists("{$g['varrun_path']}/dnsmasq.pid")) {
1812
		sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM");
1813
	}
1814

    
1815
	if (isset($config['dnsmasq']['enable'])) {
1816

    
1817
		if (platform_booting()) {
1818
			echo gettext("Starting DNS forwarder...");
1819
		} else {
1820
			sleep(1);
1821
		}
1822

    
1823
		/* generate hosts file */
1824
		if (system_hosts_generate() != 0) {
1825
			$return = 1;
1826
		}
1827

    
1828
		$args = "";
1829

    
1830
		if (isset($config['dnsmasq']['regdhcp'])) {
1831
			$args .= " --dhcp-hostsfile={$g['varetc_path']}/hosts ";
1832
		}
1833

    
1834
		/* Setup listen port, if non-default */
1835
		if (is_port($config['dnsmasq']['port'])) {
1836
			$args .= " --port={$config['dnsmasq']['port']} ";
1837
		}
1838

    
1839
		$listen_addresses = "";
1840
		if (isset($config['dnsmasq']['interface'])) {
1841
			$interfaces = explode(",", $config['dnsmasq']['interface']);
1842
			foreach ($interfaces as $interface) {
1843
				if (is_ipaddrv4($interface)) {
1844
					$listen_addresses .= " --listen-address={$interface} ";
1845
				} else if (is_ipaddrv6($interface)) {
1846
					/*
1847
					 * XXX: Since dnsmasq does not support link-local address
1848
					 * with scope specified. These checks are being done.
1849
					 */
1850
					if (is_linklocal($interface) && strstr($interface, "%")) {
1851
						$tmpaddrll6 = explode("%", $interface);
1852
						$listen_addresses .= " --listen-address={$tmpaddrll6[0]} ";
1853
					} else {
1854
						$listen_addresses .= " --listen-address={$interface} ";
1855
					}
1856
				} else if (strstr($interface, "_vip")) {
1857
					$laddr = get_configured_carp_interface_list($interface);
1858
					if (is_ipaddr($laddr)) {
1859
						$listen_addresses .= " --listen-address={$laddr} ";
1860
					}
1861
				} else {
1862
					$if = get_real_interface($interface);
1863
					if (does_interface_exist($if)) {
1864
						$laddr = get_interface_ip($interface);
1865
						if (is_ipaddrv4($laddr)) {
1866
							$listen_addresses .= " --listen-address={$laddr} ";
1867
						}
1868
						$laddr6 = get_interface_ipv6($interface);
1869
						if (is_ipaddrv6($laddr6) && !isset($config['dnsmasq']['strictbind'])) {
1870
							/*
1871
							 * XXX: Since dnsmasq does not support link-local address
1872
							 * with scope specified. These checks are being done.
1873
							 */
1874
							if (is_linklocal($laddr6) && strstr($laddr6, "%")) {
1875
								$tmpaddrll6 = explode("%", $laddr6);
1876
								$listen_addresses .= " --listen-address={$tmpaddrll6[0]} ";
1877
							} else {
1878
								$listen_addresses .= " --listen-address={$laddr6} ";
1879
							}
1880
						}
1881
					}
1882
				}
1883
			}
1884
			if (!empty($listen_addresses)) {
1885
				$args .= " {$listen_addresses} ";
1886
				if (isset($config['dnsmasq']['strictbind'])) {
1887
					$args .= " --bind-interfaces ";
1888
				}
1889
			}
1890
		}
1891

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

    
1899
			// Build an array of domain overrides to help in checking for matches.
1900
			$override_a = array();
1901
			if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
1902
				foreach ($config['dnsmasq']['domainoverrides'] as $override) {
1903
					$override_a[$override['domain']] = "y";
1904
				}
1905
			}
1906

    
1907
			// Build an array of the private reverse lookup domain names
1908
			$reverse_domain_a = array("10.in-addr.arpa", "168.192.in-addr.arpa");
1909
			// Unfortunately the 172.16.0.0/12 range does not map nicely to the in-addr.arpa scheme.
1910
			for ($subnet_num = 16; $subnet_num < 32; $subnet_num++) {
1911
				$reverse_domain_a[] = "$subnet_num.172.in-addr.arpa";
1912
			}
1913

    
1914
			// Set the --server parameter to nowhere for each reverse domain name that was not specifically specified in a domain override.
1915
			foreach ($reverse_domain_a as $reverse_domain) {
1916
				if (!isset($override_a[$reverse_domain])) {
1917
					$args .= " --server=/$reverse_domain/ ";
1918
				}
1919
			}
1920
			unset($override_a);
1921
			unset($reverse_domain_a);
1922
		}
1923

    
1924
		/* Setup forwarded domains */
1925
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
1926
			foreach ($config['dnsmasq']['domainoverrides'] as $override) {
1927
				if ($override['ip'] == "!") {
1928
					$override[ip] = "";
1929
				}
1930
				$args .= ' --server=/' . $override['domain'] . '/' . $override['ip'];
1931
			}
1932
		}
1933

    
1934
		/* Allow DNS Rebind for forwarded domains */
1935
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
1936
			if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
1937
				foreach ($config['dnsmasq']['domainoverrides'] as $override) {
1938
					$args .= ' --rebind-domain-ok=/' . $override['domain'] . '/ ';
1939
				}
1940
			}
1941
		}
1942

    
1943
		if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
1944
			$dns_rebind = "--rebind-localhost-ok --stop-dns-rebind";
1945
		}
1946

    
1947
		if (isset($config['dnsmasq']['strict_order'])) {
1948
			$args .= " --strict-order ";
1949
		}
1950

    
1951
		if (isset($config['dnsmasq']['domain_needed'])) {
1952
			$args .= " --domain-needed ";
1953
		}
1954

    
1955
		if ($config['dnsmasq']['custom_options']) {
1956
			foreach (preg_split('/\s+/', $config['dnsmasq']['custom_options']) as $c) {
1957
				$args .= " " . escapeshellarg("--{$c}");
1958
				$p = explode('=', $c);
1959
				if (array_key_exists($p[0], $standard_args)) {
1960
					unset($standard_args[$p[0]]);
1961
				}
1962
			}
1963
		}
1964
		$args .= ' ' . implode(' ', array_values($standard_args));
1965

    
1966
		/* run dnsmasq */
1967
		$cmd = "/usr/local/sbin/dnsmasq --all-servers {$dns_rebind} {$args}";
1968
		//log_error("dnsmasq command: {$cmd}");
1969
		mwexec_bg($cmd);
1970
		unset($args);
1971

    
1972
		system_dhcpleases_configure();
1973

    
1974
		if (platform_booting()) {
1975
			echo gettext("done.") . "\n";
1976
		}
1977
	}
1978

    
1979
	if (!platform_booting()) {
1980
		if (services_dhcpd_configure() != 0) {
1981
			$return = 1;
1982
		}
1983
	}
1984

    
1985
	return $return;
1986
}
1987

    
1988
function services_unbound_configure() {
1989
	global $config, $g;
1990
	$return = 0;
1991

    
1992
	if (isset($config['system']['developerspew'])) {
1993
		$mt = microtime();
1994
		echo "services_unbound_configure() being called $mt\n";
1995
	}
1996

    
1997
	// kill any running Unbound instance
1998
	if (file_exists("{$g['varrun_path']}/unbound.pid")) {
1999
		sigkillbypid("{$g['varrun_path']}/unbound.pid", "TERM");
2000
	}
2001

    
2002
	if (isset($config['unbound']['enable'])) {
2003
		if (platform_booting()) {
2004
			echo gettext("Starting DNS Resolver...");
2005
		} else {
2006
			sleep(1);
2007
		}
2008

    
2009
		/* generate hosts file */
2010
		if (system_hosts_generate() != 0) {
2011
			$return = 1;
2012
		}
2013

    
2014
		require_once('/etc/inc/unbound.inc');
2015
		sync_unbound_service();
2016
		if (platform_booting()) {
2017
			echo gettext("done.") . "\n";
2018
		}
2019

    
2020
		system_dhcpleases_configure();
2021
	}
2022

    
2023
	if (!platform_booting()) {
2024
		if (services_dhcpd_configure() != 0) {
2025
			$return = 1;
2026
		}
2027
	}
2028

    
2029
	return $return;
2030
}
2031

    
2032
function services_snmpd_configure() {
2033
	global $config, $g;
2034
	if (isset($config['system']['developerspew'])) {
2035
		$mt = microtime();
2036
		echo "services_snmpd_configure() being called $mt\n";
2037
	}
2038

    
2039
	/* kill any running snmpd */
2040
	sigkillbypid("{$g['varrun_path']}/snmpd.pid", "TERM");
2041
	sleep(2);
2042
	if (is_process_running("bsnmpd")) {
2043
		mwexec("/usr/bin/killall bsnmpd", true);
2044
	}
2045

    
2046
	if (isset($config['snmpd']['enable'])) {
2047

    
2048
		if (platform_booting()) {
2049
			echo gettext("Starting SNMP daemon... ");
2050
		}
2051

    
2052
		/* generate snmpd.conf */
2053
		$fd = fopen("{$g['varetc_path']}/snmpd.conf", "w");
2054
		if (!$fd) {
2055
			printf(gettext("Error: cannot open snmpd.conf in services_snmpd_configure().%s"), "\n");
2056
			return 1;
2057
		}
2058

    
2059

    
2060
		$snmpdconf = <<<EOD
2061
location := "{$config['snmpd']['syslocation']}"
2062
contact := "{$config['snmpd']['syscontact']}"
2063
read := "{$config['snmpd']['rocommunity']}"
2064

    
2065
EOD;
2066

    
2067
/* No docs on what write strings do there for disable for now.
2068
		if (isset($config['snmpd']['rwenable']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2069
			$snmpdconf .= <<<EOD
2070
# write string
2071
write := "{$config['snmpd']['rwcommunity']}"
2072

    
2073
EOD;
2074
		}
2075
*/
2076

    
2077

    
2078
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2079
			$snmpdconf .= <<<EOD
2080
# SNMP Trap support.
2081
traphost := {$config['snmpd']['trapserver']}
2082
trapport := {$config['snmpd']['trapserverport']}
2083
trap := "{$config['snmpd']['trapstring']}"
2084

    
2085

    
2086
EOD;
2087
		}
2088

    
2089
		$platform = trim(file_get_contents('/etc/platform'));
2090
		if (($platform == "pfSense") && ($g['product_name'] != "pfSense")) {
2091
			$platform = $g['product_name'];
2092
		}
2093
		$sysDescr = "{$g['product_name']} " . php_uname("n") .
2094
			" {$g['product_version']} {$platform} " . php_uname("s") .
2095
			" " . php_uname("r") . " " . php_uname("m");
2096

    
2097
		$snmpdconf .= <<<EOD
2098
system := 1     # pfSense
2099
%snmpd
2100
sysDescr			= "{$sysDescr}"
2101
begemotSnmpdDebugDumpPdus       = 2
2102
begemotSnmpdDebugSyslogPri      = 7
2103
begemotSnmpdCommunityString.0.1 = $(read)
2104

    
2105
EOD;
2106

    
2107
/* No docs on what write strings do there for disable for now.
2108
		if (isset($config['snmpd']['rwcommunity']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2109
			$snmpdconf .= <<<EOD
2110
begemotSnmpdCommunityString.0.2 = $(write)
2111

    
2112
EOD;
2113
		}
2114
*/
2115

    
2116

    
2117
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2118
			$snmpdconf .= <<<EOD
2119
begemotTrapSinkStatus.[$(traphost)].$(trapport) = 4
2120
begemotTrapSinkVersion.[$(traphost)].$(trapport) = 2
2121
begemotTrapSinkComm.[$(traphost)].$(trapport) = $(trap)
2122

    
2123
EOD;
2124
		}
2125

    
2126

    
2127
		$snmpdconf .= <<<EOD
2128
begemotSnmpdCommunityDisable    = 1
2129

    
2130
EOD;
2131

    
2132
		if (isset($config['snmpd']['bindlan'])) {
2133
			$config['snmpd']['bindip'] = 'lan';
2134
			unset($config['snmpd']['bindlan']);
2135
		}
2136
		$bind_to_ip = "0.0.0.0";
2137
		if (isset($config['snmpd']['bindip'])) {
2138
			if (is_ipaddr($config['snmpd']['bindip'])) {
2139
				$bind_to_ip = $config['snmpd']['bindip'];
2140
			} else {
2141
				$if = get_real_interface($config['snmpd']['bindip']);
2142
				if (does_interface_exist($if)) {
2143
					$bind_to_ip = get_interface_ip($config['snmpd']['bindip']);
2144
				}
2145
			}
2146
		}
2147

    
2148
		if (is_port($config['snmpd']['pollport'])) {
2149
			$snmpdconf .= <<<EOD
2150
begemotSnmpdPortStatus.{$bind_to_ip}.{$config['snmpd']['pollport']} = 1
2151

    
2152
EOD;
2153

    
2154
		}
2155

    
2156
		$snmpdconf .= <<<EOD
2157
begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1
2158
begemotSnmpdLocalPortType."/var/run/snmpd.sock" = 4
2159

    
2160
# These are bsnmp macros not php vars.
2161
sysContact      = $(contact)
2162
sysLocation     = $(location)
2163
sysObjectId     = 1.3.6.1.4.1.12325.1.1.2.1.$(system)
2164

    
2165
snmpEnableAuthenTraps = 2
2166

    
2167
EOD;
2168

    
2169
		if (is_array($config['snmpd']['modules'])) {
2170
			if (isset($config['snmpd']['modules']['mibii'])) {
2171
			$snmpdconf .= <<<EOD
2172
begemotSnmpdModulePath."mibII"  = "/usr/lib/snmp_mibII.so"
2173

    
2174
EOD;
2175
			}
2176

    
2177
			if (isset($config['snmpd']['modules']['netgraph'])) {
2178
				$snmpdconf .= <<<EOD
2179
begemotSnmpdModulePath."netgraph" = "/usr/lib/snmp_netgraph.so"
2180
%netgraph
2181
begemotNgControlNodeName = "snmpd"
2182

    
2183
EOD;
2184
			}
2185

    
2186
			if (isset($config['snmpd']['modules']['pf'])) {
2187
				$snmpdconf .= <<<EOD
2188
begemotSnmpdModulePath."pf"     = "/usr/lib/snmp_pf.so"
2189

    
2190
EOD;
2191
			}
2192

    
2193
			if (isset($config['snmpd']['modules']['hostres'])) {
2194
				/* XXX: hostres module crashes APU - ticket #4403 */
2195
				$specplatform = system_identify_specific_platform();
2196
				if ($specplatform['name'] == 'APU') {
2197
					log_error("'Host Resources' SNMP module was ignored because it can potentially crash system on APU boards");
2198
				} else {
2199
					$snmpdconf .= <<<EOD
2200
begemotSnmpdModulePath."hostres"     = "/usr/lib/snmp_hostres.so"
2201

    
2202
EOD;
2203
				}
2204
				unset($specplatform);
2205
			}
2206

    
2207
			if (isset($config['snmpd']['modules']['bridge'])) {
2208
				$snmpdconf .= <<<EOD
2209
begemotSnmpdModulePath."bridge"     = "/usr/lib/snmp_bridge.so"
2210
# config must end with blank line
2211

    
2212
EOD;
2213
			}
2214
			if (isset($config['snmpd']['modules']['ucd'])) {
2215
				$snmpdconf .= <<<EOD
2216
begemotSnmpdModulePath."ucd"     = "/usr/local/lib/snmp_ucd.so"
2217

    
2218
EOD;
2219
			}
2220
			if (isset($config['snmpd']['modules']['regex'])) {
2221
				$snmpdconf .= <<<EOD
2222
begemotSnmpdModulePath."regex"     = "/usr/local/lib/snmp_regex.so"
2223

    
2224
EOD;
2225
			}
2226
		}
2227

    
2228
		fwrite($fd, $snmpdconf);
2229
		fclose($fd);
2230
		unset($snmpdconf);
2231

    
2232
		if (isset($config['snmpd']['bindlan'])) {
2233
			$bindlan = "";
2234
		}
2235

    
2236
		/* run bsnmpd */
2237
		mwexec("/usr/sbin/bsnmpd -c {$g['varetc_path']}/snmpd.conf" .
2238
			"{$bindlan} -p {$g['varrun_path']}/snmpd.pid");
2239

    
2240
		if (platform_booting()) {
2241
			echo gettext("done.") . "\n";
2242
		}
2243
	}
2244

    
2245
	return 0;
2246
}
2247

    
2248
function services_dnsupdate_process($int = "", $updatehost = "", $forced = false) {
2249
	global $config, $g;
2250
	if (isset($config['system']['developerspew'])) {
2251
		$mt = microtime();
2252
		echo "services_dnsupdate_process() being called $mt\n";
2253
	}
2254

    
2255
	/* Dynamic DNS updating active? */
2256
	if (is_array($config['dnsupdates']['dnsupdate'])) {
2257
		$notify_text = "";
2258
		foreach ($config['dnsupdates']['dnsupdate'] as $i => $dnsupdate) {
2259
			if (!isset($dnsupdate['enable'])) {
2260
				continue;
2261
			}
2262
			if (!empty($int) && $int != $dnsupdate['interface']) {
2263
				continue;
2264
			}
2265
			if (!empty($updatehost) && ($updatehost != $dnsupdate['host'])) {
2266
				continue;
2267
			}
2268

    
2269
			/* determine interface name */
2270
			$if = get_real_interface($dnsupdate['interface']);
2271

    
2272
			if (isset($dnsupdate['usepublicip'])) {
2273
				$wanip = dyndnsCheckIP($dnsupdate['interface']);
2274
			} else {
2275
				$wanip = get_interface_ip($dnsupdate['interface']);
2276
			}
2277

    
2278
			$wanipv6 = get_interface_ipv6($dnsupdate['interface']);
2279
			$cacheFile = "{$g['conf_path']}/dyndns_{$dnsupdate['interface']}_rfc2136_" . escapeshellarg($dnsupdate['host']) . "_{$dnsupdate['server']}.cache";
2280
			$currentTime = time();
2281

    
2282
			if ($wanip || $wanipv6) {
2283
				$keyname = $dnsupdate['keyname'];
2284
				/* trailing dot */
2285
				if (substr($keyname, -1) != ".") {
2286
					$keyname .= ".";
2287
				}
2288

    
2289
				$hostname = $dnsupdate['host'];
2290
				/* trailing dot */
2291
				if (substr($hostname, -1) != ".") {
2292
					$hostname .= ".";
2293
				}
2294

    
2295
				/* write private key file
2296
				   this is dumb - public and private keys are the same for HMAC-MD5,
2297
				   but nsupdate insists on having both */
2298
				$fd = fopen("{$g['varetc_path']}/K{$i}{$keyname}+157+00000.private", "w");
2299
				$privkey = <<<EOD
2300
Private-key-format: v1.2
2301
Algorithm: 157 (HMAC)
2302
Key: {$dnsupdate['keydata']}
2303

    
2304
EOD;
2305
				fwrite($fd, $privkey);
2306
				fclose($fd);
2307

    
2308
				/* write public key file */
2309
				if ($dnsupdate['keytype'] == "zone") {
2310
					$flags = 257;
2311
					$proto = 3;
2312
				} else if ($dnsupdate['keytype'] == "host") {
2313
					$flags = 513;
2314
					$proto = 3;
2315
				} else if ($dnsupdate['keytype'] == "user") {
2316
					$flags = 0;
2317
					$proto = 2;
2318
				}
2319

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

    
2324
				/* generate update instructions */
2325
				$upinst = "";
2326
				if (!empty($dnsupdate['server'])) {
2327
					$upinst .= "server {$dnsupdate['server']}\n";
2328
				}
2329

    
2330
				if (file_exists($cacheFile)) {
2331
					list($cachedipv4, $cacheTimev4) = explode("|", file_get_contents($cacheFile));
2332
				}
2333
				if (file_exists("{$cacheFile}.ipv6")) {
2334
					list($cachedipv6, $cacheTimev6) = explode("|", file_get_contents("{$cacheFile}.ipv6"));
2335
				}
2336

    
2337
				// 25 Days
2338
				$maxCacheAgeSecs = 25 * 24 * 60 * 60;
2339
				$need_update = false;
2340

    
2341
				conf_mount_rw();
2342
				/* Update IPv4 if we have it. */
2343
				if (is_ipaddrv4($wanip) && $dnsupdate['recordtype'] != "AAAA") {
2344
					if (($wanip != $cachedipv4) || (($currentTime - $cacheTimev4) > $maxCacheAgeSecs) || $forced) {
2345
						$upinst .= "update delete {$dnsupdate['host']}. A\n";
2346
						$upinst .= "update add {$dnsupdate['host']}. {$dnsupdate['ttl']} A {$wanip}\n";
2347
						$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";
2348
						@file_put_contents($cacheFile, "{$wanip}|{$currentTime}");
2349
						log_error("phpDynDNS: updating cache file {$cacheFile}: {$wanip}");
2350
						$need_update = true;
2351
					} else {
2352
						log_error("phpDynDNS: Not updating {$dnsupdate['host']} A record because the IP address has not changed.");
2353
					}
2354
				} else {
2355
					@unlink($cacheFile);
2356
				}
2357

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

    
2375
				$upinst .= "\n";	/* mind that trailing newline! */
2376

    
2377
				if ($need_update) {
2378
					@file_put_contents("{$g['varetc_path']}/nsupdatecmds{$i}", $upinst);
2379
					unset($upinst);
2380
					/* invoke nsupdate */
2381
					$cmd = "/usr/local/bin/nsupdate -k {$g['varetc_path']}/K{$i}{$keyname}+157+00000.key";
2382
					if (isset($dnsupdate['usetcp'])) {
2383
						$cmd .= " -v";
2384
					}
2385
					$cmd .= " {$g['varetc_path']}/nsupdatecmds{$i}";
2386
					mwexec_bg($cmd);
2387
					unset($cmd);
2388
				}
2389
			}
2390
		}
2391
		if (!empty($notify_text)) {
2392
			notify_all_remote($notify_text);
2393
		}
2394
	}
2395

    
2396
	return 0;
2397
}
2398

    
2399
/* configure cron service */
2400
function configure_cron() {
2401
	global $g, $config;
2402

    
2403
	conf_mount_rw();
2404
	/* preserve existing crontab entries */
2405
	$crontab_contents = file("/etc/crontab", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2406

    
2407
	for ($i = 0; $i < count($crontab_contents); $i++) {
2408
		$cron_item =& $crontab_contents[$i];
2409
		if (strpos($cron_item, "# pfSense specific crontab entries") !== false) {
2410
			array_splice($crontab_contents, $i - 1);
2411
			break;
2412
		}
2413
	}
2414
	$crontab_contents = implode("\n", $crontab_contents) . "\n";
2415

    
2416

    
2417
	if (is_array($config['cron']['item'])) {
2418
		$crontab_contents .= "#\n";
2419
		$crontab_contents .= "# " . gettext("pfSense specific crontab entries") . "\n";
2420
		$crontab_contents .= "# " .gettext("Created:") . " " . date("F j, Y, g:i a") . "\n";
2421
		$crontab_contents .= "#\n";
2422

    
2423
		if (isset($config['system']['proxyurl']) && !empty($config['system']['proxyurl'])) {
2424
			$http_proxy = $config['system']['proxyurl'];
2425
			if (isset($config['system']['proxyport']) && !empty($config['system']['proxyport'])) {
2426
				$http_proxy .= ':' . $config['system']['proxyport'];
2427
			}
2428
			$crontab_contents .= "HTTP_PROXY={$http_proxy}";
2429
		}
2430

    
2431
		foreach ($config['cron']['item'] as $item) {
2432
			$crontab_contents .= "\n{$item['minute']}\t";
2433
			$crontab_contents .= "{$item['hour']}\t";
2434
			$crontab_contents .= "{$item['mday']}\t";
2435
			$crontab_contents .= "{$item['month']}\t";
2436
			$crontab_contents .= "{$item['wday']}\t";
2437
			$crontab_contents .= "{$item['who']}\t";
2438
			$crontab_contents .= "{$item['command']}";
2439
		}
2440

    
2441
		$crontab_contents .= "\n#\n";
2442
		$crontab_contents .= "# " . gettext("If possible do not add items to this file manually.") . "\n";
2443
		$crontab_contents .= "# " . gettext("If you do so, this file must be terminated with a blank line (e.g. new line)") . "\n";
2444
		$crontab_contents .= "#\n\n";
2445
	}
2446

    
2447
	/* please maintain the newline at the end of file */
2448
	file_put_contents("/etc/crontab", $crontab_contents);
2449
	unset($crontab_contents);
2450

    
2451
	/* make sure that cron is running and start it if it got killed somehow */
2452
	if (!is_process_running("cron")) {
2453
		exec("cd /tmp && /usr/sbin/cron -s 2>/dev/null");
2454
	} else {
2455
	/* do a HUP kill to force sync changes */
2456
		sigkillbypid("{$g['varrun_path']}/cron.pid", "HUP");
2457
	}
2458

    
2459
	conf_mount_ro();
2460
}
2461

    
2462
function upnp_action ($action) {
2463
	global $g, $config;
2464
	switch ($action) {
2465
		case "start":
2466
			if (file_exists('/var/etc/miniupnpd.conf')) {
2467
				@unlink("{$g['varrun_path']}/miniupnpd.pid");
2468
				mwexec_bg("/usr/local/sbin/miniupnpd -f /var/etc/miniupnpd.conf -P {$g['varrun_path']}/miniupnpd.pid");
2469
			}
2470
			break;
2471
		case "stop":
2472
			killbypid("{$g['varrun_path']}/miniupnpd.pid");
2473
			while ((int)exec("/bin/pgrep -a miniupnpd | wc -l") > 0) {
2474
				mwexec('killall miniupnpd 2>/dev/null', true);
2475
			}
2476
			mwexec('/sbin/pfctl -aminiupnpd -Fr 2>&1 >/dev/null');
2477
			mwexec('/sbin/pfctl -aminiupnpd -Fn 2>&1 >/dev/null');
2478
			break;
2479
		case "restart":
2480
			upnp_action('stop');
2481
			upnp_action('start');
2482
			break;
2483
	}
2484
}
2485

    
2486
function upnp_start() {
2487
	global $config;
2488

    
2489
	if (!isset($config['installedpackages']['miniupnpd']['config'])) {
2490
		return;
2491
	}
2492

    
2493
	if ($config['installedpackages']['miniupnpd']['config'][0]['enable']) {
2494
		echo gettext("Starting UPnP service... ");
2495
		require_once('/usr/local/pkg/miniupnpd.inc');
2496
		sync_package_miniupnpd();
2497
		echo "done.\n";
2498
	}
2499
}
2500

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

    
2504
	$is_installed = false;
2505
	$cron_changed = true;
2506

    
2507
	if (!is_array($config['cron'])) {
2508
		$config['cron'] = array();
2509
	}
2510
	if (!is_array($config['cron']['item'])) {
2511
		$config['cron']['item'] = array();
2512
	}
2513

    
2514
	$x = 0;
2515
	foreach ($config['cron']['item'] as $item) {
2516
		if (strstr($item['command'], $command)) {
2517
			$is_installed = true;
2518
			break;
2519
		}
2520
		$x++;
2521
	}
2522

    
2523
	if ($active) {
2524
		$cron_item = array();
2525
		$cron_item['minute'] = $minute;
2526
		$cron_item['hour'] = $hour;
2527
		$cron_item['mday'] = $monthday;
2528
		$cron_item['month'] = $month;
2529
		$cron_item['wday'] = $weekday;
2530
		$cron_item['who'] = $who;
2531
		$cron_item['command'] = $command;
2532
		if (!$is_installed) {
2533
			$config['cron']['item'][] = $cron_item;
2534
			write_config(sprintf(gettext("Installed cron job for %s"), $command));
2535
		} else {
2536
			if ($config['cron']['item'][$x] == $cron_item) {
2537
				$cron_changed = false;
2538
				log_error(sprintf(gettext("Checked cron job for %s, no change needed"), $command));
2539
			} else {
2540
				$config['cron']['item'][$x] = $cron_item;
2541
				write_config(sprintf(gettext("Updated cron job for %s"), $command));
2542
			}
2543
		}
2544
	} else {
2545
		if ($is_installed == true) {
2546
			unset($config['cron']['item'][$x]);
2547
			write_config(sprintf(gettext("Removed cron job for %s"), $command));
2548
		}
2549
	}
2550

    
2551
	if ($cron_changed) {
2552
		configure_cron();
2553
	}
2554
}
2555

    
2556
?>
(50-50/68)