Project

General

Profile

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

    
6
	originally part of m0n0wall (http://m0n0.ch/wall)
7
	Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
8
	Copyright (C) 2010	Ermal 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 dnsexit dnsomatic dyndns dyndns-custom dyndns-static dyns easydns eurodns freedns 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),DNSexit,DNS-O-Matic,DynDNS (dynamic),DynDNS (custom),DynDNS (static),DyNS,easyDNS,Euro Dns,freeDNS,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
	if(isset($config['system']['developerspew'])) {
52
		$mt = microtime();
53
		echo "services_radvd_configure() being called $mt\n";
54
	}
55

    
56
	if (!is_array($config['dhcpdv6']))
57
		$config['dhcpdv6'] = array();
58

    
59
	$Iflist = get_configured_interface_list();
60
	$Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces());
61
	$carplist = get_configured_carp_interface_list();
62

    
63
	$radvdconf = "# Automatically Generated, do not edit\n";
64

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

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

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

    
81
		/* are router advertisements enabled? */
82
		if ($dhcpv6ifconf['ramode'] == "disabled")
83
			continue;
84

    
85
		if (!isset($dhcpv6ifconf['rapriority']))
86
			$dhcpv6ifconf['rapriority'] = "medium";
87

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

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

    
111
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
112
		if (!is_ipaddrv6($ifcfgipv6))
113
			continue;
114

    
115
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
116
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
117
		$radvdifs[$realif] = $realif;
118

    
119
		$radvdconf .= "# Generated for DHCPv6 Server $dhcpv6if\n";
120
		$radvdconf .= "interface {$realif} {\n";
121
		if (strstr($realif, "ovpn")) {
122
			$radvdconf .= "\tUnicastOnly on;\n";
123
		}
124
		$radvdconf .= "\tAdvSendAdvert on;\n";
125
		$radvdconf .= "\tMinRtrAdvInterval 5;\n";
126
		$radvdconf .= "\tMaxRtrAdvInterval 20;\n";
127
		$mtu = get_interface_mtu($realif);
128
		if (is_numeric($mtu))
129
			$radvdconf .= "\tAdvLinkMTU {$mtu};\n";
130
		else
131
			$radvdconf .= "\tAdvLinkMTU 1280;\n";
132

    
133
		switch($dhcpv6ifconf['rapriority']) {
134
			case "low":
135
				$radvdconf .= "\tAdvDefaultPreference low;\n";
136
				break;
137
			case "high":
138
				$radvdconf .= "\tAdvDefaultPreference high;\n";
139
				break;
140
			default:
141
				$radvdconf .= "\tAdvDefaultPreference medium;\n";
142
				break;
143
		}
144
		switch($dhcpv6ifconf['ramode']) {
145
			case "managed":
146
			case "assist":
147
				$radvdconf .= "\tAdvManagedFlag on;\n";
148
				$radvdconf .= "\tAdvOtherConfigFlag on;\n";
149
				break;
150
			case "stateless_dhcp":
151
				$radvdconf .= "\tAdvManagedFlag off;\n";
152
				$radvdconf .= "\tAdvOtherConfigFlag on;\n";
153
				break;
154
		}
155
		$radvdconf .= "\tprefix {$subnetv6}/{$ifcfgsnv6} {\n";
156
		if($carpif == true) {
157
			$radvdconf .= "\t\tDeprecatePrefix off;\n";
158
		} else {
159
			$radvdconf .= "\t\tDeprecatePrefix on;\n";
160
		}
161
		switch($dhcpv6ifconf['ramode']) {
162
			case "managed":
163
				$radvdconf .= "\t\tAdvOnLink on;\n";
164
				$radvdconf .= "\t\tAdvAutonomous off;\n";
165
				$radvdconf .= "\t\tAdvRouterAddr on;\n";
166
				break;
167
			case "router":
168
				$radvdconf .= "\t\tAdvOnLink off;\n";
169
				$radvdconf .= "\t\tAdvAutonomous off;\n";
170
				$radvdconf .= "\t\tAdvRouterAddr on;\n";
171
				break;
172
			case "stateless_dhcp":
173
			case "assist":
174
				$radvdconf .= "\t\tAdvOnLink on;\n";
175
				$radvdconf .= "\t\tAdvAutonomous on;\n";
176
				$radvdconf .= "\t\tAdvRouterAddr on;\n";
177
				break;
178
			case "unmanaged":
179
				$radvdconf .= "\t\tAdvOnLink on;\n";
180
				$radvdconf .= "\t\tAdvAutonomous on;\n";
181
				$radvdconf .= "\t\tAdvRouterAddr on;\n";
182
				break;				
183
		}
184
		$radvdconf .= "\t};\n";
185

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

    
231
		/* add DNS servers */
232
		$dnslist = array();
233
		if (isset($dhcpv6ifconf['rasamednsasdhcp6']) && is_array($dhcpv6ifconf['dnsserver']) && !empty($dhcpv6ifconf['dnsserver'])) {
234
			foreach($dhcpv6ifconf['dnsserver'] as $server)
235
				if (is_ipaddrv6($server))
236
					$dnslist[] = $server;
237
		} elseif (!isset($dhcpv6ifconf['rasamednsasdhcp6']) && isset($dhcpv6ifconf['radnsserver']) && is_array($dhcpv6ifconf['radnsserver'])) {
238
			foreach($dhcpv6ifconf['radnsserver'] as $server)
239
				if (is_ipaddrv6($server))
240
					$dnslist[] = $server;
241
		} elseif (isset($config['dnsmasq']['enable']) || isset($config['unbound']['enable'])) {
242
			$dnslist[] = get_interface_ipv6($realif);
243
		} elseif (is_array($config['system']['dnsserver']) && !empty($config['system']['dnsserver'])) {
244
			foreach($config['system']['dnsserver'] as $server) {
245
				if (is_ipaddrv6($server))
246
					$dnslist[] = $server;
247
			}
248
		}
249
		if (count($dnslist) > 0) {
250
			$dnsstring = implode(" ", $dnslist);
251
			if ($dnsstring <> "")
252
				$radvdconf .= "\tRDNSS {$dnsstring} { };\n";
253
		}
254
		if (!empty($dhcpv6ifconf['domain'])) {
255
			$radvdconf .= "\tDNSSL {$dhcpv6ifconf['domain']} { };\n";
256
		} elseif (!empty($config['system']['domain'])) {
257
			$radvdconf .= "\tDNSSL {$config['system']['domain']} { };\n";
258
		}
259
		$radvdconf .= "};\n";
260
	}
261

    
262
	/* handle DHCP-PD prefixes and 6RD dynamic interfaces */
263
	foreach ($Iflist as $if => $ifdescr) {
264
		if(!isset($config['interfaces'][$if]['track6-interface']))
265
			continue;
266
		if(!isset($config['interfaces'][$if]['enable']))
267
			continue;
268
		/* Do not put in the config an interface which is down */
269
		if (isset($blacklist[$if]))
270
			continue;
271
		$trackif = $config['interfaces'][$if]['track6-interface'];
272
		if (empty($config['interfaces'][$trackif]))
273
			continue;
274

    
275
		if (strstr($if, "_vip")) {
276
			// CARP IP, find parent
277
			$ifparent = link_carp_interface_to_parent($if);
278
			$realif = convert_friendly_interface_to_real_interface_name($ifparent);
279
		} else {
280
			$realif = get_real_interface($if, "inet6");
281
		}
282
		
283
		/* prevent duplicate entries, manual overrides */
284
		if (isset($radvdifs[$realif]))
285
			continue;
286

    
287
		$ifcfgipv6 = get_interface_ipv6($if);
288
		if(!is_ipaddrv6($ifcfgipv6)) {
289
			$subnetv6 = "::";
290
			$ifcfgsnv6 = "64";
291
		} else {
292
			$ifcfgsnv6 = get_interface_subnetv6($if);
293
			$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
294
		}
295
		$radvdifs[$realif] = $realif;
296

    
297
		$autotype = $config['interfaces'][$trackif]['ipaddrv6'];
298
	
299
		if ($g['debug'])
300
			log_error("configuring RA on {$if} for type {$autotype} radvd subnet {$subnetv6}/{$ifcfgsnv6}");
301

    
302
		$radvdconf .= "# Generated config for {$autotype} delegation from {$trackif} on {$if}\n";
303
		$radvdconf .= "interface {$realif} {\n";
304
		$radvdconf .= "\tAdvSendAdvert on;\n";
305
		$radvdconf .= "\tMinRtrAdvInterval 3;\n";
306
		$radvdconf .= "\tMaxRtrAdvInterval 10;\n";
307
		$mtu = get_interface_mtu($realif);
308
		if (is_numeric($mtu))
309
			$radvdconf .= "\tAdvLinkMTU {$mtu};\n";
310
		else
311
			$radvdconf .= "\tAdvLinkMTU 1280;\n";
312
		$radvdconf .= "\tAdvOtherConfigFlag on;\n";
313
		$radvdconf .= "\t\tprefix {$subnetv6}/{$ifcfgsnv6} {\n";
314
		$radvdconf .= "\t\tAdvOnLink on;\n";
315
		$radvdconf .= "\t\tAdvAutonomous on;\n";
316
		$radvdconf .= "\t\tAdvRouterAddr on;\n";
317
		$radvdconf .= "\t};\n";
318

    
319
		/* add DNS servers */
320
		$dnslist = array();
321
		if (isset($config['dnsmasq']['enable']) || isset($config['unbound']['enable'])) {
322
			$dnslist[] = $ifcfgipv6;
323
		} elseif (is_array($config['system']['dnsserver']) && !empty($config['system']['dnsserver'])) {
324
			foreach($config['system']['dnsserver'] as $server) {
325
				if(is_ipaddrv6($server))
326
					$dnslist[] = $server;
327
			}
328
		}
329
		if (count($dnslist) > 0) {
330
			$dnsstring = implode(" ", $dnslist);
331
			if (!empty($dnsstring))
332
				$radvdconf .= "\tRDNSS {$dnsstring} { };\n";
333
		}
334
		if (!empty($config['system']['domain'])) {
335
			$radvdconf .= "\tDNSSL {$config['system']['domain']} { };\n";
336
		}
337
		$radvdconf .= "};\n";
338
	}
339

    
340
	/* write radvd.conf */
341
	if (!@file_put_contents("{$g['varetc_path']}/radvd.conf", $radvdconf)) {
342
		log_error("Error: cannot open radvd.conf in services_radvd_configure().\n");
343
		if (platform_booting())
344
			printf("Error: cannot open radvd.conf in services_radvd_configure().\n");
345
	}
346
	unset($radvdconf);
347

    
348
	if (count($radvdifs) > 0) {
349
		if (isvalidpid("{$g['varrun_path']}/radvd.pid"))
350
			sigkillbypid("{$g['varrun_path']}/radvd.pid", "HUP");
351
		else
352
			mwexec("/usr/local/sbin/radvd -p {$g['varrun_path']}/radvd.pid -C {$g['varetc_path']}/radvd.conf -m syslog");
353
	} else {
354
		/* we need to shut down the radvd cleanly, it will send out the prefix
355
		 * information with a lifetime of 0 to notify clients of a (possible) new prefix */
356
		if (isvalidpid("{$g['varrun_path']}/radvd.pid")) {
357
			log_error("Shutting down Router Advertisment daemon cleanly");
358
			killbypid("{$g['varrun_path']}/radvd.pid");
359
			@unlink("{$g['varrun_path']}/radvd.pid");
360
		}
361
	}
362
	return 0;
363
}
364

    
365
function services_dhcpd_configure($family = "all", $blacklist = array()) {
366
	global $config, $g;
367

    
368
	/* configure DHCPD chroot once */
369
	$fd = fopen("{$g['tmp_path']}/dhcpd.sh","w");
370
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}\n");
371
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/dev\n");
372
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/etc\n");
373
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/usr/local/sbin\n");
374
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/var/db\n");
375
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/var/run\n");
376
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/usr\n");
377
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/lib\n");
378
	fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/run\n");
379
	fwrite($fd, "/usr/sbin/chown -R dhcpd:_dhcp {$g['dhcpd_chroot_path']}/*\n");
380
	fwrite($fd, "/bin/cp -n /lib/libc.so.* {$g['dhcpd_chroot_path']}/lib/\n");
381
	fwrite($fd, "/bin/cp -n /usr/local/sbin/dhcpd {$g['dhcpd_chroot_path']}/usr/local/sbin/\n");
382
	fwrite($fd, "/bin/chmod a+rx {$g['dhcpd_chroot_path']}/usr/local/sbin/dhcpd\n");
383

    
384
	$status = `/sbin/mount | /usr/bin/grep -v grep  | /usr/bin/grep  "{$g['dhcpd_chroot_path']}/dev"`;
385
	if (!trim($status))
386
		fwrite($fd, "/sbin/mount -t devfs devfs {$g['dhcpd_chroot_path']}/dev\n");
387
	fclose($fd);
388
	mwexec("/bin/sh {$g['tmp_path']}/dhcpd.sh");
389

    
390
	if ($family == "all" || $family == "inet")
391
		services_dhcpdv4_configure();
392
	if ($family == "all" || $family == "inet6") {
393
		services_dhcpdv6_configure($blacklist);
394
		services_radvd_configure($blacklist);
395
	}
396
}
397

    
398
function services_dhcpdv4_configure() {
399
	global $config, $g;
400
	$need_ddns_updates = false;
401
	$ddns_zones = array();
402

    
403
	if($g['services_dhcp_server_enable'] == false)
404
		return;
405

    
406
	if(isset($config['system']['developerspew'])) {
407
		$mt = microtime();
408
		echo "services_dhcpdv4_configure($if) being called $mt\n";
409
	}
410

    
411
	/* kill any running dhcpd */
412
	if (isvalidpid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid"))
413
		killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid");
414

    
415
	/* DHCP enabled on any interfaces? */
416
	if (!is_dhcp_server_enabled())
417
		return 0;
418

    
419
	/* if OLSRD is enabled, allow WAN to house DHCP. */
420
	if (!function_exists('is_package_installed'))
421
		require_once('pkg-utils.inc');
422
	if (is_package_installed('olsrd') && isset($config['installedpackages']['olsrd']))
423
		foreach($config['installedpackages']['olsrd']['config'] as $olsrd)
424
			if (isset($olsrd['enable']) && $olsrd['enable'] == "on") {
425
				$is_olsr_enabled = true;
426
				break;
427
			}
428

    
429
	if (platform_booting()) {
430
		/* restore the leases, if we have them */
431
		if (file_exists("{$g['cf_conf_path']}/dhcpleases.tgz")) {
432
			$dhcprestore = "";
433
			$dhcpreturn = "";
434
			exec("cd /;LANG=C /usr/bin/tar -xzf {$g['cf_conf_path']}/dhcpleases.tgz 2>&1", $dhcprestore, $dhcpreturn);
435
			$dhcprestore = implode(" ", $dhcprestore);
436
			if($dhcpreturn <> 0) {
437
				log_error(sprintf(gettext('DHCP leases restore failed exited with %1$s, the error is: %2$s%3$s'), $dhcpreturn, $dhcprestore, "\n"));
438
			}
439
		}
440
		/* 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. */
441
		if (($g['platform'] == "pfSense") && !isset($config['system']['use_mfs_tmpvar'])) {
442
			unlink_if_exists("{$g['cf_conf_path']}/dhcpleases.tgz");
443
		}
444
	}
445

    
446
	$syscfg = $config['system'];
447
	if (!is_array($config['dhcpd']))
448
		$config['dhcpd'] = array();
449
	$dhcpdcfg = $config['dhcpd'];
450
	$Iflist = get_configured_interface_list();
451

    
452
	/* Only consider DNS servers with IPv4 addresses for the IPv4 DHCP server. */
453
	$dns_arrv4 = array();
454
	if (is_array($syscfg['dnsserver'])) {
455
		foreach($syscfg['dnsserver'] as $dnsserver) {
456
			if (is_ipaddrv4($dnsserver)) {
457
				$dns_arrv4[] = $dnsserver;
458
			}
459
		}
460
	}
461

    
462
	if (platform_booting())
463
		echo gettext("Starting DHCP service...");
464
	else
465
		sleep(1);
466

    
467
	$custoptions = "";
468
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
469
		if(is_array($dhcpifconf['numberoptions']) && is_array($dhcpifconf['numberoptions']['item'])) {
470
			foreach($dhcpifconf['numberoptions']['item'] as $itemidx => $item) {
471
				if(!empty($item['type']))
472
					$itemtype = $item['type'];
473
				else
474
					$itemtype = "text";
475
				$custoptions .= "option custom-{$dhcpif}-{$itemidx} code {$item['number']} = {$itemtype};\n";
476
			}
477
		}
478
	}
479

    
480
	$dhcpdconf = <<<EOD
481

    
482
option domain-name "{$syscfg['domain']}";
483
option ldap-server code 95 = text;
484
option domain-search-list code 119 = text;
485
option arch code 93 = unsigned integer 16; # RFC4578
486
{$custoptions}
487
default-lease-time 7200;
488
max-lease-time 86400;
489
log-facility local7;
490
one-lease-per-client true;
491
deny duplicates;
492
ping-check true;
493
update-conflict-detection false;
494

    
495
EOD;
496

    
497
	if(!isset($dhcpifconf['disableauthoritative']))
498
		$dhcpdconf .= "authoritative;\n";
499

    
500
	if(isset($dhcpifconf['alwaysbroadcast']))
501
		$dhcpdconf .= "always-broadcast on\n";
502

    
503
	$dhcpdifs = array();
504
	$enable_add_routers = false;
505
	$gateways_arr = return_gateways_array();
506
	/* only add a routers line if the system has any IPv4 gateway at all */
507
	/* a static route has a gateway, manually overriding this field always works */
508
	foreach($gateways_arr as $gwitem) {
509
		if($gwitem['ipprotocol'] == "inet") {
510
			$enable_add_routers = true;
511
			break;
512
		}
513
	}
514

    
515
	/*    loop through and determine if we need to setup
516
	 *    failover peer "bleh" entries
517
	 */
518
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
519

    
520
		if (!isset($config['interfaces'][$dhcpif]['enable']))
521
			continue;
522

    
523
		interfaces_staticarp_configure($dhcpif);
524

    
525
		if (!isset($dhcpifconf['enable']))
526
			continue;
527

    
528
		if($dhcpifconf['failover_peerip'] <> "") {
529
			$intip = get_interface_ip($dhcpif);
530
			/*
531
			 *    yep, failover peer is defined.
532
			 *    does it match up to a defined vip?
533
			 */
534
			$skew = 110;
535
			if(is_array($config['virtualip']['vip'])) {
536
				foreach ($config['virtualip']['vip'] as $vipent) {
537
					if($vipent['interface'] == $dhcpif) {
538
						$carp_nw = gen_subnet($vipent['subnet'], $vipent['subnet_bits']);
539
						if (ip_in_subnet($dhcpifconf['failover_peerip'], "{$carp_nw}/{$vipent['subnet_bits']}")) {
540
							/* this is the interface! */
541
							if(is_numeric($vipent['advskew']) && (intval($vipent['advskew']) < 20)) {
542
								$skew = 0;
543
								break;
544
							}
545
						}
546
					}
547
				}
548
			} else {
549
				log_error(gettext("Warning!  DHCP Failover setup and no CARP virtual IPs defined!"));
550
			}
551
			if($skew > 10) {
552
				$type = "secondary";
553
				$my_port = "520";
554
				$peer_port = "519";
555
			} else {
556
				$my_port = "519";
557
				$peer_port = "520";
558
				$type = "primary";
559
				$dhcpdconf_pri  = "split 128;\n";
560
				$dhcpdconf_pri .= "  mclt 600;\n";
561
			}
562

    
563
			if (is_ipaddrv4($intip)) {
564
			$dhcpdconf .= <<<EOPP
565
failover peer "dhcp_{$dhcpif}" {
566
  {$type};
567
  address {$intip};
568
  port {$my_port};
569
  peer address {$dhcpifconf['failover_peerip']};
570
  peer port {$peer_port};
571
  max-response-delay 10;
572
  max-unacked-updates 10;
573
  {$dhcpdconf_pri}
574
  load balance max seconds 3;
575
}
576
\n
577
EOPP;
578
			}
579
		}
580
	}
581

    
582
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
583

    
584
		$newzone = array();
585
		$ifcfg = $config['interfaces'][$dhcpif];
586

    
587
		if (!isset($dhcpifconf['enable']) || !isset($Iflist[$dhcpif]))
588
			continue;
589
		$ifcfgip = get_interface_ip($dhcpif);
590
		$ifcfgsn = get_interface_subnet($dhcpif);
591
		$subnet = gen_subnet($ifcfgip, $ifcfgsn);
592
		$subnetmask = gen_subnet_mask($ifcfgsn);
593

    
594
		if (!is_ipaddr($subnet))
595
			continue;
596

    
597
		if($is_olsr_enabled == true)
598
			if($dhcpifconf['netmask'])
599
				$subnetmask = gen_subnet_mask($dhcpifconf['netmask']);
600

    
601
		$all_pools = array();
602
		$all_pools[] = $dhcpifconf;
603
		if (is_array($dhcpifconf['pool'])) {
604
			$all_pools = array_merge($all_pools, $dhcpifconf['pool']);
605
		}
606

    
607
		$dnscfg = "";
608

    
609
		if ($dhcpifconf['domain']) {
610
			$dnscfg .= "	option domain-name \"{$dhcpifconf['domain']}\";\n";
611
		}
612

    
613
		if($dhcpifconf['domainsearchlist'] <> "") {
614
			$dnscfg .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpifconf['domainsearchlist'])) . "\";\n";
615
		}
616

    
617
		if (isset($dhcpifconf['ddnsupdate'])) {
618
			$need_ddns_updates = true;
619
			$newzone = array();
620
			if($dhcpifconf['ddnsdomain'] <> "") {
621
				$newzone['domain-name'] = $dhcpifconf['ddnsdomain'];
622
				$dnscfg .= "	ddns-domainname \"{$dhcpifconf['ddnsdomain']}\";\n";
623
			} else {
624
				$newzone['domain-name'] = $config['system']['domain'];
625
			}
626
			$revsubnet = explode(".", $subnet);
627
			$revsubnet = array_reverse($revsubnet);
628
			foreach ($revsubnet as $octet) {
629
				if ($octet != "0")
630
					break;
631
				array_shift($revsubnet);
632
			}
633
			$newzone['ptr-domain'] = implode(".", $revsubnet) . ".in-addr.arpa";
634
		}
635

    
636
		if (is_array($dhcpifconf['dnsserver']) && ($dhcpifconf['dnsserver'][0])) {
637
			$dnscfg .= "	option domain-name-servers " . join(",", $dhcpifconf['dnsserver']) . ";";
638
			if ($newzone['domain-name'])
639
				$newzone['dns-servers'] = $dhcpifconf['dnsserver'];
640
		} else if (isset($config['dnsmasq']['enable'])) {
641
			$dnscfg .= "	option domain-name-servers {$ifcfgip};";
642
			if ($newzone['domain-name'] && is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0]))
643
				$newzone['dns-servers'] = $syscfg['dnsserver'];
644
		} else if (isset($config['unbound']['enable'])) {
645
			$dnscfg .= "	option domain-name-servers {$ifcfgip};";
646
		} else if (!empty($dns_arrv4)) {
647
			$dnscfg .= "	option domain-name-servers " . join(",", $dns_arrv4) . ";";
648
			if ($newzone['domain-name'])
649
				$newzone['dns-servers'] = $dns_arrv4;
650
		}
651

    
652
		/* Create classes - These all contain comma separated lists. Join them into one 
653
		   big comma separated string then split them all up. */
654
		$all_mac_strings = array();
655
		if (is_array($dhcpifconf['pool'])) {
656
			foreach($all_pools as $poolconf) {
657
				$all_mac_strings[] = $poolconf['mac_allow'];
658
				$all_mac_strings[] = $poolconf['mac_deny'];
659
			}
660
		}
661
		$all_mac_strings[] = $dhcpifconf['mac_allow'];
662
		$all_mac_strings[] = $dhcpifconf['mac_deny'];
663
		if (!empty($all_mac_strings)) {
664
			$all_mac_list = array_unique(explode(',', implode(',', $all_mac_strings)));
665
			foreach ($all_mac_list as $mac) {
666
				if (empty($mac)) {
667
					continue;
668
				}
669
				$dhcpdconf .= 'class "' . str_replace(':', '', $mac) . '" {' . "\n";
670
				// Skip the first octet of the MAC address - for media type, typically Ethernet ("01") and match the rest.
671
				$dhcpdconf .= '	match if substring (hardware, 1, ' . (substr_count($mac, ':') + 1) . ') = ' . $mac . ';' . "\n";
672
				$dhcpdconf .= '}' . "\n";
673
			}
674
		}
675

    
676
		$dhcpdconf .= "subnet {$subnet} netmask {$subnetmask} {\n";
677

    
678
		// Setup pool options
679
		foreach($all_pools as $poolconf) {
680
			if (!(ip_in_subnet($poolconf['range']['from'], "{$subnet}/{$ifcfgsn}") && ip_in_subnet($poolconf['range']['to'], "{$subnet}/{$ifcfgsn}"))) {
681
				// If the user has changed the subnet from the interfaces page and applied,
682
				// but has not updated the DHCP range, then the range to/from of the pool can be outside the subnet.
683
				// This can also happen when implementing the batch of changes when the setup wizard reloads the new settings.
684
				$error_msg = sprintf(gettext("Invalid DHCP pool %s - %s for %s subnet %s/%s detected. Please correct the settings in Services, DHCP Server"), $poolconf['range']['from'], $poolconf['range']['to'], convert_real_interface_to_friendly_descr($dhcpif), $subnet, $ifcfgsn);
685
				$do_file_notice = true;
686
				$conf_ipv4_address = $ifcfg['ipaddr'];
687
				$conf_ipv4_subnetmask = $ifcfg['subnet'];
688
				if (is_ipaddrv4($conf_ipv4_address) && is_subnet("{$conf_ipv4_address}/{$conf_ipv4_subnetmask}")) {
689
					$conf_subnet_base = gen_subnet($conf_ipv4_address, $conf_ipv4_subnetmask);
690
					if (ip_in_subnet($poolconf['range']['from'], "{$conf_subnet_base}/{$conf_ipv4_subnetmask}") &&
691
					    ip_in_subnet($poolconf['range']['to'], "{$conf_subnet_base}/{$conf_ipv4_subnetmask}")) {
692
						// Even though the running interface subnet does not match the pool range,
693
						// the interface subnet in the config file contains the pool range.
694
						// We are somewhere part-way through a settings reload, e.g. after running the setup wizard.
695
						// services_dhcpdv4_configure will be called again later when the new interface settings from
696
						// the config are applied and at that time everything will match up.
697
						// Ignore this pool on this interface for now and just log the error to the system log.
698
						log_error($error_msg);
699
						$do_file_notice = false;
700
					}
701
				}
702
				if ($do_file_notice) {
703
					file_notice("DHCP", $error_msg);
704
				}
705
				continue;
706
			}
707
			$dhcpdconf .= "	pool {\n";
708
			/* is failover dns setup? */
709
			if (is_array($poolconf['dnsserver']) && $poolconf['dnsserver'][0] <> "") {
710
				$dhcpdconf .= "		option domain-name-servers {$poolconf['dnsserver'][0]}";
711
				if($poolconf['dnsserver'][1] <> "")
712
					$dhcpdconf .= ",{$poolconf['dnsserver'][1]}";
713
				if($poolconf['dnsserver'][2] <> "")
714
					$dhcpdconf .= ",{$poolconf['dnsserver'][2]}";
715
				if($poolconf['dnsserver'][3] <> "")
716
					$dhcpdconf .= ",{$poolconf['dnsserver'][3]}";
717
				$dhcpdconf .= ";\n";
718
			}
719

    
720
			/* allow/deny MACs */
721
			$mac_allow_list = array_unique(explode(',', $poolconf['mac_allow']));
722
			foreach ($mac_allow_list as $mac) {
723
				if (empty($mac))
724
					continue;
725
				$dhcpdconf .= "		allow members of \"" . str_replace(':', '', $mac) . "\";\n";
726
			}
727
			$mac_deny_list = array_unique(explode(',', $poolconf['mac_deny']));
728
			foreach ($mac_deny_list as $mac) {
729
				if (empty($mac))
730
					continue;
731
				$dhcpdconf .= "		deny members of \"" . str_replace(':', '', $mac) . "\";\n";
732
			}
733

    
734
			if($poolconf['failover_peerip'] <> "")
735
				$dhcpdconf .= "		deny dynamic bootp clients;\n";
736

    
737
			if (isset($poolconf['denyunknown']))
738
			   $dhcpdconf .= "		deny unknown-clients;\n";
739

    
740
			if ($poolconf['gateway'] && $poolconf['gateway'] != "none" && ($poolconf['gateway'] != $dhcpifconf['gateway']))
741
				$dhcpdconf .= "		option routers {$poolconf['gateway']};\n";
742

    
743
			if($dhcpifconf['failover_peerip'] <> "") {
744
				$dhcpdconf .= "		failover peer \"dhcp_{$dhcpif}\";\n";
745
			}
746

    
747
			$pdnscfg = "";
748

    
749
			if ($poolconf['domain'] && ($poolconf['domain'] != $dhcpifconf['domain'])) {
750
				$pdnscfg .= "		option domain-name \"{$poolconf['domain']}\";\n";
751
			}
752

    
753
			if(!empty($poolconf['domainsearchlist']) && ($poolconf['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
754
				$pdnscfg .= "		option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $poolconf['domainsearchlist'])) . "\";\n";
755
			}
756

    
757
			if (isset($poolconf['ddnsupdate'])) {
758
				if (($poolconf['ddnsdomain'] <> "") && ($poolconf['ddnsdomain'] != $dhcpifconf['ddnsdomain']))
759
					$pdnscfg .= "		ddns-domainname \"{$poolconf['ddnsdomain']}\";\n";
760
				$pdnscfg .= "		ddns-update-style interim;\n";
761
			}
762

    
763
			if (is_array($poolconf['dnsserver']) && ($poolconf['dnsserver'][0]) && ($poolconf['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
764
				$pdnscfg .= "		option domain-name-servers " . join(",", $poolconf['dnsserver']) . ";\n";
765
			}
766
			$dhcpdconf .= "{$pdnscfg}";
767

    
768
			// default-lease-time
769
			if ($poolconf['defaultleasetime'] && ($poolconf['defaultleasetime'] != $dhcpifconf['defaultleasetime']))
770
				$dhcpdconf .= "		default-lease-time {$poolconf['defaultleasetime']};\n";
771

    
772
			// max-lease-time
773
			if ($poolconf['maxleasetime'] && ($poolconf['maxleasetime'] != $dhcpifconf['maxleasetime']))
774
				$dhcpdconf .= "		max-lease-time {$poolconf['maxleasetime']};\n";
775

    
776
			// netbios-name*
777
			if (is_array($poolconf['winsserver']) && $poolconf['winsserver'][0] && ($poolconf['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
778
				$dhcpdconf .= "		option netbios-name-servers " . join(",", $poolconf['winsserver']) . ";\n";
779
				$dhcpdconf .= "		option netbios-node-type 8;\n";
780
			}
781

    
782
			// ntp-servers
783
			if (is_array($poolconf['ntpserver']) && $poolconf['ntpserver'][0] && ($poolconf['ntpserver'][0] != $dhcpifconf['ntpserver'][0]))
784
				$dhcpdconf .= "		option ntp-servers " . join(",", $poolconf['ntpserver']) . ";\n";
785

    
786
			// tftp-server-name
787
			if (!empty($poolconf['tftp']) && ($poolconf['tftp'] != $dhcpifconf['tftp']))
788
				$dhcpdconf .= "		option tftp-server-name \"{$poolconf['tftp']}\";\n";
789

    
790
			// ldap-server
791
			if (!empty($poolconf['ldap']) && ($poolconf['ldap'] != $dhcpifconf['ldap']))
792
				$dhcpdconf .= "		option ldap-server \"{$poolconf['ldap']}\";\n";
793

    
794
			// net boot information
795
			if(isset($poolconf['netboot'])) {
796
				if (!empty($poolconf['nextserver']) && ($poolconf['nextserver'] != $dhcpifconf['nextserver'])) {
797
					$dhcpdconf .= "		next-server {$poolconf['nextserver']};\n";
798
				}
799
				if (!empty($poolconf['filename']) && ($poolconf['filename'] != $dhcpifconf['filename'])) {
800
					$dhcpdconf .= "		filename \"{$poolconf['filename']}\";\n";
801
				}
802
				if (!empty($poolconf['rootpath']) && ($poolconf['rootpath'] != $dhcpifconf['rootpath'])) {
803
					$dhcpdconf .= "		option root-path \"{$poolconf['rootpath']}\";\n";
804
				}
805
			}
806
			$dhcpdconf .= "		range {$poolconf['range']['from']} {$poolconf['range']['to']};\n";
807
			$dhcpdconf .= "	}\n\n";
808
		}
809
// End of settings inside pools
810

    
811
		if ($dhcpifconf['gateway'] && $dhcpifconf['gateway'] != "none") {
812
			$routers = $dhcpifconf['gateway'];
813
			$add_routers = true;
814
		} elseif ($dhcpifconf['gateway'] == "none") {
815
			$add_routers = false;
816
		} else {
817
			$add_routers = $enable_add_routers;
818
			$routers = $ifcfgip;
819
		}
820
		if($add_routers)
821
			$dhcpdconf .= "	option routers {$routers};\n";
822

    
823
		$dhcpdconf .= <<<EOD
824
$dnscfg
825

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

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

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

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

    
845
		// tftp-server-name
846
		if ($dhcpifconf['tftp'] <> "")
847
			$dhcpdconf .= "	option tftp-server-name \"{$dhcpifconf['tftp']}\";\n";
848

    
849
		// Handle option, number rowhelper values
850
		$dhcpdconf .= "\n";
851
		if($dhcpifconf['numberoptions']['item']) {
852
			foreach($dhcpifconf['numberoptions']['item'] as $itemidx => $item) {
853
				if(empty($item['type']) || $item['type'] == "text")
854
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} \"{$item['value']}\";\n";
855
				else
856
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} {$item['value']};\n";
857
			}
858
		}
859

    
860
		// ldap-server
861
		if ($dhcpifconf['ldap'] <> "")
862
			$dhcpdconf .= "	option ldap-server \"{$dhcpifconf['ldap']}\";\n";
863

    
864
		// net boot information
865
		if(isset($dhcpifconf['netboot'])) {
866
			if ($dhcpifconf['nextserver'] <> "") {
867
				$dhcpdconf .= "	next-server {$dhcpifconf['nextserver']};\n";
868
			}
869
			if (!empty($dhcpifconf['filename']) && !empty($dhcpifconf['filename32']) && !empty($dhcpifconf['filename64'])) {
870
				$dhcpdconf .= "	if option arch = 00:06 {\n";
871
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename32']}\";\n";
872
				$dhcpdconf .= "	} else if option arch = 00:07 {\n";
873
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename64']}\";\n";
874
				$dhcpdconf .= "	} else if option arch = 00:09 {\n";
875
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename64']}\";\n";
876
				$dhcpdconf .= "	} else {\n";
877
				$dhcpdconf .= "		filename \"{$dhcpifconf['filename']}\";\n";
878
				$dhcpdconf .= "	}\n\n";
879
			} elseif (!empty($dhcpifconf['filename'])) {
880
				$dhcpdconf .= "	filename \"{$dhcpifconf['filename']}\";\n";
881
			}
882
			if (!empty($dhcpifconf['rootpath'])) {
883
				$dhcpdconf .= "	option root-path \"{$dhcpifconf['rootpath']}\";\n";
884
			}
885
		}
886

    
887
		$dhcpdconf .= <<<EOD
888
}
889

    
890
EOD;
891

    
892
		/* add static mappings */
893
		if (is_array($dhcpifconf['staticmap'])) {
894

    
895
			$i = 0;
896
			foreach ($dhcpifconf['staticmap'] as $sm) {
897
				$dhcpdconf .= "host s_{$dhcpif}_{$i} {\n";
898

    
899
                if ($sm['mac'])
900
                    $dhcpdconf .= "        hardware ethernet {$sm['mac']};\n";
901

    
902
                if ($sm['cid'])
903
                    $dhcpdconf .= "        option dhcp-client-identifier \"{$sm['cid']}\";\n";
904

    
905
				if ($sm['ipaddr'])
906
					$dhcpdconf .= "	fixed-address {$sm['ipaddr']};\n";
907

    
908
				if ($sm['hostname']) {
909
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
910
					$dhhostname = str_replace(".", "_", $dhhostname);
911
					$dhcpdconf .= "	option host-name \"{$dhhostname}\";\n";
912
				}
913
				if ($sm['filename'])
914
					$dhcpdconf .= "	filename \"{$sm['filename']}\";\n";
915

    
916
				if ($sm['rootpath'])
917
					$dhcpdconf .= "	option root-path \"{$sm['rootpath']}\";\n";
918

    
919
				if ($sm['gateway'] && ($sm['gateway'] != $dhcpifconf['gateway']))
920
					$dhcpdconf .= "	option routers {$sm['gateway']};\n";
921

    
922
				$smdnscfg = "";
923

    
924
				if ($sm['domain'] && ($sm['domain'] != $dhcpifconf['domain'])) {
925
					$smdnscfg .= "	option domain-name \"{$sm['domain']}\";\n";
926
				}
927

    
928
				if(!empty($sm['domainsearchlist']) && ($sm['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
929
					$smdnscfg .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $sm['domainsearchlist'])) . "\";\n";
930
				}
931

    
932
				if (isset($sm['ddnsupdate'])) {
933
					if (($sm['ddnsdomain'] <> "") && ($sm['ddnsdomain'] != $dhcpifconf['ddnsdomain']))
934
						$pdnscfg .= "		ddns-domainname \"{$sm['ddnsdomain']}\";\n";
935
					$pdnscfg .= "		ddns-update-style interim;\n";
936
				}
937

    
938
				if (is_array($sm['dnsserver']) && ($sm['dnsserver'][0]) && ($sm['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
939
					$smdnscfg .= "	option domain-name-servers " . join(",", $sm['dnsserver']) . ";\n";
940
				}
941
				$dhcpdconf .= "{$smdnscfg}";
942

    
943
				// default-lease-time
944
				if ($sm['defaultleasetime'] && ($sm['defaultleasetime'] != $dhcpifconf['defaultleasetime']))
945
					$dhcpdconf .= "	default-lease-time {$sm['defaultleasetime']};\n";
946

    
947
				// max-lease-time
948
				if ($sm['maxleasetime'] && ($sm['maxleasetime'] != $dhcpifconf['maxleasetime']))
949
					$dhcpdconf .= "	max-lease-time {$sm['maxleasetime']};\n";
950

    
951
				// netbios-name*
952
				if (is_array($sm['winsserver']) && $sm['winsserver'][0] && ($sm['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
953
					$dhcpdconf .= "	option netbios-name-servers " . join(",", $sm['winsserver']) . ";\n";
954
					$dhcpdconf .= "	option netbios-node-type 8;\n";
955
				}
956

    
957
				// ntp-servers
958
				if (is_array($sm['ntpserver']) && $sm['ntpserver'][0] && ($sm['ntpserver'][0] != $dhcpifconf['ntpserver'][0]))
959
					$dhcpdconf .= "	option ntp-servers " . join(",", $sm['ntpserver']) . ";\n";
960

    
961
				// tftp-server-name
962
				if (!empty($sm['tftp']) && ($sm['tftp'] != $dhcpifconf['tftp']))
963
					$dhcpdconf .= "	option tftp-server-name \"{$sm['tftp']}\";\n";
964

    
965
				$dhcpdconf .= "}\n";
966
				$i++;
967
			}
968
		}
969

    
970
		$dhcpdifs[] = get_real_interface($dhcpif);
971
		if ($newzone['domain-name'])
972
		{
973
			if ($need_ddns_updates)
974
			{
975
				$newzone['dns-servers'] = array($dhcpifconf['ddnsdomainprimary']);
976
			}
977
			$ddns_zones[] = $newzone;
978
		}
979
	}
980

    
981
	if ($need_ddns_updates) {
982
		$dhcpdconf .= "ddns-update-style interim;\n";
983
		$dhcpdconf .= "update-static-leases on;\n";
984

    
985
		$dhcpdconf .= dhcpdkey($dhcpifconf);
986
		$dhcpdconf .= dhcpdzones($ddns_zones, $dhcpifconf);
987
	}
988

    
989
	/* write dhcpd.conf */
990
	if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpd.conf", $dhcpdconf)) {
991
		printf(gettext("Error: cannot open dhcpd.conf in services_dhcpdv4_configure().%s"), "\n");
992
		unset($dhcpdconf);
993
		return 1;
994
	}
995
	unset($dhcpdconf);
996

    
997
	/* create an empty leases database */
998
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases"))
999
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases");
1000

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

    
1005
	/* fire up dhcpd in a chroot */
1006
	if (count($dhcpdifs) > 0) {
1007
		mwexec("/usr/local/sbin/dhcpd -user dhcpd -group _dhcp -chroot {$g['dhcpd_chroot_path']} -cf /etc/dhcpd.conf -pf {$g['varrun_path']}/dhcpd.pid " .
1008
			join(" ", $dhcpdifs));
1009
	}
1010

    
1011
	if (platform_booting())
1012
		print "done.\n";
1013

    
1014
	return 0;
1015
}
1016

    
1017
function dhcpdkey($dhcpifconf)
1018
{
1019
	$dhcpdconf = "";
1020
	if ($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "")
1021
	{
1022
		$dhcpdconf .= "key {$dhcpifconf['ddnsdomainkeyname']} {\n";
1023
		$dhcpdconf .= "	algorithm hmac-md5;\n";
1024
		$dhcpdconf .= "	secret {$dhcpifconf['ddnsdomainkey']};\n";
1025
		$dhcpdconf .= "}\n";
1026
	}
1027

    
1028
	return $dhcpdconf;
1029
}
1030

    
1031
function dhcpdzones($ddns_zones, $dhcpifconf)
1032
{
1033
	$dhcpdconf = "";
1034

    
1035
	if (is_array($ddns_zones)) {
1036
		$added_zones = array();
1037
		foreach ($ddns_zones as $zone) {
1038
			if (!is_array($zone) || empty($zone) || !is_array($zone['dns-servers']))
1039
				continue;
1040
			$primary = $zone['dns-servers'][0];
1041
			$secondary = empty($zone['dns-servers'][1]) ? "" : $zone['dns-servers'][1];
1042

    
1043
			// Make sure we aren't using any invalid or IPv6 DNS servers.
1044
			if (!is_ipaddrv4($primary)) {
1045
				if (is_ipaddrv4($secondary)) {
1046
					$primary = $secondary;
1047
					$secondary = "";
1048
				} else {
1049
					continue;
1050
				}
1051
			}
1052

    
1053
			// We don't need to add zones multiple times.
1054
			if ($zone['domain-name'] && !in_array($zone['domain-name'], $added_zones)) {
1055
				$dhcpdconf .= "zone {$zone['domain-name']}. {\n";
1056
				$dhcpdconf .= "	primary {$primary};\n";
1057
				if (is_ipaddrv4($secondary))
1058
					$dhcpdconf .= "	secondary {$secondary};\n";
1059
				if($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "")
1060
                    $dhcpdconf .= "	key {$dhcpifconf['ddnsdomainkeyname']};\n";
1061
				$dhcpdconf .= "}\n";
1062
				$added_zones[] = $zone['domain-name'];
1063
			}
1064
			if ($zone['ptr-domain'] && !in_array($zone['ptr-domain'], $added_zones)) {
1065
				$dhcpdconf .= "zone {$zone['ptr-domain']} {\n";
1066
				$dhcpdconf .= "	primary {$primary};\n";
1067
				if (is_ipaddrv4($secondary))
1068
					$dhcpdconf .= "	secondary {$secondary};\n";
1069
				if($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "")
1070
                    $dhcpdconf .= "	key {$dhcpifconf['ddnsdomainkeyname']};\n";
1071
				$dhcpdconf .= "}\n";
1072
				$added_zones[] = $zone['ptr-domain'];
1073
			}
1074
		}
1075
	}
1076

    
1077
	return $dhcpdconf;
1078
}
1079

    
1080
function services_dhcpdv6_configure($blacklist = array()) {
1081
	global $config, $g;
1082

    
1083
	if($g['services_dhcp_server_enable'] == false)
1084
		return;
1085

    
1086
	if(isset($config['system']['developerspew'])) {
1087
		$mt = microtime();
1088
		echo "services_dhcpd_configure($if) being called $mt\n";
1089
	}
1090

    
1091
	/* kill any running dhcpd */
1092
	if (isvalidpid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid"))
1093
		killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid");
1094
	if (isvalidpid("{$g['varrun_path']}/dhcpleases6.pid"))
1095
		killbypid("{$g['varrun_path']}/dhcpleases6.pid");
1096

    
1097
	/* DHCP enabled on any interfaces? */
1098
	if (!is_dhcpv6_server_enabled())
1099
		return 0;
1100

    
1101
	if (platform_booting()) {
1102
		if ($g['platform'] != "pfSense") {
1103
			/* restore the leases, if we have them */
1104
			if (file_exists("{$g['cf_conf_path']}/dhcp6leases.tgz")) {
1105
				$dhcprestore = "";
1106
				$dhcpreturn = "";
1107
				exec("cd /;LANG=C /usr/bin/tar -xzf {$g['cf_conf_path']}/dhcp6leases.tgz 2>&1", $dhcprestore, $dhcpreturn);
1108
				$dhcprestore = implode(" ", $dhcprestore);
1109
				if($dhcpreturn <> 0) {
1110
					log_error("DHCP leases v6 restore failed exited with $dhcpreturn, the error is: $dhcprestore\n");
1111
				}
1112
			}
1113
		}
1114
	}
1115

    
1116
	$syscfg = $config['system'];
1117
	if (!is_array($config['dhcpdv6']))
1118
		$config['dhcpdv6'] = array();
1119
	$dhcpdv6cfg = $config['dhcpdv6'];
1120
	$Iflist = get_configured_interface_list();
1121
	$Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces());
1122

    
1123

    
1124
	if (platform_booting())
1125
		echo "Starting DHCPv6 service...";
1126
	else
1127
		sleep(1);
1128

    
1129
	/* we add a fake entry for interfaces that are set to track6 another WAN */
1130
	foreach ($Iflist as $ifname) {
1131
		/* Do not put in the config an interface which is down */
1132
		if (isset($blacklist[$ifname]))
1133
			continue;
1134
		if (!empty($config['interfaces'][$ifname]['track6-interface'])) {
1135
			$realif = get_real_interface($ifname, "inet6");
1136
			$ifcfgipv6 = get_interface_ipv6($ifname);
1137
			if(!is_ipaddrv6($ifcfgipv6))
1138
				continue;
1139
			$ifcfgipv6 = Net_IPv6::getNetmask($ifcfgipv6, 64);
1140
			$trackifname = $config['interfaces'][$ifname]['track6-interface'];
1141
			$trackcfg = $config['interfaces'][$trackifname];
1142
			$pdlen = calculate_ipv6_delegation_length($trackifname);
1143
			$ifcfgipv6arr =explode(":", $ifcfgipv6);
1144
			$dhcpdv6cfg[$ifname] = array();
1145
			$dhcpdv6cfg[$ifname]['enable'] = true;
1146
			/* range */
1147
			$ifcfgipv6arr[7] = "1000";
1148
			$dhcpdv6cfg[$ifname]['range'] = array();
1149
			$dhcpdv6cfg[$ifname]['range']['from'] = Net_IPv6::compress(implode(":", $ifcfgipv6arr));
1150
			$ifcfgipv6arr[7] = "2000";
1151
			$dhcpdv6cfg[$ifname]['range']['to'] = Net_IPv6::compress(implode(":", $ifcfgipv6arr));
1152
			/* prefix length > 0? We can add dhcp6 prefix delegation server */
1153
			if($pdlen > 2) {
1154
				$pdlenmax = $pdlen;
1155
				$pdlenhalf = $pdlenmax -1;
1156
				$pdlenmin = (64 - ceil($pdlenhalf / 4));
1157
				$dhcpdv6cfg[$ifname]['prefixrange'] = array();
1158
				$dhcpdv6cfg[$ifname]['prefixrange']['prefixlength'] = $pdlenmin;
1159

    
1160
				/* set the delegation start to half the current address block */
1161
				$range = Net_IPv6::parseAddress($ifcfgipv6, (64 - $pdlenmax));
1162
				$range['start'] = Net_IPv6::getNetmask($range['end'], (64 - $pdlenhalf));
1163

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

    
1168
				$dhcpdv6cfg[$ifname]['prefixrange']['from'] = Net_IPv6::compress($range['start']);
1169
				$dhcpdv6cfg[$ifname]['prefixrange']['to'] = Net_IPv6::compress($range['end']);
1170
			}
1171
			$dhcpdv6cfg[$ifname]['dns6ip'] = get_interface_ipv6($ifname);
1172
		}
1173
	}
1174

    
1175
	$custoptionsv6 = "";
1176
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1177
		if(is_array($dhcpv6ifconf['numberoptions']) && is_array($dhcpv6ifconf['numberoptions']['item'])) {
1178
			foreach($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
1179
				$custoptionsv6 .= "option custom-{$dhcpv6if}-{$itemv6idx} code {$itemv6['number']} = text;\n";
1180
			}
1181
		}
1182
	}
1183

    
1184
	if(isset($dhcpv6ifconf['netboot']) && !empty($dhcpv6ifconf['bootfile_url']))
1185
		$custoptionsv6 .= "option dhcp6.bootfile-url code 59 = string;\n";
1186

    
1187
	$dhcpdv6conf = <<<EOD
1188

    
1189
option domain-name "{$syscfg['domain']}";
1190
option ldap-server code 95 = text;
1191
option domain-search-list code 119 = text;
1192
{$custoptionsv6}
1193
default-lease-time 7200;
1194
max-lease-time 86400;
1195
log-facility local7;
1196
one-lease-per-client true;
1197
deny duplicates;
1198
ping-check true;
1199
update-conflict-detection false;
1200

    
1201
EOD;
1202

    
1203
	if(!isset($dhcpv6ifconf['disableauthoritative']))
1204
		$dhcpdv6conf .= "authoritative;\n";
1205

    
1206
	if(isset($dhcpv6ifconf['alwaysbroadcast']))
1207
		$dhcpdv6conf .= "always-broadcast on\n";
1208

    
1209
	$dhcpdv6ifs = array();
1210

    
1211
	$dhcpv6num = 0;
1212
	$nsupdate = false;
1213

    
1214
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1215

    
1216
		$ddns_zones = array();
1217

    
1218
		$ifcfgv6 = $config['interfaces'][$dhcpv6if];
1219

    
1220
		if (!isset($dhcpv6ifconf['enable']) || !isset($Iflist[$dhcpv6if]) || !isset($ifcfgv6['enable']))
1221
			continue;
1222
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1223
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
1224
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1225

    
1226
		if ($is_olsr_enabled == true) {
1227
			if($dhcpv6ifconf['netmask'])
1228
				$subnetmask = gen_subnet_maskv6($dhcpv6ifconf['netmask']);
1229
		}
1230

    
1231
		$dnscfgv6 = "";
1232

    
1233
		if ($dhcpv6ifconf['domain']) {
1234
			$dnscfgv6 .= "	option domain-name \"{$dhcpv6ifconf['domain']}\";\n";
1235
		}
1236

    
1237
    	if ($dhcpv6ifconf['domainsearchlist'] <> "") {
1238
			$dnscfgv6 .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpv6ifconf['domainsearchlist'])) . "\";\n";
1239
    	}
1240

    
1241
		if (isset($dhcpv6ifconf['ddnsupdate'])) {
1242
			if($dhcpv6ifconf['ddnsdomain'] <> "") {
1243
				$dnscfgv6 .= "	ddns-domainname \"{$dhcpv6ifconf['ddnsdomain']}\";\n";
1244
			}
1245
			$dnscfgv6 .= "	ddns-update-style interim;\n";
1246
			$nsupdate = true;
1247
		}
1248

    
1249
		if (is_array($dhcpv6ifconf['dnsserver']) && ($dhcpv6ifconf['dnsserver'][0])) {
1250
			$dnscfgv6 .= "	option dhcp6.name-servers " . join(",", $dhcpv6ifconf['dnsserver']) . ";";
1251
		} else if (((isset($config['dnsmasq']['enable'])) || isset($config['unbound']['enable'])) && (is_ipaddrv6($ifcfgipv6))) {
1252
			$dnscfgv6 .= "	option dhcp6.name-servers {$ifcfgipv6};";
1253
		} else if (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
1254
			$dns_arrv6 = array();
1255
			foreach($syscfg['dnsserver'] as $dnsserver) {
1256
				if (is_ipaddrv6($dnsserver)) {
1257
					$dns_arrv6[] = $dnsserver;
1258
				}
1259
			}
1260
			if(!empty($dns_arrv6))
1261
				$dnscfgv6 .= "	option dhcp6.name-servers " . join(",", $dns_arrv6) . ";";
1262
		}
1263

    
1264
		if ($dhcpv6ifconf['domain']) {
1265
			$newzone = array();
1266
			$newzone['domain-name'] = $dhcpv6ifconf['domain']; 
1267
			$newzone['dns-servers'][] = $dhcpv6ifconf['ddnsdomainprimary'];
1268
			$ddns_zones[] = $newzone;
1269
		}
1270

    
1271
		if (is_ipaddrv6($ifcfgipv6)) {
1272
			$dhcpdv6conf .= "subnet6 {$subnetv6}/{$ifcfgsnv6}";
1273
		} else {
1274
			$subnet6 = gen_subnetv6($dhcpv6ifconf['range']['from'], "64");
1275
			$dhcpdv6conf .= "subnet6 {$subnet6}/64";
1276
		}
1277
		$dhcpdv6conf .= " {\n";
1278

    
1279
		$dhcpdv6conf .= <<<EOD
1280
	range6 {$dhcpv6ifconf['range']['from']} {$dhcpv6ifconf['range']['to']};
1281
$dnscfgv6
1282

    
1283
EOD;
1284

    
1285
		if (is_ipaddrv6($dhcpv6ifconf['prefixrange']['from']) && is_ipaddrv6($dhcpv6ifconf['prefixrange']['to'])) {
1286
			$dhcpdv6conf .= "	prefix6 {$dhcpv6ifconf['prefixrange']['from']} {$dhcpv6ifconf['prefixrange']['to']} /{$dhcpv6ifconf['prefixrange']['prefixlength']};\n";
1287
		}
1288
		if (is_ipaddrv6($dhcpv6ifconf['dns6ip'])) {
1289
			$dhcpdv6conf .= "	option dhcp6.name-servers {$dhcpv6ifconf['dns6ip']};\n";
1290
		}
1291
    		// default-lease-time
1292
		if ($dhcpv6ifconf['defaultleasetime'])
1293
			$dhcpdv6conf .= "	default-lease-time {$dhcpv6ifconf['defaultleasetime']};\n";
1294

    
1295
		// max-lease-time
1296
		if ($dhcpv6ifconf['maxleasetime'])
1297
			$dhcpdv6conf .= "	max-lease-time {$dhcpv6ifconf['maxleasetime']};\n";
1298

    
1299
		// ntp-servers
1300
		if (is_array($dhcpv6ifconf['ntpserver']) && $dhcpv6ifconf['ntpserver'][0]) {
1301
			$ntpservers = array();
1302
			foreach($dhcpv6ifconf['ntpserver'] as $ntpserver) {
1303
				if(is_ipaddrv6($ntpserver))
1304
					$ntpservers[] = $ntpserver;
1305
			}
1306
			if(count($ntpservers) > 0 )
1307
				$dhcpdv6conf .= "       option dhcp6.sntp-servers " . join(",", $dhcpv6ifconf['ntpserver']) . ";\n";
1308
		}
1309
		// tftp-server-name
1310
		/* Needs ISC DHCPD support
1311
		 if ($dhcpv6ifconf['tftp'] <> "")
1312
			$dhcpdv6conf .= "	option tftp-server-name \"{$dhcpv6ifconf['tftp']}\";\n";
1313
		*/
1314

    
1315
		// Handle option, number rowhelper values
1316
		$dhcpdv6conf .= "\n";
1317
		if ($dhcpv6ifconf['numberoptions']['item']) {
1318
			foreach($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
1319
				$dhcpdv6conf .= "	option custom-{$dhcpv6if}-{$itemv6idx} \"{$itemv6['value']}\";\n";
1320
			}
1321
		}
1322

    
1323
		// ldap-server
1324
		if ($dhcpv6ifconf['ldap'] <> "")
1325
			$dhcpdv6conf .= "	option ldap-server \"{$dhcpv6ifconf['ldap']}\";\n";
1326

    
1327
		// net boot information
1328
		if(isset($dhcpv6ifconf['netboot'])) {
1329
			if (!empty($dhcpv6ifconf['bootfile_url'])) {
1330
				$dhcpdv6conf .= "	option dhcp6.bootfile-url \"{$dhcpv6ifconf['bootfile_url']}\";\n";
1331
			}
1332
		}
1333

    
1334
		$dhcpdv6conf .= "}\n";
1335

    
1336
		/* add static mappings */
1337
		/* Needs to use DUID */
1338
		if (is_array($dhcpv6ifconf['staticmap'])) {
1339
			$i = 0;
1340
			foreach ($dhcpv6ifconf['staticmap'] as $sm) {
1341
				$dhcpdv6conf .= <<<EOD
1342
host s_{$dhcpv6if}_{$i} {
1343
	host-identifier option dhcp6.client-id {$sm['duid']};
1344

    
1345
EOD;
1346
				if ($sm['ipaddrv6'])
1347
					$dhcpdv6conf .= "	fixed-address6 {$sm['ipaddrv6']};\n";
1348

    
1349
				if ($sm['hostname']) {
1350
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
1351
					$dhhostname = str_replace(".", "_", $dhhostname);
1352
					$dhcpdv6conf .= "	option host-name {$dhhostname};\n";
1353
				}
1354
				if ($sm['filename'])
1355
					$dhcpdv6conf .= "	filename \"{$sm['filename']}\";\n";
1356

    
1357
				if ($sm['rootpath'])
1358
					$dhcpdv6conf .= "	option root-path \"{$sm['rootpath']}\";\n";
1359

    
1360
				$dhcpdv6conf .= "}\n";
1361
				$i++;
1362
			}
1363
		}
1364

    
1365
		if ($dhcpv6ifconf['domain'])
1366
		{
1367
			$dhcpdv6conf .= dhcpdkey($dhcpv6ifconf);
1368
			$dhcpdv6conf .= dhcpdzones($ddns_zones, $dhcpv6ifconf);
1369
		}
1370

    
1371
		if ($config['dhcpdv6'][$dhcpv6if]['ramode'] <> "unmanaged" && isset($config['interfaces'][$dhcpv6if]['enable'])) {
1372
			if(preg_match("/poes/si", $dhcpv6if)) {
1373
				/* magic here */
1374
				$dhcpdv6ifs = array_merge($dhcpdv6ifs, get_pppoes_child_interfaces($dhcpv6if));
1375
			} else {
1376
				$realif = get_real_interface($dhcpv6if, "inet6");
1377
				if (stristr("$realif", "bridge")) {
1378
					$mac = get_interface_mac($realif);
1379
					$v6address = generate_ipv6_from_mac($mac);
1380
					/* Create link local address for bridges */
1381
					mwexec("/sbin/ifconfig {$realif} inet6 {$v6address}");
1382
				}
1383
				$realif = escapeshellcmd($realif);
1384
				$dhcpdv6ifs[] = $realif;
1385
			}
1386
		}
1387
	}
1388

    
1389
	if ($nsupdate)
1390
	{
1391
		$dhcpdv6conf .= "ddns-update-style interim;\n";
1392
	}
1393
	else
1394
	{
1395
		$dhcpdv6conf .= "ddns-update-style none;\n";
1396
	}
1397

    
1398
	/* write dhcpdv6.conf */
1399
	if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf", $dhcpdv6conf)) {
1400
		log_error("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n");
1401
		if (platform_booting())
1402
			printf("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n");
1403
		unset($dhcpdv6conf);
1404
		return 1;
1405
	}
1406
	unset($dhcpdv6conf);
1407

    
1408
	/* create an empty leases v6 database */
1409
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases"))
1410
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases");
1411

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

    
1416
	/* fire up dhcpd in a chroot */
1417
	if (count($dhcpdv6ifs) > 0) {
1418
		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 " .
1419
			join(" ", $dhcpdv6ifs));
1420
		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");
1421
	}
1422
	if (platform_booting())
1423
		print gettext("done.") . "\n";
1424

    
1425
	return 0;
1426
}
1427

    
1428
function services_igmpproxy_configure() {
1429
        global $config, $g;
1430

    
1431
        /* kill any running igmpproxy */
1432
        killbyname("igmpproxy");
1433

    
1434
	if (!is_array($config['igmpproxy']['igmpentry']) || (count($config['igmpproxy']['igmpentry']) == 0))
1435
		return 1;
1436

    
1437
        $iflist = get_configured_interface_list();
1438

    
1439
        $igmpconf = <<<EOD
1440

    
1441
##------------------------------------------------------
1442
## Enable Quickleave mode (Sends Leave instantly)
1443
##------------------------------------------------------
1444
quickleave
1445

    
1446
EOD;
1447

    
1448
        foreach ($config['igmpproxy']['igmpentry'] as $igmpcf) {
1449
                unset($iflist[$igmpcf['ifname']]);
1450
                $realif = get_real_interface($igmpcf['ifname']);
1451
                if (empty($igmpcf['threshold']))
1452
                        $threshld = 1;
1453
                else
1454
                        $threshld = $igmpcf['threshold'];
1455
                $igmpconf .= "phyint {$realif} {$igmpcf['type']} ratelimit 0 threshold {$threshld}\n";
1456

    
1457
                if ($igmpcf['address'] <> "") {
1458
                        $item = explode(" ", $igmpcf['address']);
1459
                        foreach($item as $iww)
1460
                                $igmpconf .= "altnet {$iww}\n";
1461
                }
1462
                $igmpconf .= "\n";
1463
        }
1464
        foreach ($iflist as $ifn) {
1465
                $realif = get_real_interface($ifn);
1466
                $igmpconf .= "phyint {$realif} disabled\n";
1467
        }
1468
	$igmpconf .= "\n";
1469

    
1470
        $igmpfl = fopen($g['tmp_path'] . "/igmpproxy.conf", "w");
1471
        if (!$igmpfl) {
1472
                log_error(gettext("Could not write Igmpproxy configuration file!"));
1473
                return;
1474
        }
1475
        fwrite($igmpfl, $igmpconf);
1476
        fclose($igmpfl);
1477
	unset($igmpconf);
1478

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

    
1483
        return 0;
1484
}
1485

    
1486
function services_dhcrelay_configure() {
1487
	global $config, $g;
1488
	if ($g['platform'] == 'jail')
1489
		return;
1490
	if(isset($config['system']['developerspew'])) {
1491
		$mt = microtime();
1492
		echo "services_dhcrelay_configure() being called $mt\n";
1493
	}
1494

    
1495
	/* kill any running dhcrelay */
1496
	killbypid("{$g['varrun_path']}/dhcrelay.pid");
1497

    
1498
	$dhcrelaycfg =& $config['dhcrelay'];
1499

    
1500
	/* DHCPRelay enabled on any interfaces? */
1501
	if (!isset($dhcrelaycfg['enable']))
1502
		return 0;
1503

    
1504
	if (platform_booting())
1505
		echo gettext("Starting DHCP relay service...");
1506
	else
1507
		sleep(1);
1508

    
1509
	$iflist = get_configured_interface_list();
1510

    
1511
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1512
	foreach ($dhcifaces as $dhcrelayif) {
1513
		if (!isset($iflist[$dhcrelayif]) ||
1514
			link_interface_to_bridge($dhcrelayif))
1515
			continue;
1516

    
1517
		if (is_ipaddr(get_interface_ip($dhcrelayif)))
1518
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1519
	}
1520

    
1521
	/*
1522
	 * In order for the relay to work, it needs to be active
1523
	 * on the interface in which the destination server sits.
1524
	 */
1525
	$srvips = explode(",", $dhcrelaycfg['server']);
1526
	foreach ($srvips as $srcidx => $srvip) {
1527
		unset($destif);
1528
		foreach ($iflist as $ifname) {
1529
			$subnet = get_interface_ip($ifname);
1530
			if (!is_ipaddr($subnet))
1531
				continue;
1532
			$subnet .=  "/" . get_interface_subnet($ifname);
1533
			if (ip_in_subnet($srvip, $subnet)) {
1534
				$destif = get_real_interface($ifname);
1535
				break;
1536
			}
1537
		}
1538
		if (!isset($destif)) {
1539
			foreach (get_staticroutes() as $rtent) {
1540
				if (ip_in_subnet($srvip, $rtent['network'])) {
1541
					$a_gateways = return_gateways_array(true);
1542
					$destif = $a_gateways[$rtent['gateway']]['interface'];
1543
					break;
1544
				}
1545
			}
1546
		}
1547

    
1548
		if (!isset($destif)) {
1549
			/* Create a array from the existing route table */
1550
        		exec("/usr/bin/netstat -rnWf inet", $route_str);
1551
        		array_shift($route_str);
1552
        		array_shift($route_str);
1553
        		array_shift($route_str);
1554
        		array_shift($route_str);
1555
        		$route_arr = array();
1556
        		foreach($route_str as $routeline) {
1557
				$items = preg_split("/[ ]+/i", $routeline);
1558
				if (is_subnetv4($items[0])) {
1559
					$subnet = $items[0];
1560
				} elseif (is_ipaddrv4($items[0])) {
1561
					$subnet = "{$items[0]}/32";
1562
				} else {
1563
					// Not a subnet or IP address, skip to the next line.
1564
					continue;
1565
				}
1566
				if (ip_in_subnet($srvip, $subnet)) {
1567
					$destif = trim($items[6]);
1568
					break;
1569
				}
1570
			}
1571
		}
1572

    
1573
		if (!isset($destif)) {
1574
			if (is_array($config['gateways']['gateway_item'])) {
1575
				foreach ($config['gateways']['gateway_item'] as $gateway) {
1576
					if (isset($gateway['defaultgw'])) {
1577
						$destif = get_real_interface($gateway['interface']);
1578
						break;
1579
					}
1580
				}
1581
			} else
1582
				$destif = get_real_interface("wan");
1583
		}
1584

    
1585
		if (!empty($destif))
1586
			$dhcrelayifs[] = $destif;
1587
	}
1588
	$dhcrelayifs = array_unique($dhcrelayifs);
1589

    
1590
	/* fire up dhcrelay */
1591
	if (empty($dhcrelayifs)) {
1592
		log_error("No suitable interface found for running dhcrelay!");
1593
		return; /* XXX */
1594
	}
1595

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

    
1598
	if (isset($dhcrelaycfg['agentoption']))
1599
		$cmd .=  " -a -m replace";
1600

    
1601
	$cmd .= " " . implode(" ", $srvips);
1602
	mwexec($cmd);
1603
	unset($cmd);
1604

    
1605
	return 0;
1606
}
1607

    
1608
function services_dhcrelay6_configure() {
1609
	global $config, $g;
1610
	if ($g['platform'] == 'jail')
1611
		return;
1612
	if(isset($config['system']['developerspew'])) {
1613
		$mt = microtime();
1614
		echo "services_dhcrelay6_configure() being called $mt\n";
1615
	}
1616

    
1617
	/* kill any running dhcrelay */
1618
	killbypid("{$g['varrun_path']}/dhcrelay6.pid");
1619

    
1620
	$dhcrelaycfg =& $config['dhcrelay6'];
1621

    
1622
	/* DHCPv6 Relay enabled on any interfaces? */
1623
	if (!isset($dhcrelaycfg['enable']))
1624
		return 0;
1625

    
1626
	if (platform_booting())
1627
		echo gettext("Starting DHCPv6 relay service...");
1628
	else
1629
		sleep(1);
1630

    
1631
	$iflist = get_configured_interface_list();
1632

    
1633
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
1634
	foreach ($dhcifaces as $dhcrelayif) {
1635
		if (!isset($iflist[$dhcrelayif]) ||
1636
			link_interface_to_bridge($dhcrelayif))
1637
			continue;
1638

    
1639
		if (is_ipaddrv6(get_interface_ipv6($dhcrelayif)))
1640
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
1641
	}
1642
	$dhcrelayifs = array_unique($dhcrelayifs);
1643

    
1644
	/*
1645
	 * In order for the relay to work, it needs to be active
1646
	 * on the interface in which the destination server sits.
1647
	 */
1648
	$srvips = explode(",", $dhcrelaycfg['server']);
1649
	if (!is_array($srvips)) {
1650
		log_error("No destination IP has been configured!");
1651
		return;
1652
	}
1653

    
1654
        $srvifaces = array();
1655
	foreach ($srvips as $srcidx => $srvip) {
1656
		unset($destif);
1657
		foreach ($iflist as $ifname) {
1658
			$subnet = get_interface_ipv6($ifname);
1659
			if (!is_ipaddrv6($subnet))
1660
				continue;
1661
			$subnet .=  "/" . get_interface_subnetv6($ifname);
1662
			if (ip_in_subnet($srvip, $subnet)) {
1663
				$destif = get_real_interface($ifname);
1664
				break;
1665
			}
1666
		}
1667
		if (!isset($destif)) {
1668
			if (is_array($config['staticroutes']['route'])) {
1669
				foreach ($config['staticroutes']['route'] as $rtent) {
1670
					if (ip_in_subnet($srvip, $rtent['network'])) {
1671
						$a_gateways = return_gateways_array(true);
1672
						$destif = $a_gateways[$rtent['gateway']]['interface'];
1673
						break;
1674
					}
1675
				}
1676
			}
1677
		}
1678

    
1679
		if (!isset($destif)) {
1680
			/* Create a array from the existing route table */
1681
        		exec("/usr/bin/netstat -rnWf inet6", $route_str);
1682
        		array_shift($route_str);
1683
        		array_shift($route_str);
1684
        		array_shift($route_str);
1685
        		array_shift($route_str);
1686
        		$route_arr = array();
1687
        		foreach($route_str as $routeline) {
1688
                		$items = preg_split("/[ ]+/i", $routeline);
1689
				if (ip_in_subnet($srvip, $items[0])) {
1690
					$destif = trim($items[6]);
1691
					break;
1692
				}
1693
        		}
1694
		}
1695

    
1696
		if (!isset($destif)) {
1697
			if (is_array($config['gateways']['gateway_item'])) {
1698
				foreach ($config['gateways']['gateway_item'] as $gateway) {
1699
					if (isset($gateway['defaultgw'])) {
1700
						$destif = get_real_interface($gateway['interface']);
1701
						break;
1702
					}
1703
				}
1704
			} else
1705
				$destif = get_real_interface("wan");
1706
		}
1707

    
1708
		if (!empty($destif)) {
1709
			$srvifaces[] = "{$srvip}%{$destif}";
1710
		}
1711
	}
1712

    
1713
	/* fire up dhcrelay */
1714
	if (empty($dhcrelayifs) || empty($srvifaces) ) {
1715
		log_error("No suitable interface found for running dhcrelay -6!");
1716
		return; /* XXX */
1717
	}
1718

    
1719
	$cmd = "/usr/local/sbin/dhcrelay -6 -pf \"{$g['varrun_path']}/dhcrelay6.pid\"";
1720
	foreach ($dhcrelayifs as $dhcrelayif) {
1721
		$cmd .= " -l {$dhcrelayif}";
1722
	}
1723
	foreach ($srvifaces as $srviface) {
1724
		$cmd .= " -u \"{$srviface}\"";
1725
	}
1726
	mwexec($cmd);
1727
	unset($cmd);
1728

    
1729
	return 0;
1730
}
1731

    
1732
function services_dyndns_configure_client($conf) {
1733

    
1734
	if (!isset($conf['enable']))
1735
		return;
1736

    
1737
	/* load up the dyndns.class */
1738
	require_once("dyndns.class");
1739

    
1740
	$dns = new updatedns($dnsService = $conf['type'],
1741
		$dnsHost = $conf['host'],
1742
		$dnsUser = $conf['username'],
1743
		$dnsPass = $conf['password'],
1744
		$dnsWildcard = $conf['wildcard'],
1745
		$dnsMX = $conf['mx'],
1746
		$dnsIf = "{$conf['interface']}",
1747
		$dnsBackMX = NULL,
1748
		$dnsServer = NULL,
1749
		$dnsPort = NULL,
1750
		$dnsUpdateURL = "{$conf['updateurl']}",
1751
		$forceUpdate = $conf['force'],
1752
		$dnsZoneID=$conf['zoneid'],
1753
		$dnsTTL=$conf['ttl'],
1754
		$dnsResultMatch = "{$conf['resultmatch']}",
1755
		$dnsRequestIf = "{$conf['requestif']}",
1756
		$dnsID = "{$conf['id']}",
1757
		$dnsVerboseLog = $conf['verboselog'],
1758
		$curlIpresolveV4 = $conf['curl_ipresolve_v4'],
1759
		$curlSslVerifypeer = $conf['curl_ssl_verifypeer']);
1760
}
1761

    
1762
function services_dyndns_configure($int = "") {
1763
	global $config, $g;
1764
	if(isset($config['system']['developerspew'])) {
1765
		$mt = microtime();
1766
		echo "services_dyndns_configure() being called $mt\n";
1767
	}
1768

    
1769
	$dyndnscfg = $config['dyndnses']['dyndns'];
1770
	$gwgroups = return_gateway_groups_array();
1771
	if (is_array($dyndnscfg)) {
1772
		if (platform_booting())
1773
			echo gettext("Starting DynDNS clients...");
1774

    
1775
		foreach ($dyndnscfg as $dyndns) {
1776
			if ((empty($int)) || ($int == $dyndns['interface']) || (is_array($gwgroups[$dyndns['interface']]))) {
1777
				$dyndns['verboselog'] = isset($dyndns['verboselog']);
1778
				$dyndns['curl_ipresolve_v4'] = isset($dyndns['curl_ipresolve_v4']);
1779
				$dyndns['curl_ssl_verifypeer'] = isset($dyndns['curl_ssl_verifypeer']);
1780
				services_dyndns_configure_client($dyndns);
1781
				sleep(1);
1782
			}
1783
		}
1784

    
1785
		if (platform_booting())
1786
			echo gettext("done.") . "\n";
1787
	}
1788

    
1789
	return 0;
1790
}
1791

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

    
1818
function services_dnsmasq_configure() {
1819
	global $config, $g;
1820
	$return = 0;
1821

    
1822
	// hard coded args: will be removed to avoid duplication if specified in custom_options
1823
	$standard_args = array(
1824
		"dns-forward-max" => "--dns-forward-max=5000",
1825
		"cache-size" => "--cache-size=10000",
1826
		"local-ttl" => "--local-ttl=1"
1827
	);
1828

    
1829

    
1830
	if(isset($config['system']['developerspew'])) {
1831
		$mt = microtime();
1832
		echo "services_dnsmasq_configure() being called $mt\n";
1833
	}
1834

    
1835
	/* kill any running dnsmasq */
1836
	if (file_exists("{$g['varrun_path']}/dnsmasq.pid"))
1837
		sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM");
1838

    
1839
	if (isset($config['dnsmasq']['enable'])) {
1840

    
1841
		if (platform_booting())
1842
			echo gettext("Starting DNS forwarder...");
1843
		else
1844
			sleep(1);
1845

    
1846
                /* generate hosts file */
1847
                if(system_hosts_generate()!=0)
1848
                        $return = 1;
1849

    
1850
		$args = "";
1851

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

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

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

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

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

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

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

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

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

    
1957
		if(!isset($config['system']['webgui']['nodnsrebindcheck']))
1958
			$dns_rebind = "--rebind-localhost-ok --stop-dns-rebind";
1959

    
1960
		if (isset($config['dnsmasq']['strict_order'])) {
1961
			$args .= " --strict-order ";
1962
		}
1963

    
1964
		if (isset($config['dnsmasq']['domain_needed'])) {
1965
			$args .= " --domain-needed ";
1966
		}
1967

    
1968
		if ($config['dnsmasq']['custom_options'])
1969
			foreach (preg_split('/\s+/', $config['dnsmasq']['custom_options']) as $c) {
1970
				$args .= " " . escapeshellarg("--{$c}");
1971
				$p = explode('=', $c);
1972
				if (array_key_exists($p[0], $standard_args))
1973
					unset($standard_args[$p[0]]);
1974
			}
1975
		$args .= ' ' . implode(' ', array_values($standard_args));
1976

    
1977
		/* run dnsmasq */
1978
		$cmd = "/usr/local/sbin/dnsmasq --all-servers {$dns_rebind} {$args}";
1979
		//log_error("dnsmasq command: {$cmd}");
1980
		mwexec_bg($cmd);
1981
		unset($args);
1982

    
1983
        system_dhcpleases_configure();
1984

    
1985
		if (platform_booting())
1986
			echo gettext("done.") . "\n";
1987
	}
1988

    
1989
	if (!platform_booting()) {
1990
		if(services_dhcpd_configure()!=0)
1991
			$return = 1;
1992
	}
1993

    
1994
	return $return;
1995
}
1996

    
1997
function services_unbound_configure() {
1998
	global $config, $g;
1999
	$return = 0;
2000

    
2001
	if (isset($config['system']['developerspew'])) {
2002
		$mt = microtime();
2003
		echo "services_unbound_configure() being called $mt\n";
2004
	}
2005

    
2006
	// kill any running Unbound instance
2007
	if (file_exists("{$g['varrun_path']}/unbound.pid"))
2008
		sigkillbypid("{$g['varrun_path']}/unbound.pid", "TERM");
2009

    
2010
	if (isset($config['unbound']['enable'])) {
2011
		if (platform_booting())
2012
			echo gettext("Starting DNS Resolver...");
2013
		else
2014
			sleep(1);
2015

    
2016
		/* generate hosts file */
2017
		if(system_hosts_generate()!=0)
2018
			$return = 1;
2019

    
2020
		require_once('/etc/inc/unbound.inc');
2021
		sync_unbound_service();
2022
		if (platform_booting())
2023
			echo gettext("done.") . "\n";
2024

    
2025
        system_dhcpleases_configure();
2026
	}
2027

    
2028
	if (!platform_booting()) {
2029
		if (services_dhcpd_configure()!=0)
2030
			$return = 1;
2031
	}
2032

    
2033
	return $return;
2034
}
2035

    
2036
function services_snmpd_configure() {
2037
	global $config, $g;
2038
	if(isset($config['system']['developerspew'])) {
2039
		$mt = microtime();
2040
		echo "services_snmpd_configure() being called $mt\n";
2041
	}
2042

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

    
2049
	if (isset($config['snmpd']['enable'])) {
2050

    
2051
		if (platform_booting())
2052
			echo gettext("Starting SNMP daemon... ");
2053

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

    
2061

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

    
2067
EOD;
2068

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

    
2075
EOD;
2076
		}
2077
*/
2078

    
2079

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

    
2087

    
2088
EOD;
2089
		}
2090

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

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

    
2107
EOD;
2108

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

    
2114
EOD;
2115
		}
2116
*/
2117

    
2118

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

    
2125
EOD;
2126
		}
2127

    
2128

    
2129
		$snmpdconf .= <<<EOD
2130
begemotSnmpdCommunityDisable    = 1
2131

    
2132
EOD;
2133

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

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

    
2153
EOD;
2154

    
2155
		}
2156

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

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

    
2166
snmpEnableAuthenTraps = 2
2167

    
2168
EOD;
2169

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

    
2175
EOD;
2176
		    }
2177

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

    
2184
EOD;
2185
		    }
2186

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

    
2191
EOD;
2192
		    }
2193

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

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

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

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

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

    
2225
EOD;
2226
			}
2227
		}
2228

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

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

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

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

    
2245
	return 0;
2246
}
2247

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

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

    
2266
			/* determine interface name */
2267
			$if = get_real_interface($dnsupdate['interface']);
2268
			
2269
			if (isset($dnsupdate['usepublicip']))
2270
                                $wanip = dyndnsCheckIP($dnsupdate['interface']);
2271
                        else
2272
                                $wanip = get_interface_ip($dnsupdate['interface']);
2273
			
2274
			$wanipv6 = get_interface_ipv6($dnsupdate['interface']);
2275
			$cacheFile = "{$g['conf_path']}/dyndns_{$dnsupdate['interface']}_rfc2136_" . escapeshellarg($dnsupdate['host']) . "_{$dnsupdate['server']}.cache";
2276
			$currentTime = time();
2277

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

    
2284
				$hostname = $dnsupdate['host'];
2285
				/* trailing dot */
2286
				if (substr($hostname, -1) != ".")
2287
					$hostname .= ".";
2288

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

    
2298
EOD;
2299
				fwrite($fd, $privkey);
2300
				fclose($fd);
2301

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

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

    
2318
				/* generate update instructions */
2319
				$upinst = "";
2320
				if (!empty($dnsupdate['server']))
2321
					$upinst .= "server {$dnsupdate['server']}\n";
2322

    
2323
				if (file_exists($cacheFile)) {
2324
					list($cachedipv4, $cacheTimev4) = explode("|", file_get_contents($cacheFile));
2325
				}
2326
				if (file_exists("{$cacheFile}.ipv6")) {
2327
					list($cachedipv6, $cacheTimev6) = explode("|", file_get_contents("{$cacheFile}.ipv6"));
2328
				}
2329

    
2330
				// 25 Days
2331
				$maxCacheAgeSecs = 25 * 24 * 60 * 60;
2332
				$need_update = false;
2333

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

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

    
2366
				$upinst .= "\n";	/* mind that trailing newline! */
2367

    
2368
				if ($need_update) {
2369
					@file_put_contents("{$g['varetc_path']}/nsupdatecmds{$i}", $upinst);
2370
					unset($upinst);
2371
					/* invoke nsupdate */
2372
					$cmd = "/usr/local/bin/nsupdate -k {$g['varetc_path']}/K{$i}{$keyname}+157+00000.key";
2373
					if (isset($dnsupdate['usetcp']))
2374
						$cmd .= " -v";
2375
					$cmd .= " {$g['varetc_path']}/nsupdatecmds{$i}";
2376
					mwexec_bg($cmd);
2377
					unset($cmd);
2378
				}
2379
			}
2380
		}
2381
		if (!empty($notify_text)) {
2382
			notify_all_remote($notify_text);
2383
		}
2384
	}
2385

    
2386
	return 0;
2387
}
2388

    
2389
/* configure cron service */
2390
function configure_cron() {
2391
	global $g, $config;
2392

    
2393
	conf_mount_rw();
2394
	/* preserve existing crontab entries */
2395
	$crontab_contents = file("/etc/crontab", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2396

    
2397
	for ($i = 0; $i < count($crontab_contents); $i++) {
2398
		$cron_item =& $crontab_contents[$i];
2399
		if (strpos($cron_item, "# pfSense specific crontab entries") !== false) {
2400
			array_splice($crontab_contents, $i - 1);
2401
			break;
2402
		}
2403
	}
2404
	$crontab_contents = implode("\n", $crontab_contents) . "\n";
2405

    
2406

    
2407
	if (is_array($config['cron']['item'])) {
2408
		$crontab_contents .= "#\n";
2409
		$crontab_contents .= "# " . gettext("pfSense specific crontab entries") . "\n";
2410
		$crontab_contents .= "# " .gettext( "Created:") . " " . date("F j, Y, g:i a") . "\n";
2411
		$crontab_contents .= "#\n";
2412

    
2413
		if (isset($config['system']['proxyurl']) && !empty($config['system']['proxyurl'])) {
2414
			$http_proxy = $config['system']['proxyurl'];
2415
			if (isset($config['system']['proxyport']) && !empty($config['system']['proxyport']))
2416
				$http_proxy .= ':' . $config['system']['proxyport'];
2417
			$crontab_contents .= "HTTP_PROXY={$http_proxy}";
2418
		}
2419

    
2420
		foreach ($config['cron']['item'] as $item) {
2421
			$crontab_contents .= "\n{$item['minute']}\t";
2422
			$crontab_contents .= "{$item['hour']}\t";
2423
			$crontab_contents .= "{$item['mday']}\t";
2424
			$crontab_contents .= "{$item['month']}\t";
2425
			$crontab_contents .= "{$item['wday']}\t";
2426
			$crontab_contents .= "{$item['who']}\t";
2427
			$crontab_contents .= "{$item['command']}";
2428
		}
2429

    
2430
		$crontab_contents .= "\n#\n";
2431
		$crontab_contents .= "# " . gettext("If possible do not add items to this file manually.") . "\n";
2432
		$crontab_contents .= "# " . gettext("If you do so, this file must be terminated with a blank line (e.g. new line)") . "\n";
2433
		$crontab_contents .= "#\n\n";
2434
	}
2435

    
2436
	/* please maintain the newline at the end of file */
2437
	file_put_contents("/etc/crontab", $crontab_contents);
2438
	unset($crontab_contents);
2439

    
2440
	/* make sure that cron is running and start it if it got killed somehow */
2441
	if (!is_process_running("cron")) {
2442
		exec("cd /tmp && /usr/sbin/cron -s 2>/dev/null");
2443
	} else {
2444
	/* do a HUP kill to force sync changes */
2445
		sigkillbypid("{$g['varrun_path']}/cron.pid", "HUP");
2446
	}
2447

    
2448
	conf_mount_ro();
2449
}
2450

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

    
2474
function upnp_start() {
2475
	global $config;
2476

    
2477
	if(!isset($config['installedpackages']['miniupnpd']['config']))
2478
		return;
2479

    
2480
	if($config['installedpackages']['miniupnpd']['config'][0]['enable']) {
2481
		echo gettext("Starting UPnP service... ");
2482
		require_once('/usr/local/pkg/miniupnpd.inc');
2483
		sync_package_miniupnpd();
2484
		echo "done.\n";
2485
	}
2486
}
2487

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

    
2491
	$is_installed = false;
2492
	$cron_changed = true;
2493

    
2494
	if (!is_array($config['cron']))
2495
		$config['cron'] = array();
2496
	if (!is_array($config['cron']['item']))
2497
		$config['cron']['item'] = array();
2498

    
2499
	$x=0;
2500
	foreach($config['cron']['item'] as $item) {
2501
		if(strstr($item['command'], $command)) {
2502
			$is_installed = true;
2503
			break;
2504
		}
2505
		$x++;
2506
	}
2507

    
2508
	if($active) {
2509
		$cron_item = array();
2510
		$cron_item['minute'] = $minute;
2511
		$cron_item['hour'] = $hour;
2512
		$cron_item['mday'] = $monthday;
2513
		$cron_item['month'] = $month;
2514
		$cron_item['wday'] = $weekday;
2515
		$cron_item['who'] = $who;
2516
		$cron_item['command'] = $command;
2517
		if(!$is_installed) {
2518
			$config['cron']['item'][] = $cron_item;
2519
			write_config(sprintf(gettext("Installed cron job for %s"), $command));
2520
		} else {
2521
			if ($config['cron']['item'][$x] == $cron_item) {
2522
				$cron_changed = false;
2523
				log_error(sprintf(gettext("Checked cron job for %s, no change needed"), $command));
2524
			} else {
2525
				$config['cron']['item'][$x] = $cron_item;
2526
				write_config(sprintf(gettext("Updated cron job for %s"), $command));
2527
			}
2528
		}
2529
	} else {
2530
		if($is_installed == true) {
2531
			unset($config['cron']['item'][$x]);
2532
			write_config(sprintf(gettext("Removed cron job for %s"), $command));
2533
		}
2534
	}
2535

    
2536
	if ($cron_changed)
2537
		configure_cron();
2538
}
2539

    
2540
?>
(50-50/68)