Project

General

Profile

Download (78.5 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 {\n";
925
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename']}\";\n";
926
				$dhcpdconf .= "	}\n\n";
927
			} elseif (!empty($dhcpifconf['filename'])) {
928
				$dhcpdconf .= "	filename \"{$dhcpifconf['filename']}\";\n";
929
			}
930
			if (!empty($dhcpifconf['rootpath'])) {
931
				$dhcpdconf .= "	option root-path \"{$dhcpifconf['rootpath']}\";\n";
932
			}
933
		}
934

    
935
		$dhcpdconf .= <<<EOD
936
}
937

    
938
EOD;
939

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

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

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

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

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

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

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

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

    
976
				$smdnscfg = "";
977

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

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

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

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

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

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

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

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

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

    
1024
				$dhcpdconf .= "}\n";
1025
				$i++;
1026
			}
1027
		}
1028

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

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

    
1042
		$dhcpdconf .= dhcpdkey($dhcpifconf);
1043
		$dhcpdconf .= dhcpdzones($ddns_zones, $dhcpifconf);
1044
	}
1045

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

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

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

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

    
1069
	if (platform_booting()) {
1070
		print "done.\n";
1071
	}
1072

    
1073
	return 0;
1074
}
1075

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

    
1085
	return $dhcpdconf;
1086
}
1087

    
1088
function dhcpdzones($ddns_zones, $dhcpifconf) {
1089
	$dhcpdconf = "";
1090

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

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

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

    
1138
	return $dhcpdconf;
1139
}
1140

    
1141
function services_dhcpdv6_configure($blacklist = array()) {
1142
	global $config, $g;
1143

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

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

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

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

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

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

    
1189

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

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

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

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

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

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

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

    
1257
	$dhcpdv6conf = <<<EOD
1258

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

    
1271
EOD;
1272

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

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

    
1281
	$dhcpdv6ifs = array();
1282

    
1283
	$dhcpv6num = 0;
1284
	$nsupdate = false;
1285

    
1286
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1287

    
1288
		$ddns_zones = array();
1289

    
1290
		$ifcfgv6 = $config['interfaces'][$dhcpv6if];
1291

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

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

    
1305
		$dnscfgv6 = "";
1306

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

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

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

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

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

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

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

    
1358
EOD;
1359

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

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

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

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

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

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

    
1415
		$dhcpdv6conf .= "}\n";
1416

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

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

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

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

    
1444
				$dhcpdv6conf .= "}\n";
1445
				$i++;
1446
			}
1447
		}
1448

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

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

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

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

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

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

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

    
1508
	return 0;
1509
}
1510

    
1511
function services_igmpproxy_configure() {
1512
	global $config, $g;
1513

    
1514
	/* kill any running igmpproxy */
1515
	killbyname("igmpproxy");
1516

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

    
1521
	$iflist = get_configured_interface_list();
1522

    
1523
	$igmpconf = <<<EOD
1524

    
1525
##------------------------------------------------------
1526
## Enable Quickleave mode (Sends Leave instantly)
1527
##------------------------------------------------------
1528
quickleave
1529

    
1530
EOD;
1531

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

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

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

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

    
1569
	return 0;
1570
}
1571

    
1572
function services_dhcrelay_configure() {
1573
	global $config, $g;
1574

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

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

    
1583
	$dhcrelaycfg =& $config['dhcrelay'];
1584

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

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

    
1596
	$iflist = get_configured_interface_list();
1597

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

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

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

    
1616
	$dhcrelayifs = array_unique($dhcrelayifs);
1617

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

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

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

    
1630
	$cmd .= " " . implode(" ", $srvips);
1631
	mwexec($cmd);
1632
	unset($cmd);
1633

    
1634
	return 0;
1635
}
1636

    
1637
function services_dhcrelay6_configure() {
1638
	global $config, $g;
1639

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

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

    
1648
	$dhcrelaycfg =& $config['dhcrelay6'];
1649

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

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

    
1661
	$iflist = get_configured_interface_list();
1662

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

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

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

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

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

    
1698
	return 0;
1699
}
1700

    
1701
function services_dyndns_configure_client($conf) {
1702

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

    
1707
	/* load up the dyndns.class */
1708
	require_once("dyndns.class");
1709

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

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

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

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

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

    
1761
	return 0;
1762
}
1763

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

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

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

    
1802

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

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

    
1813
	if (isset($config['dnsmasq']['enable'])) {
1814

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

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

    
1826
		$args = "";
1827

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1970
		system_dhcpleases_configure();
1971

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

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

    
1983
	return $return;
1984
}
1985

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

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

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

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

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

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

    
2018
		system_dhcpleases_configure();
2019
	}
2020

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

    
2027
	return $return;
2028
}
2029

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

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

    
2044
	if (isset($config['snmpd']['enable'])) {
2045

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

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

    
2057

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

    
2063
EOD;
2064

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

    
2071
EOD;
2072
		}
2073
*/
2074

    
2075

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

    
2083

    
2084
EOD;
2085
		}
2086

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

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

    
2103
EOD;
2104

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

    
2110
EOD;
2111
		}
2112
*/
2113

    
2114

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

    
2121
EOD;
2122
		}
2123

    
2124

    
2125
		$snmpdconf .= <<<EOD
2126
begemotSnmpdCommunityDisable    = 1
2127

    
2128
EOD;
2129

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

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

    
2150
EOD;
2151

    
2152
		}
2153

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

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

    
2163
snmpEnableAuthenTraps = 2
2164

    
2165
EOD;
2166

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

    
2172
EOD;
2173
			}
2174

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

    
2181
EOD;
2182
			}
2183

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

    
2188
EOD;
2189
			}
2190

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

    
2200
EOD;
2201
				}
2202
				unset($specplatform);
2203
			}
2204

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

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

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

    
2222
EOD;
2223
			}
2224
		}
2225

    
2226
		fwrite($fd, $snmpdconf);
2227
		fclose($fd);
2228
		unset($snmpdconf);
2229

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

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

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

    
2243
	return 0;
2244
}
2245

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

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

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

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

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

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

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

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

    
2302
EOD;
2303
				fwrite($fd, $privkey);
2304
				fclose($fd);
2305

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

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

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

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

    
2335
				// 25 Days
2336
				$maxCacheAgeSecs = 25 * 24 * 60 * 60;
2337
				$need_update = false;
2338

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

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

    
2373
				$upinst .= "\n";	/* mind that trailing newline! */
2374

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

    
2394
	return 0;
2395
}
2396

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

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

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

    
2414

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

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

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

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

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

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

    
2457
	conf_mount_ro();
2458
}
2459

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

    
2484
function upnp_start() {
2485
	global $config;
2486

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

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

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

    
2502
	$is_installed = false;
2503
	$cron_changed = true;
2504

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

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

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

    
2549
	if ($cron_changed) {
2550
		configure_cron();
2551
	}
2552
}
2553

    
2554
?>
(50-50/68)