Project

General

Profile

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
130
		$radvdconf .= "# Generated for DHCPv6 Server $dhcpv6if\n";
131
		$radvdconf .= "interface {$realif} {\n";
132
		if (strstr($realif, "ovpn")) {
133
			$radvdconf .= "\tUnicastOnly on;\n";
134
		}
135
		$radvdconf .= "\tAdvSendAdvert on;\n";
136
		$radvdconf .= "\tMinRtrAdvInterval 5;\n";
137
		$radvdconf .= "\tMaxRtrAdvInterval 20;\n";
138
		$mtu = get_interface_mtu($realif);
139
		if (is_numeric($mtu)) {
140
			$radvdconf .= "\tAdvLinkMTU {$mtu};\n";
141
		} else {
142
			$radvdconf .= "\tAdvLinkMTU 1280;\n";
143
		}
144
		// $radvdconf .= "\tDeprecatePrefix on;\n";
145
		switch ($dhcpv6ifconf['rapriority']) {
146
			case "low":
147
				$radvdconf .= "\tAdvDefaultPreference low;\n";
148
				break;
149
			case "high":
150
				$radvdconf .= "\tAdvDefaultPreference high;\n";
151
				break;
152
			default:
153
				$radvdconf .= "\tAdvDefaultPreference medium;\n";
154
				break;
155
		}
156
		switch ($dhcpv6ifconf['ramode']) {
157
			case "managed":
158
			case "assist":
159
				$radvdconf .= "\tAdvManagedFlag on;\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 "assist":
181
				$radvdconf .= "\t\tAdvOnLink on;\n";
182
				$radvdconf .= "\t\tAdvAutonomous on;\n";
183
				$radvdconf .= "\t\tAdvRouterAddr on;\n";
184
				break;
185
			case "unmanaged":
186
				$radvdconf .= "\t\tAdvOnLink on;\n";
187
				$radvdconf .= "\t\tAdvAutonomous on;\n";
188
				$radvdconf .= "\t\tAdvRouterAddr on;\n";
189
				break;
190
		}
191
		$radvdconf .= "\t};\n";
192

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
461
	if (platform_booting()) {
462
		/* restore the leases, if we have them */
463
		if (file_exists("{$g['cf_conf_path']}/dhcpleases.tgz")) {
464
			$dhcprestore = "";
465
			$dhcpreturn = "";
466
			exec("cd /;LANG=C /usr/bin/tar -xzf {$g['cf_conf_path']}/dhcpleases.tgz 2>&1", $dhcprestore, $dhcpreturn);
467
			$dhcprestore = implode(" ", $dhcprestore);
468
			if ($dhcpreturn <> 0) {
469
				log_error(sprintf(gettext('DHCP leases restore failed exited with %1$s, the error is: %2$s%3$s'), $dhcpreturn, $dhcprestore, "\n"));
470
			}
471
		}
472
		/* 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. */
473
		if (($g['platform'] == "pfSense") && !isset($config['system']['use_mfs_tmpvar'])) {
474
			unlink_if_exists("{$g['cf_conf_path']}/dhcpleases.tgz");
475
		}
476
	}
477

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

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

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

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

    
515
	$dhcpdconf = <<<EOD
516

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

    
530
EOD;
531

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

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

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

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

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

    
561
		interfaces_staticarp_configure($dhcpif);
562

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

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

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

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

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

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

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

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

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

    
650
		$dnscfg = "";
651

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

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

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

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

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

    
721
		$dhcpdconf .= "subnet {$subnet} netmask {$subnetmask} {\n";
722

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

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

    
757
			if ($poolconf['failover_peerip'] <> "") {
758
				$dhcpdconf .= "		deny dynamic bootp clients;\n";
759
			}
760

    
761
			if (isset($poolconf['denyunknown'])) {
762
			   $dhcpdconf .= "		deny unknown-clients;\n";
763
			}
764

    
765
			if ($poolconf['gateway'] && $poolconf['gateway'] != "none" && ($poolconf['gateway'] != $dhcpifconf['gateway'])) {
766
				$dhcpdconf .= "		option routers {$poolconf['gateway']};\n";
767
			}
768

    
769
			if ($dhcpifconf['failover_peerip'] <> "") {
770
				$dhcpdconf .= "		failover peer \"dhcp_{$dhcpif}\";\n";
771
			}
772

    
773
			$pdnscfg = "";
774

    
775
			if ($poolconf['domain'] && ($poolconf['domain'] != $dhcpifconf['domain'])) {
776
				$pdnscfg .= "		option domain-name \"{$poolconf['domain']}\";\n";
777
			}
778

    
779
			if (!empty($poolconf['domainsearchlist']) && ($poolconf['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
780
				$pdnscfg .= "		option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $poolconf['domainsearchlist'])) . "\";\n";
781
			}
782

    
783
			if (isset($poolconf['ddnsupdate'])) {
784
				if (($poolconf['ddnsdomain'] <> "") && ($poolconf['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
785
					$pdnscfg .= "		ddns-domainname \"{$poolconf['ddnsdomain']}\";\n";
786
				}
787
				$pdnscfg .= "		ddns-update-style interim;\n";
788
			}
789

    
790
			if (is_array($poolconf['dnsserver']) && ($poolconf['dnsserver'][0]) && ($poolconf['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
791
				$pdnscfg .= "		option domain-name-servers " . join(",", $poolconf['dnsserver']) . ";\n";
792
			}
793
			$dhcpdconf .= "{$pdnscfg}";
794

    
795
			// default-lease-time
796
			if ($poolconf['defaultleasetime'] && ($poolconf['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) {
797
				$dhcpdconf .= "		default-lease-time {$poolconf['defaultleasetime']};\n";
798
			}
799

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

    
805
			// netbios-name*
806
			if (is_array($poolconf['winsserver']) && $poolconf['winsserver'][0] && ($poolconf['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
807
				$dhcpdconf .= "		option netbios-name-servers " . join(",", $poolconf['winsserver']) . ";\n";
808
				$dhcpdconf .= "		option netbios-node-type 8;\n";
809
			}
810

    
811
			// ntp-servers
812
			if (is_array($poolconf['ntpserver']) && $poolconf['ntpserver'][0] && ($poolconf['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
813
				$dhcpdconf .= "		option ntp-servers " . join(",", $poolconf['ntpserver']) . ";\n";
814
			}
815

    
816
			// tftp-server-name
817
			if (!empty($poolconf['tftp']) && ($poolconf['tftp'] != $dhcpifconf['tftp'])) {
818
				$dhcpdconf .= "		option tftp-server-name \"{$poolconf['tftp']}\";\n";
819
			}
820

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

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

    
843
		if ($dhcpifconf['gateway'] && $dhcpifconf['gateway'] != "none") {
844
			$routers = $dhcpifconf['gateway'];
845
			$add_routers = true;
846
		} elseif ($dhcpifconf['gateway'] == "none") {
847
			$add_routers = false;
848
		} else {
849
			$add_routers = $enable_add_routers;
850
			$routers = $ifcfgip;
851
		}
852
		if ($add_routers) {
853
			$dhcpdconf .= "	option routers {$routers};\n";
854
		}
855

    
856
		$dhcpdconf .= <<<EOD
857
$dnscfg
858

    
859
EOD;
860
		// default-lease-time
861
		if ($dhcpifconf['defaultleasetime']) {
862
			$dhcpdconf .= "	default-lease-time {$dhcpifconf['defaultleasetime']};\n";
863
		}
864

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

    
870
		// netbios-name*
871
		if (is_array($dhcpifconf['winsserver']) && $dhcpifconf['winsserver'][0]) {
872
			$dhcpdconf .= "	option netbios-name-servers " . join(",", $dhcpifconf['winsserver']) . ";\n";
873
			$dhcpdconf .= "	option netbios-node-type 8;\n";
874
		}
875

    
876
		// ntp-servers
877
		if (is_array($dhcpifconf['ntpserver']) && $dhcpifconf['ntpserver'][0]) {
878
			$dhcpdconf .= "	option ntp-servers " . join(",", $dhcpifconf['ntpserver']) . ";\n";
879
		}
880

    
881
		// tftp-server-name
882
		if ($dhcpifconf['tftp'] <> "") {
883
			$dhcpdconf .= "	option tftp-server-name \"{$dhcpifconf['tftp']}\";\n";
884
		}
885

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

    
898
		// ldap-server
899
		if ($dhcpifconf['ldap'] <> "") {
900
			$dhcpdconf .= "	option ldap-server \"{$dhcpifconf['ldap']}\";\n";
901
		}
902

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

    
924
		$dhcpdconf .= <<<EOD
925
}
926

    
927
EOD;
928

    
929
		/* add static mappings */
930
		if (is_array($dhcpifconf['staticmap'])) {
931

    
932
			$i = 0;
933
			foreach ($dhcpifconf['staticmap'] as $sm) {
934
				$dhcpdconf .= "host s_{$dhcpif}_{$i} {\n";
935

    
936
				if ($sm['mac']) {
937
					$dhcpdconf .= "        hardware ethernet {$sm['mac']};\n";
938
				}
939

    
940
				if ($sm['cid']) {
941
					$dhcpdconf .= "        option dhcp-client-identifier \"{$sm['cid']}\";\n";
942
				}
943

    
944
				if ($sm['ipaddr']) {
945
					$dhcpdconf .= "	fixed-address {$sm['ipaddr']};\n";
946
				}
947

    
948
				if ($sm['hostname']) {
949
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
950
					$dhhostname = str_replace(".", "_", $dhhostname);
951
					$dhcpdconf .= "	option host-name \"{$dhhostname}\";\n";
952
				}
953
				if ($sm['filename']) {
954
					$dhcpdconf .= "	filename \"{$sm['filename']}\";\n";
955
				}
956

    
957
				if ($sm['rootpath']) {
958
					$dhcpdconf .= "	option root-path \"{$sm['rootpath']}\";\n";
959
				}
960

    
961
				if ($sm['gateway'] && ($sm['gateway'] != $dhcpifconf['gateway'])) {
962
					$dhcpdconf .= "	option routers {$sm['gateway']};\n";
963
				}
964

    
965
				$smdnscfg = "";
966

    
967
				if ($sm['domain'] && ($sm['domain'] != $dhcpifconf['domain'])) {
968
					$smdnscfg .= "	option domain-name \"{$sm['domain']}\";\n";
969
				}
970

    
971
				if (!empty($sm['domainsearchlist']) && ($sm['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
972
					$smdnscfg .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $sm['domainsearchlist'])) . "\";\n";
973
				}
974

    
975
				if (isset($sm['ddnsupdate'])) {
976
					if (($sm['ddnsdomain'] <> "") && ($sm['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
977
						$pdnscfg .= "		ddns-domainname \"{$sm['ddnsdomain']}\";\n";
978
					}
979
					$pdnscfg .= "		ddns-update-style interim;\n";
980
				}
981

    
982
				if (is_array($sm['dnsserver']) && ($sm['dnsserver'][0]) && ($sm['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
983
					$smdnscfg .= "	option domain-name-servers " . join(",", $sm['dnsserver']) . ";\n";
984
				}
985
				$dhcpdconf .= "{$smdnscfg}";
986

    
987
				// default-lease-time
988
				if ($sm['defaultleasetime'] && ($sm['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) {
989
					$dhcpdconf .= "	default-lease-time {$sm['defaultleasetime']};\n";
990
				}
991

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

    
997
				// netbios-name*
998
				if (is_array($sm['winsserver']) && $sm['winsserver'][0] && ($sm['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
999
					$dhcpdconf .= "	option netbios-name-servers " . join(",", $sm['winsserver']) . ";\n";
1000
					$dhcpdconf .= "	option netbios-node-type 8;\n";
1001
				}
1002

    
1003
				// ntp-servers
1004
				if (is_array($sm['ntpserver']) && $sm['ntpserver'][0] && ($sm['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
1005
					$dhcpdconf .= "	option ntp-servers " . join(",", $sm['ntpserver']) . ";\n";
1006
				}
1007

    
1008
				// tftp-server-name
1009
				if (!empty($sm['tftp']) && ($sm['tftp'] != $dhcpifconf['tftp'])) {
1010
					$dhcpdconf .= "	option tftp-server-name \"{$sm['tftp']}\";\n";
1011
				}
1012

    
1013
				$dhcpdconf .= "}\n";
1014
				$i++;
1015
			}
1016
		}
1017

    
1018
		$dhcpdifs[] = get_real_interface($dhcpif);
1019
		if ($newzone['domain-name']) {
1020
			if ($need_ddns_updates) {
1021
				$newzone['dns-servers'] = array($dhcpifconf['ddnsdomainprimary']);
1022
			}
1023
			$ddns_zones[] = $newzone;
1024
		}
1025
	}
1026

    
1027
	if ($need_ddns_updates) {
1028
		$dhcpdconf .= "ddns-update-style interim;\n";
1029
		$dhcpdconf .= "update-static-leases on;\n";
1030

    
1031
		$dhcpdconf .= dhcpdkey($dhcpifconf);
1032
		$dhcpdconf .= dhcpdzones($ddns_zones, $dhcpifconf);
1033
	}
1034

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

    
1043
	/* create an empty leases database */
1044
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases")) {
1045
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases");
1046
	}
1047

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

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

    
1058
	if (platform_booting()) {
1059
		print "done.\n";
1060
	}
1061

    
1062
	return 0;
1063
}
1064

    
1065
function dhcpdkey($dhcpifconf)
1066
{
1067
	$dhcpdconf = "";
1068
	if ($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "") {
1069
		$dhcpdconf .= "key {$dhcpifconf['ddnsdomainkeyname']} {\n";
1070
		$dhcpdconf .= "	algorithm hmac-md5;\n";
1071
		$dhcpdconf .= "	secret {$dhcpifconf['ddnsdomainkey']};\n";
1072
		$dhcpdconf .= "}\n";
1073
	}
1074

    
1075
	return $dhcpdconf;
1076
}
1077

    
1078
function dhcpdzones($ddns_zones, $dhcpifconf)
1079
{
1080
	$dhcpdconf = "";
1081

    
1082
	if (is_array($ddns_zones)) {
1083
		$added_zones = array();
1084
		foreach ($ddns_zones as $zone) {
1085
			if (!is_array($zone) || empty($zone) || !is_array($zone['dns-servers'])) {
1086
				continue;
1087
			}
1088
			$primary = $zone['dns-servers'][0];
1089
			$secondary = empty($zone['dns-servers'][1]) ? "" : $zone['dns-servers'][1];
1090

    
1091
			// Make sure we aren't using any invalid or IPv6 DNS servers.
1092
			if (!is_ipaddrv4($primary)) {
1093
				if (is_ipaddrv4($secondary)) {
1094
					$primary = $secondary;
1095
					$secondary = "";
1096
				} else {
1097
					continue;
1098
				}
1099
			}
1100

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

    
1129
	return $dhcpdconf;
1130
}
1131

    
1132
function services_dhcpdv6_configure($blacklist = array()) {
1133
	global $config, $g;
1134

    
1135
	if ($g['services_dhcp_server_enable'] == false) {
1136
		return;
1137
	}
1138

    
1139
	if (isset($config['system']['developerspew'])) {
1140
		$mt = microtime();
1141
		echo "services_dhcpd_configure($if) being called $mt\n";
1142
	}
1143

    
1144
	/* kill any running dhcpd */
1145
	if (isvalidpid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid")) {
1146
		killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid");
1147
	}
1148
	if (isvalidpid("{$g['varrun_path']}/dhcpleases6.pid")) {
1149
		killbypid("{$g['varrun_path']}/dhcpleases6.pid");
1150
	}
1151

    
1152
	/* DHCP enabled on any interfaces? */
1153
	if (!is_dhcpv6_server_enabled()) {
1154
		return 0;
1155
	}
1156

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

    
1172
	$syscfg = $config['system'];
1173
	if (!is_array($config['dhcpdv6'])) {
1174
		$config['dhcpdv6'] = array();
1175
	}
1176
	$dhcpdv6cfg = $config['dhcpdv6'];
1177
	$Iflist = get_configured_interface_list();
1178
	$Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces());
1179

    
1180

    
1181
	if (platform_booting()) {
1182
		echo "Starting DHCPv6 service...";
1183
	} else {
1184
		sleep(1);
1185
	}
1186

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

    
1220
				/* set the delegation start to half the current address block */
1221
				$range = Net_IPv6::parseAddress($ifcfgipv6, (64 - $pdlenmax));
1222
				$range['start'] = Net_IPv6::getNetmask($range['end'], (64 - $pdlenhalf));
1223

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

    
1228
				$dhcpdv6cfg[$ifname]['prefixrange']['from'] = Net_IPv6::compress($range['start']);
1229
				$dhcpdv6cfg[$ifname]['prefixrange']['to'] = Net_IPv6::compress($range['end']);
1230
			}
1231
			$dhcpdv6cfg[$ifname]['dns6ip'] = get_interface_ipv6($ifname);
1232
		}
1233
	}
1234

    
1235
	$custoptionsv6 = "";
1236
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1237
		if (is_array($dhcpv6ifconf['numberoptions']) && is_array($dhcpv6ifconf['numberoptions']['item'])) {
1238
			foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
1239
				$custoptionsv6 .= "option custom-{$dhcpv6if}-{$itemv6idx} code {$itemv6['number']} = text;\n";
1240
			}
1241
		}
1242
	}
1243

    
1244
	if (isset($dhcpv6ifconf['netboot']) && !empty($dhcpv6ifconf['bootfile_url'])) {
1245
		$custoptionsv6 .= "option dhcp6.bootfile-url code 59 = string;\n";
1246
	}
1247

    
1248
	$dhcpdv6conf = <<<EOD
1249

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

    
1262
EOD;
1263

    
1264
	if (!isset($dhcpv6ifconf['disableauthoritative'])) {
1265
		$dhcpdv6conf .= "authoritative;\n";
1266
	}
1267

    
1268
	if (isset($dhcpv6ifconf['alwaysbroadcast'])) {
1269
		$dhcpdv6conf .= "always-broadcast on\n";
1270
	}
1271

    
1272
	$dhcpdv6ifs = array();
1273

    
1274
	$dhcpv6num = 0;
1275
	$nsupdate = false;
1276

    
1277
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1278

    
1279
		$ddns_zones = array();
1280

    
1281
		$ifcfgv6 = $config['interfaces'][$dhcpv6if];
1282

    
1283
		if (!isset($dhcpv6ifconf['enable']) || !isset($Iflist[$dhcpv6if]) || !isset($ifcfgv6['enable'])) {
1284
			continue;
1285
		}
1286
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1287
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
1288
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1289

    
1290
		if ($is_olsr_enabled == true) {
1291
			if ($dhcpv6ifconf['netmask']) {
1292
				$subnetmask = gen_subnet_maskv6($dhcpv6ifconf['netmask']);
1293
			}
1294
		}
1295

    
1296
		$dnscfgv6 = "";
1297

    
1298
		if ($dhcpv6ifconf['domain']) {
1299
			$dnscfgv6 .= "	option domain-name \"{$dhcpv6ifconf['domain']}\";\n";
1300
		}
1301

    
1302
		if ($dhcpv6ifconf['domainsearchlist'] <> "") {
1303
			$dnscfgv6 .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpv6ifconf['domainsearchlist'])) . "\";\n";
1304
		}
1305

    
1306
		if (isset($dhcpv6ifconf['ddnsupdate'])) {
1307
			if ($dhcpv6ifconf['ddnsdomain'] <> "") {
1308
				$dnscfgv6 .= "	ddns-domainname \"{$dhcpv6ifconf['ddnsdomain']}\";\n";
1309
			}
1310
			$dnscfgv6 .= "	ddns-update-style interim;\n";
1311
			$nsupdate = true;
1312
		}
1313

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

    
1330
		if ($dhcpv6ifconf['domain']) {
1331
			$newzone = array();
1332
			$newzone['domain-name'] = $dhcpv6ifconf['domain'];
1333
			$newzone['dns-servers'][] = $dhcpv6ifconf['ddnsdomainprimary'];
1334
			$ddns_zones[] = $newzone;
1335
		}
1336

    
1337
		if (is_ipaddrv6($ifcfgipv6)) {
1338
			$dhcpdv6conf .= "subnet6 {$subnetv6}/{$ifcfgsnv6}";
1339
		} else {
1340
			$subnet6 = gen_subnetv6($dhcpv6ifconf['range']['from'], "64");
1341
			$dhcpdv6conf .= "subnet6 {$subnet6}/64";
1342
		}
1343
		$dhcpdv6conf .= " {\n";
1344

    
1345
		$dhcpdv6conf .= <<<EOD
1346
	range6 {$dhcpv6ifconf['range']['from']} {$dhcpv6ifconf['range']['to']};
1347
$dnscfgv6
1348

    
1349
EOD;
1350

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

    
1362
		// max-lease-time
1363
		if ($dhcpv6ifconf['maxleasetime']) {
1364
			$dhcpdv6conf .= "	max-lease-time {$dhcpv6ifconf['maxleasetime']};\n";
1365
		}
1366

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

    
1386
		// Handle option, number rowhelper values
1387
		$dhcpdv6conf .= "\n";
1388
		if ($dhcpv6ifconf['numberoptions']['item']) {
1389
			foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
1390
				$dhcpdv6conf .= "	option custom-{$dhcpv6if}-{$itemv6idx} \"{$itemv6['value']}\";\n";
1391
			}
1392
		}
1393

    
1394
		// ldap-server
1395
		if ($dhcpv6ifconf['ldap'] <> "") {
1396
			$dhcpdv6conf .= "	option ldap-server \"{$dhcpv6ifconf['ldap']}\";\n";
1397
		}
1398

    
1399
		// net boot information
1400
		if (isset($dhcpv6ifconf['netboot'])) {
1401
			if (!empty($dhcpv6ifconf['bootfile_url'])) {
1402
				$dhcpdv6conf .= "	option dhcp6.bootfile-url \"{$dhcpv6ifconf['bootfile_url']}\";\n";
1403
			}
1404
		}
1405

    
1406
		$dhcpdv6conf .= "}\n";
1407

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

    
1417
EOD;
1418
				if ($sm['ipaddrv6']) {
1419
					$dhcpdv6conf .= "	fixed-address6 {$sm['ipaddrv6']};\n";
1420
				}
1421

    
1422
				if ($sm['hostname']) {
1423
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
1424
					$dhhostname = str_replace(".", "_", $dhhostname);
1425
					$dhcpdv6conf .= "	option host-name {$dhhostname};\n";
1426
				}
1427
				if ($sm['filename']) {
1428
					$dhcpdv6conf .= "	filename \"{$sm['filename']}\";\n";
1429
				}
1430

    
1431
				if ($sm['rootpath']) {
1432
					$dhcpdv6conf .= "	option root-path \"{$sm['rootpath']}\";\n";
1433
				}
1434

    
1435
				$dhcpdv6conf .= "}\n";
1436
				$i++;
1437
			}
1438
		}
1439

    
1440
		if ($dhcpv6ifconf['domain']) {
1441
			$dhcpdv6conf .= dhcpdkey($dhcpv6ifconf);
1442
			$dhcpdv6conf .= dhcpdzones($ddns_zones, $dhcpv6ifconf);
1443
		}
1444

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

    
1463
	if ($nsupdate) {
1464
		$dhcpdv6conf .= "ddns-update-style interim;\n";
1465
	} else {
1466
		$dhcpdv6conf .= "ddns-update-style none;\n";
1467
	}
1468

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

    
1480
	/* create an empty leases v6 database */
1481
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases")) {
1482
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases");
1483
	}
1484

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

    
1489
	/* fire up dhcpd in a chroot */
1490
	if (count($dhcpdv6ifs) > 0) {
1491
		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 " .
1492
			join(" ", $dhcpdv6ifs));
1493
		mwexec("/usr/local/sbin/dhcpleases6 -c \"/usr/local/bin/php -f /usr/local/sbin/prefixes.php|/bin/sh\" -l {$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases");
1494
	}
1495
	if (platform_booting()) {
1496
		print gettext("done.") . "\n";
1497
	}
1498

    
1499
	return 0;
1500
}
1501

    
1502
function services_igmpproxy_configure() {
1503
	global $config, $g;
1504

    
1505
	/* kill any running igmpproxy */
1506
	killbyname("igmpproxy");
1507

    
1508
	if (!is_array($config['igmpproxy']['igmpentry']) || (count($config['igmpproxy']['igmpentry']) == 0)) {
1509
		return 1;
1510
	}
1511

    
1512
	$iflist = get_configured_interface_list();
1513

    
1514
	$igmpconf = <<<EOD
1515

    
1516
##------------------------------------------------------
1517
## Enable Quickleave mode (Sends Leave instantly)
1518
##------------------------------------------------------
1519
quickleave
1520

    
1521
EOD;
1522

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

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

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

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

    
1560
	return 0;
1561
}
1562

    
1563
function services_dhcrelay_configure() {
1564
	global $config, $g;
1565
	if ($g['platform'] == 'jail') {
1566
		return;
1567
	}
1568
	if (isset($config['system']['developerspew'])) {
1569
		$mt = microtime();
1570
		echo "services_dhcrelay_configure() being called $mt\n";
1571
	}
1572

    
1573
	/* kill any running dhcrelay */
1574
	killbypid("{$g['varrun_path']}/dhcrelay.pid");
1575

    
1576
	$dhcrelaycfg =& $config['dhcrelay'];
1577

    
1578
	/* DHCPRelay enabled on any interfaces? */
1579
	if (!isset($dhcrelaycfg['enable'])) {
1580
		return 0;
1581
	}
1582

    
1583
	if (platform_booting()) {
1584
		echo gettext("Starting DHCP relay service...");
1585
	} else {
1586
		sleep(1);
1587
	}
1588

    
1589
	$iflist = get_configured_interface_list();
1590

    
1591
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1592
	foreach ($dhcifaces as $dhcrelayif) {
1593
		if (!isset($iflist[$dhcrelayif]) ||
1594
		    link_interface_to_bridge($dhcrelayif)) {
1595
			continue;
1596
		}
1597

    
1598
		if (is_ipaddr(get_interface_ip($dhcrelayif))) {
1599
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1600
		}
1601
	}
1602

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

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

    
1656
		if (!isset($destif)) {
1657
			if (is_array($config['gateways']['gateway_item'])) {
1658
				foreach ($config['gateways']['gateway_item'] as $gateway) {
1659
					if (isset($gateway['defaultgw'])) {
1660
						$destif = get_real_interface($gateway['interface']);
1661
						break;
1662
					}
1663
				}
1664
			} else {
1665
				$destif = get_real_interface("wan");
1666
			}
1667
		}
1668

    
1669
		if (!empty($destif)) {
1670
			$dhcrelayifs[] = $destif;
1671
		}
1672
	}
1673
	$dhcrelayifs = array_unique($dhcrelayifs);
1674

    
1675
	/* fire up dhcrelay */
1676
	if (empty($dhcrelayifs)) {
1677
		log_error("No suitable interface found for running dhcrelay!");
1678
		return; /* XXX */
1679
	}
1680

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

    
1683
	if (isset($dhcrelaycfg['agentoption'])) {
1684
		$cmd .=  " -a -m replace";
1685
	}
1686

    
1687
	$cmd .= " " . implode(" ", $srvips);
1688
	mwexec($cmd);
1689
	unset($cmd);
1690

    
1691
	return 0;
1692
}
1693

    
1694
function services_dhcrelay6_configure() {
1695
	global $config, $g;
1696
	if ($g['platform'] == 'jail') {
1697
		return;
1698
	}
1699
	if (isset($config['system']['developerspew'])) {
1700
		$mt = microtime();
1701
		echo "services_dhcrelay6_configure() being called $mt\n";
1702
	}
1703

    
1704
	/* kill any running dhcrelay */
1705
	killbypid("{$g['varrun_path']}/dhcrelay6.pid");
1706

    
1707
	$dhcrelaycfg =& $config['dhcrelay6'];
1708

    
1709
	/* DHCPv6 Relay enabled on any interfaces? */
1710
	if (!isset($dhcrelaycfg['enable'])) {
1711
		return 0;
1712
	}
1713

    
1714
	if (platform_booting()) {
1715
		echo gettext("Starting DHCPv6 relay service...");
1716
	} else {
1717
		sleep(1);
1718
	}
1719

    
1720
	$iflist = get_configured_interface_list();
1721

    
1722
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1723
	foreach ($dhcifaces as $dhcrelayif) {
1724
		if (!isset($iflist[$dhcrelayif]) ||
1725
		    link_interface_to_bridge($dhcrelayif))
1726
			continue;
1727

    
1728
		if (is_ipaddrv6(get_interface_ipv6($dhcrelayif))) {
1729
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1730
		}
1731
	}
1732
	$dhcrelayifs = array_unique($dhcrelayifs);
1733

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

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

    
1782
		if (!isset($destif)) {
1783
			if (is_array($config['gateways']['gateway_item'])) {
1784
				foreach ($config['gateways']['gateway_item'] as $gateway) {
1785
					if (isset($gateway['defaultgw'])) {
1786
						$destif = get_real_interface($gateway['interface']);
1787
						break;
1788
					}
1789
				}
1790
			} else {
1791
				$destif = get_real_interface("wan");
1792
			}
1793
		}
1794

    
1795
		if (!empty($destif)) {
1796
			$srvifaces[] = "{$srvip}%{$destif}";
1797
		}
1798
	}
1799

    
1800
	/* fire up dhcrelay */
1801
	if (empty($dhcrelayifs) || empty($srvifaces)) {
1802
		log_error("No suitable interface found for running dhcrelay -6!");
1803
		return; /* XXX */
1804
	}
1805

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

    
1816
	return 0;
1817
}
1818

    
1819
function services_dyndns_configure_client($conf) {
1820

    
1821
	if (!isset($conf['enable'])) {
1822
		return;
1823
	}
1824

    
1825
	/* load up the dyndns.class */
1826
	require_once("dyndns.class");
1827

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

    
1850
function services_dyndns_configure($int = "") {
1851
	global $config, $g;
1852
	if (isset($config['system']['developerspew'])) {
1853
		$mt = microtime();
1854
		echo "services_dyndns_configure() being called $mt\n";
1855
	}
1856

    
1857
	$dyndnscfg = $config['dyndnses']['dyndns'];
1858
	$gwgroups = return_gateway_groups_array();
1859
	if (is_array($dyndnscfg)) {
1860
		if (platform_booting()) {
1861
			echo gettext("Starting DynDNS clients...");
1862
		}
1863

    
1864
		foreach ($dyndnscfg as $dyndns) {
1865
			if ((empty($int)) || ($int == $dyndns['interface']) || (is_array($gwgroups[$dyndns['interface']]))) {
1866
				$dyndns['verboselog'] = isset($dyndns['verboselog']);
1867
				$dyndns['curl_ipresolve_v4'] = isset($dyndns['curl_ipresolve_v4']);
1868
				$dyndns['curl_ssl_verifypeer'] = isset($dyndns['curl_ssl_verifypeer']);
1869
				services_dyndns_configure_client($dyndns);
1870
				sleep(1);
1871
			}
1872
		}
1873

    
1874
		if (platform_booting()) {
1875
			echo gettext("done.") . "\n";
1876
		}
1877
	}
1878

    
1879
	return 0;
1880
}
1881

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

    
1909
function services_dnsmasq_configure() {
1910
	global $config, $g;
1911
	$return = 0;
1912

    
1913
	// hard coded args: will be removed to avoid duplication if specified in custom_options
1914
	$standard_args = array(
1915
		"dns-forward-max" => "--dns-forward-max=5000",
1916
		"cache-size" => "--cache-size=10000",
1917
		"local-ttl" => "--local-ttl=1"
1918
	);
1919

    
1920

    
1921
	if (isset($config['system']['developerspew'])) {
1922
		$mt = microtime();
1923
		echo "services_dnsmasq_configure() being called $mt\n";
1924
	}
1925

    
1926
	/* kill any running dnsmasq */
1927
	if (file_exists("{$g['varrun_path']}/dnsmasq.pid")) {
1928
		sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM");
1929
	}
1930

    
1931
	if (isset($config['dnsmasq']['enable'])) {
1932

    
1933
		if (platform_booting()) {
1934
			echo gettext("Starting DNS forwarder...");
1935
		} else {
1936
			sleep(1);
1937
		}
1938

    
1939
		/* generate hosts file */
1940
		if (system_hosts_generate()!=0) {
1941
			$return = 1;
1942
		}
1943

    
1944
		$args = "";
1945

    
1946
		if (isset($config['dnsmasq']['regdhcp'])) {
1947
			$args .= " --dhcp-hostsfile={$g['varetc_path']}/hosts ";
1948
		}
1949

    
1950
		/* Setup listen port, if non-default */
1951
		if (is_port($config['dnsmasq']['port'])) {
1952
			$args .= " --port={$config['dnsmasq']['port']} ";
1953
		}
1954

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

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

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

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

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

    
2040
		/* Setup forwarded domains */
2041
		if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
2042
			foreach ($config['dnsmasq']['domainoverrides'] as $override) {
2043
				if ($override['ip'] == "!") {
2044
					$override[ip] = "";
2045
				}
2046
				$args .= ' --server=/' . $override['domain'] . '/' . $override['ip'];
2047
			}
2048
		}
2049

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

    
2059
		if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
2060
			$dns_rebind = "--rebind-localhost-ok --stop-dns-rebind";
2061
		}
2062

    
2063
		if (isset($config['dnsmasq']['strict_order'])) {
2064
			$args .= " --strict-order ";
2065
		}
2066

    
2067
		if (isset($config['dnsmasq']['domain_needed'])) {
2068
			$args .= " --domain-needed ";
2069
		}
2070

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

    
2082
		/* run dnsmasq */
2083
		$cmd = "/usr/local/sbin/dnsmasq --all-servers {$dns_rebind} {$args}";
2084
		//log_error("dnsmasq command: {$cmd}");
2085
		mwexec_bg($cmd);
2086
		unset($args);
2087

    
2088
		system_dhcpleases_configure();
2089

    
2090
		if (platform_booting()) {
2091
			echo gettext("done.") . "\n";
2092
		}
2093
	}
2094

    
2095
	if (!platform_booting()) {
2096
		if (services_dhcpd_configure()!=0) {
2097
			$return = 1;
2098
		}
2099
	}
2100

    
2101
	return $return;
2102
}
2103

    
2104
function services_unbound_configure() {
2105
	global $config, $g;
2106
	$return = 0;
2107

    
2108
	if (isset($config['system']['developerspew'])) {
2109
		$mt = microtime();
2110
		echo "services_unbound_configure() being called $mt\n";
2111
	}
2112

    
2113
	// kill any running Unbound instance
2114
	if (file_exists("{$g['varrun_path']}/unbound.pid")) {
2115
		sigkillbypid("{$g['varrun_path']}/unbound.pid", "TERM");
2116
	}
2117

    
2118
	if (isset($config['unbound']['enable'])) {
2119
		if (platform_booting()) {
2120
			echo gettext("Starting DNS Resolver...");
2121
		} else {
2122
			sleep(1);
2123
		}
2124

    
2125
		/* generate hosts file */
2126
		if (system_hosts_generate()!=0) {
2127
			$return = 1;
2128
		}
2129

    
2130
		require_once('/etc/inc/unbound.inc');
2131
		sync_unbound_service();
2132
		if (platform_booting()) {
2133
			echo gettext("done.") . "\n";
2134
		}
2135

    
2136
		system_dhcpleases_configure();
2137
	}
2138

    
2139
	if (!platform_booting()) {
2140
		if (services_dhcpd_configure()!=0) {
2141
			$return = 1;
2142
		}
2143
	}
2144

    
2145
	return $return;
2146
}
2147

    
2148
function services_snmpd_configure() {
2149
	global $config, $g;
2150
	if (isset($config['system']['developerspew'])) {
2151
		$mt = microtime();
2152
		echo "services_snmpd_configure() being called $mt\n";
2153
	}
2154

    
2155
	/* kill any running snmpd */
2156
	sigkillbypid("{$g['varrun_path']}/snmpd.pid", "TERM");
2157
	sleep(2);
2158
	if (is_process_running("bsnmpd")) {
2159
		mwexec("/usr/bin/killall bsnmpd", true);
2160
	}
2161

    
2162
	if (isset($config['snmpd']['enable'])) {
2163

    
2164
		if (platform_booting()) {
2165
			echo gettext("Starting SNMP daemon... ");
2166
		}
2167

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

    
2175

    
2176
		$snmpdconf = <<<EOD
2177
location := "{$config['snmpd']['syslocation']}"
2178
contact := "{$config['snmpd']['syscontact']}"
2179
read := "{$config['snmpd']['rocommunity']}"
2180

    
2181
EOD;
2182

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

    
2189
EOD;
2190
		}
2191
*/
2192

    
2193

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

    
2201

    
2202
EOD;
2203
		}
2204

    
2205
		$version = trim(file_get_contents('/etc/version'));
2206
		$platform = trim(file_get_contents('/etc/platform'));
2207
		if (($platform == "pfSense") && ($g['product_name'] != "pfSense")) {
2208
			$platform = $g['product_name'];
2209
		}
2210
		$sysDescr = "{$g['product_name']} " . php_uname("n") .
2211
			" {$version} {$platform} " . php_uname("s") .
2212
			" " . php_uname("r") . " " . php_uname("m");
2213

    
2214
		$snmpdconf .= <<<EOD
2215
system := 1     # pfSense
2216
%snmpd
2217
sysDescr			= "{$sysDescr}"
2218
begemotSnmpdDebugDumpPdus       = 2
2219
begemotSnmpdDebugSyslogPri      = 7
2220
begemotSnmpdCommunityString.0.1 = $(read)
2221

    
2222
EOD;
2223

    
2224
/* No docs on what write strings do there for disable for now.
2225
		if (isset($config['snmpd']['rwcommunity']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
2226
			$snmpdconf .= <<<EOD
2227
begemotSnmpdCommunityString.0.2 = $(write)
2228

    
2229
EOD;
2230
		}
2231
*/
2232

    
2233

    
2234
		if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) {
2235
			$snmpdconf .= <<<EOD
2236
begemotTrapSinkStatus.[$(traphost)].$(trapport) = 4
2237
begemotTrapSinkVersion.[$(traphost)].$(trapport) = 2
2238
begemotTrapSinkComm.[$(traphost)].$(trapport) = $(trap)
2239

    
2240
EOD;
2241
		}
2242

    
2243

    
2244
		$snmpdconf .= <<<EOD
2245
begemotSnmpdCommunityDisable    = 1
2246

    
2247
EOD;
2248

    
2249
		if (isset($config['snmpd']['bindlan'])) {
2250
			$config['snmpd']['bindip'] = 'lan';
2251
			unset($config['snmpd']['bindlan']);
2252
		}
2253
		$bind_to_ip = "0.0.0.0";
2254
		if (isset($config['snmpd']['bindip'])) {
2255
			if (is_ipaddr($config['snmpd']['bindip'])) {
2256
				$bind_to_ip = $config['snmpd']['bindip'];
2257
			} else {
2258
				$if = get_real_interface($config['snmpd']['bindip']);
2259
				if (does_interface_exist($if)) {
2260
					$bind_to_ip = get_interface_ip($config['snmpd']['bindip']);
2261
				}
2262
			}
2263
		}
2264

    
2265
		if (is_port($config['snmpd']['pollport'])) {
2266
			$snmpdconf .= <<<EOD
2267
begemotSnmpdPortStatus.{$bind_to_ip}.{$config['snmpd']['pollport']} = 1
2268

    
2269
EOD;
2270

    
2271
		}
2272

    
2273
		$snmpdconf .= <<<EOD
2274
begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1
2275
begemotSnmpdLocalPortType."/var/run/snmpd.sock" = 4
2276

    
2277
# These are bsnmp macros not php vars.
2278
sysContact      = $(contact)
2279
sysLocation     = $(location)
2280
sysObjectId     = 1.3.6.1.4.1.12325.1.1.2.1.$(system)
2281

    
2282
snmpEnableAuthenTraps = 2
2283

    
2284
EOD;
2285

    
2286
		if (is_array($config['snmpd']['modules'])) {
2287
			if (isset($config['snmpd']['modules']['mibii'])) {
2288
			$snmpdconf .= <<<EOD
2289
begemotSnmpdModulePath."mibII"  = "/usr/lib/snmp_mibII.so"
2290

    
2291
EOD;
2292
			}
2293

    
2294
			if (isset($config['snmpd']['modules']['netgraph'])) {
2295
				$snmpdconf .= <<<EOD
2296
begemotSnmpdModulePath."netgraph" = "/usr/lib/snmp_netgraph.so"
2297
%netgraph
2298
begemotNgControlNodeName = "snmpd"
2299

    
2300
EOD;
2301
			}
2302

    
2303
			if (isset($config['snmpd']['modules']['pf'])) {
2304
				$snmpdconf .= <<<EOD
2305
begemotSnmpdModulePath."pf"     = "/usr/lib/snmp_pf.so"
2306

    
2307
EOD;
2308
			}
2309

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

    
2319
EOD;
2320
				}
2321
				unset($specplatform);
2322
			}
2323

    
2324
			if (isset($config['snmpd']['modules']['bridge'])) {
2325
				$snmpdconf .= <<<EOD
2326
begemotSnmpdModulePath."bridge"     = "/usr/lib/snmp_bridge.so"
2327
# config must end with blank line
2328

    
2329
EOD;
2330
			}
2331
			if (isset($config['snmpd']['modules']['ucd'])) {
2332
				$snmpdconf .= <<<EOD
2333
begemotSnmpdModulePath."ucd"     = "/usr/local/lib/snmp_ucd.so"
2334

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

    
2341
EOD;
2342
			}
2343
		}
2344

    
2345
		fwrite($fd, $snmpdconf);
2346
		fclose($fd);
2347
		unset($snmpdconf);
2348

    
2349
		if (isset($config['snmpd']['bindlan'])) {
2350
			$bindlan = "";
2351
		}
2352

    
2353
		/* run bsnmpd */
2354
		mwexec("/usr/sbin/bsnmpd -c {$g['varetc_path']}/snmpd.conf" .
2355
			"{$bindlan} -p {$g['varrun_path']}/snmpd.pid");
2356

    
2357
		if (platform_booting()) {
2358
			echo gettext("done.") . "\n";
2359
		}
2360
	}
2361

    
2362
	return 0;
2363
}
2364

    
2365
function services_dnsupdate_process($int = "", $updatehost = "", $forced = false) {
2366
	global $config, $g;
2367
	if (isset($config['system']['developerspew'])) {
2368
		$mt = microtime();
2369
		echo "services_dnsupdate_process() being called $mt\n";
2370
	}
2371

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

    
2386
			/* determine interface name */
2387
			$if = get_real_interface($dnsupdate['interface']);
2388

    
2389
			if (isset($dnsupdate['usepublicip'])) {
2390
				$wanip = dyndnsCheckIP($dnsupdate['interface']);
2391
			} else {
2392
				$wanip = get_interface_ip($dnsupdate['interface']);
2393
			}
2394

    
2395
			$wanipv6 = get_interface_ipv6($dnsupdate['interface']);
2396
			$cacheFile = "{$g['conf_path']}/dyndns_{$dnsupdate['interface']}_rfc2136_" . escapeshellarg($dnsupdate['host']) . "_{$dnsupdate['server']}.cache";
2397
			$currentTime = time();
2398

    
2399
			if ($wanip || $wanipv6) {
2400
				$keyname = $dnsupdate['keyname'];
2401
				/* trailing dot */
2402
				if (substr($keyname, -1) != ".") {
2403
					$keyname .= ".";
2404
				}
2405

    
2406
				$hostname = $dnsupdate['host'];
2407
				/* trailing dot */
2408
				if (substr($hostname, -1) != ".") {
2409
					$hostname .= ".";
2410
				}
2411

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

    
2421
EOD;
2422
				fwrite($fd, $privkey);
2423
				fclose($fd);
2424

    
2425
				/* write public key file */
2426
				if ($dnsupdate['keytype'] == "zone") {
2427
					$flags = 257;
2428
					$proto = 3;
2429
				} else if ($dnsupdate['keytype'] == "host") {
2430
					$flags = 513;
2431
					$proto = 3;
2432
				} else if ($dnsupdate['keytype'] == "user") {
2433
					$flags = 0;
2434
					$proto = 2;
2435
				}
2436

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

    
2441
				/* generate update instructions */
2442
				$upinst = "";
2443
				if (!empty($dnsupdate['server'])) {
2444
					$upinst .= "server {$dnsupdate['server']}\n";
2445
				}
2446

    
2447
				if (file_exists($cacheFile)) {
2448
					list($cachedipv4, $cacheTimev4) = explode("|", file_get_contents($cacheFile));
2449
				}
2450
				if (file_exists("{$cacheFile}.ipv6")) {
2451
					list($cachedipv6, $cacheTimev6) = explode("|", file_get_contents("{$cacheFile}.ipv6"));
2452
				}
2453

    
2454
				// 25 Days
2455
				$maxCacheAgeSecs = 25 * 24 * 60 * 60;
2456
				$need_update = false;
2457

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

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

    
2492
				$upinst .= "\n";	/* mind that trailing newline! */
2493

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

    
2513
	return 0;
2514
}
2515

    
2516
/* configure cron service */
2517
function configure_cron() {
2518
	global $g, $config;
2519

    
2520
	conf_mount_rw();
2521
	/* preserve existing crontab entries */
2522
	$crontab_contents = file("/etc/crontab", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2523

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

    
2533

    
2534
	if (is_array($config['cron']['item'])) {
2535
		$crontab_contents .= "#\n";
2536
		$crontab_contents .= "# " . gettext("pfSense specific crontab entries") . "\n";
2537
		$crontab_contents .= "# " .gettext("Created:") . " " . date("F j, Y, g:i a") . "\n";
2538
		$crontab_contents .= "#\n";
2539

    
2540
		if (isset($config['system']['proxyurl']) && !empty($config['system']['proxyurl'])) {
2541
			$http_proxy = $config['system']['proxyurl'];
2542
			if (isset($config['system']['proxyport']) && !empty($config['system']['proxyport'])) {
2543
				$http_proxy .= ':' . $config['system']['proxyport'];
2544
			}
2545
			$crontab_contents .= "HTTP_PROXY={$http_proxy}";
2546
		}
2547

    
2548
		foreach ($config['cron']['item'] as $item) {
2549
			$crontab_contents .= "\n{$item['minute']}\t";
2550
			$crontab_contents .= "{$item['hour']}\t";
2551
			$crontab_contents .= "{$item['mday']}\t";
2552
			$crontab_contents .= "{$item['month']}\t";
2553
			$crontab_contents .= "{$item['wday']}\t";
2554
			$crontab_contents .= "{$item['who']}\t";
2555
			$crontab_contents .= "{$item['command']}";
2556
		}
2557

    
2558
		$crontab_contents .= "\n#\n";
2559
		$crontab_contents .= "# " . gettext("If possible do not add items to this file manually.") . "\n";
2560
		$crontab_contents .= "# " . gettext("If you do so, this file must be terminated with a blank line (e.g. new line)") . "\n";
2561
		$crontab_contents .= "#\n\n";
2562
	}
2563

    
2564
	/* please maintain the newline at the end of file */
2565
	file_put_contents("/etc/crontab", $crontab_contents);
2566
	unset($crontab_contents);
2567

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

    
2571
	conf_mount_ro();
2572
}
2573

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

    
2598
function upnp_start() {
2599
	global $config;
2600

    
2601
	if (!isset($config['installedpackages']['miniupnpd']['config'])) {
2602
		return;
2603
	}
2604

    
2605
	if ($config['installedpackages']['miniupnpd']['config'][0]['enable']) {
2606
		echo gettext("Starting UPnP service... ");
2607
		require_once('/usr/local/pkg/miniupnpd.inc');
2608
		sync_package_miniupnpd();
2609
		echo "done.\n";
2610
	}
2611
}
2612

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

    
2616
	$is_installed = false;
2617
	$cron_changed = true;
2618

    
2619
	if (!is_array($config['cron'])) {
2620
		$config['cron'] = array();
2621
	}
2622
	if (!is_array($config['cron']['item'])) {
2623
		$config['cron']['item'] = array();
2624
	}
2625

    
2626
	$x=0;
2627
	foreach ($config['cron']['item'] as $item) {
2628
		if (strstr($item['command'], $command)) {
2629
			$is_installed = true;
2630
			break;
2631
		}
2632
		$x++;
2633
	}
2634

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

    
2663
	if ($cron_changed) {
2664
		configure_cron();
2665
	}
2666
}
2667

    
2668
?>
(49-49/67)