Project

General

Profile

Download (161 KB) Statistics
| Branch: | Tag: | Revision:
1 17f6eafa Scott Ullrich
<?php
2 5b237745 Scott Ullrich
/*
3 ac24dc24 Renato Botelho
 * services.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6 38809d47 Renato Botelho do Couto
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8 a68f7a3d Luiz Otavio O Souza
 * Copyright (c) 2014-2024 Rubicon Communications, LLC (Netgate)
9 ac24dc24 Renato Botelho
 * All rights reserved.
10
 *
11
 * originally part of m0n0wall (http://m0n0.ch/wall)
12 c5d81585 Renato Botelho
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
13 ac24dc24 Renato Botelho
 * All rights reserved.
14
 *
15 b12ea3fb Renato Botelho
 * Licensed under the Apache License, Version 2.0 (the "License");
16
 * you may not use this file except in compliance with the License.
17
 * You may obtain a copy of the License at
18 ac24dc24 Renato Botelho
 *
19 b12ea3fb Renato Botelho
 * http://www.apache.org/licenses/LICENSE-2.0
20 ac24dc24 Renato Botelho
 *
21 b12ea3fb Renato Botelho
 * Unless required by applicable law or agreed to in writing, software
22
 * distributed under the License is distributed on an "AS IS" BASIS,
23
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
 * See the License for the specific language governing permissions and
25
 * limitations under the License.
26 ac24dc24 Renato Botelho
 */
27 5b237745 Scott Ullrich
28 a4cd7de1 Christian McDonald
require_once('services_dhcp.inc');
29 12e3bbce Matthew Fine
30 d9d91d5d Nita Vesa
define('DYNDNS_PROVIDER_VALUES', 'all-inkl azure azurev6 citynetwork cloudflare cloudflare-v6 cloudns custom custom-v6 desec desec-v6 digitalocean digitalocean-v6 dnsexit dnsimple dnsimple-v6 dnsmadeeasy dnsomatic domeneshop domeneshop-v6 dreamhost dreamhost-v6 duiadns duiadns-v6 dyfi dyndns dyndns-custom dyndns-static dyns dynv6 dynv6-v6 easydns easydns-v6 eurodns freedns freedns-v6 freedns2 freedns2-v6 glesys gandi-livedns gandi-livedns-v6 godaddy godaddy-v6 googledomains gratisdns he-net he-net-v6 he-net-tunnelbroker hover linode linode-v6 loopia mythicbeasts mythicbeasts-v6 name.com name.com-v6 namecheap nicru nicru-v6 noip noip-v6 noip-free noip-free-v6 onecom onecom-v6 ods opendns ovh-dynhost route53 route53-v6 selfhost spdyn spdyn-v6 strato yandex yandex-v6 zoneedit porkbun porkbun-v6');
31 e2b29aac Christopher
define('DYNDNS_PROVIDER_DESCRIPTIONS', 'All-Inkl.com,Azure DNS,Azure DNS (v6),City Network,Cloudflare,Cloudflare (v6),ClouDNS,Custom,Custom (v6),deSEC,deSEC (v6),DigitalOcean,DigitalOcean (v6),DNSexit,DNSimple,DNSimple (v6),DNS Made Easy,DNS-O-Matic,Domeneshop,Domeneshop (v6),DreamHost,Dreamhost (v6),DuiaDns.net,DuiaDns.net (v6),DY.fi,DynDNS (dynamic),DynDNS (custom),DynDNS (static),DyNS,Dynv6,Dynv6 (v6),easyDNS,easyDNS (v6),Euro Dns,freeDNS,freeDNS (v6),freeDNS API Version 2, freeDNS API Version 2 (v6),GleSYS,Gandi LiveDNS,Gandi LiveDNS (v6),GoDaddy,GoDaddy (v6),Google Domains,GratisDNS,HE.net,HE.net (v6),HE.net Tunnelbroker,Hover,Linode,Linode (v6),Loopia,Mythic Beasts,Mythic Beasts (v6),Name.com,Name.com (v6),Namecheap,NIC.RU,NIC.RU (v6),No-IP,No-IP (v6),No-IP (free),No-IP (free-v6),One.com,One.com (v6),ODS.org,OpenDNS,OVH DynHOST,Route 53,Route 53 (v6),SelfHost,SPDYN,SPDYN (v6),Strato,Yandex,Yandex (v6),ZoneEdit,Porkbun,Porkbun (v6)');
32 0e3aeb6b Phil Davis
33 3aa55bbe Phil Davis
/* implement ipv6 route advertising daemon */
34 92977616 Ermal
function services_radvd_configure($blacklist = array()) {
35 532a1a0e Reid Linnemann
	global $g;
36 61e047a5 Phil Davis
37 532a1a0e Reid Linnemann
	if (config_path_enabled('system','developerspew')) {
38 d57293a4 Seth Mos
		$mt = microtime();
39 3f9cc8e4 smos
		echo "services_radvd_configure() being called $mt\n";
40 d57293a4 Seth Mos
	}
41
42 63d6bb4f Marcos Mendoza
	config_init_path('dhcpdv6');
43 d57293a4 Seth Mos
44
	$Iflist = get_configured_interface_list();
45 e9ab2ddb smos
	$Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces());
46 d57293a4 Seth Mos
47 3f9cc8e4 smos
	$radvdconf = "# Automatically Generated, do not edit\n";
48 4a3ff493 Seth Mos
49 753bd64d Seth Mos
	/* Process all links which need the router advertise daemon */
50 3f9cc8e4 smos
	$radvdifs = array();
51 668e8961 smos
52 3f9cc8e4 smos
	/* handle manually configured DHCP6 server settings first */
53 532a1a0e Reid Linnemann
	foreach (config_get_path('dhcpdv6', []) as $dhcpv6if => $dhcpv6ifconf) {
54 fae6b2c0 jim-p
		if (empty($dhcpv6ifconf)) {
55
			continue;
56
		}
57 532a1a0e Reid Linnemann
		if (!config_path_enabled("interfaces/{$dhcpv6if}")) {
58 d7d2dc52 smos
			continue;
59 61e047a5 Phil Davis
		}
60 5078cd76 smos
61 92977616 Ermal
		/* Do not put in the config an interface which is down */
62 61e047a5 Phil Davis
		if (isset($blacklist[$dhcpv6if])) {
63 92977616 Ermal
			continue;
64 61e047a5 Phil Davis
		}
65
		if (!isset($dhcpv6ifconf['ramode'])) {
66 8ca73e85 smos
			$dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode'];
67 61e047a5 Phil Davis
		}
68 8ca73e85 smos
69 3f9cc8e4 smos
		/* are router advertisements enabled? */
70 61e047a5 Phil Davis
		if ($dhcpv6ifconf['ramode'] == "disabled") {
71 361bb4a9 smos
			continue;
72 61e047a5 Phil Davis
		}
73 d57293a4 Seth Mos
74 61e047a5 Phil Davis
		if (!isset($dhcpv6ifconf['rapriority'])) {
75 8ca73e85 smos
			$dhcpv6ifconf['rapriority'] = "medium";
76 61e047a5 Phil Davis
		}
77 8ca73e85 smos
78 c0509674 Chris Buechler
		$racarpif = false;
79 8d4adafb znerol
		$rasrcaddr = "";
80 c0509674 Chris Buechler
		/* check if binding to CARP IP */
81 abc7b305 Viktor G
		if (!empty($dhcpv6ifconf['rainterface']) && strstr($dhcpv6ifconf['rainterface'], "_vip")) {
82
			if (get_carp_interface_status($dhcpv6ifconf['rainterface']) == "MASTER") {
83 8d4adafb znerol
				if (is_linklocal(get_configured_vip_ipv6($dhcpv6ifconf['rainterface']))) {
84
					$rasrcaddr = get_configured_vip_ipv6($dhcpv6ifconf['rainterface']);
85 abc7b305 Viktor G
				} else {
86 c0509674 Chris Buechler
					$dhcpv6if = $dhcpv6ifconf['rainterface'];
87
					$racarpif = true;
88
				}
89 abc7b305 Viktor G
			} else {
90
				continue;
91 c0509674 Chris Buechler
			}
92
		}
93
94 2a5960b0 Luiz Otavio O Souza
		$realif = get_real_interface($dhcpv6if, "inet6");
95 61e047a5 Phil Davis
96
		if (isset($radvdifs[$realif])) {
97 c18a10cc smos
			continue;
98 61e047a5 Phil Davis
		}
99 c18a10cc smos
100 2626cbd1 Ermal
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
101 61e047a5 Phil Davis
		if (!is_ipaddrv6($ifcfgipv6)) {
102 5078cd76 smos
			continue;
103 61e047a5 Phil Davis
		}
104 5078cd76 smos
105 d57293a4 Seth Mos
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
106
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
107 f8f8d131 Chris Buechler
		if (!is_subnetv6($subnetv6 . "/" . $ifcfgsnv6)) {
108
			log_error("radvd: skipping configuration for interface $dhcpv6if because its subnet or prefix length is invalid.");
109
			continue;
110
		}
111 60c05056 Ermal
		$radvdifs[$realif] = $realif;
112 d57293a4 Seth Mos
113 3f9cc8e4 smos
		$radvdconf .= "# Generated for DHCPv6 Server $dhcpv6if\n";
114
		$radvdconf .= "interface {$realif} {\n";
115 e03b6bbc Chris Buechler
		if (strstr($realif, "ovpn")) {
116
			$radvdconf .= "\tUnicastOnly on;\n";
117
		}
118 3f9cc8e4 smos
		$radvdconf .= "\tAdvSendAdvert on;\n";
119 c3099b79 Chris Buechler
120 8d4adafb znerol
		if ($rasrcaddr) {
121
			$radvdconf .= "\tAdvRASrcAddress {\n";
122
			$radvdconf .= "\t\t{$rasrcaddr};\n";
123
			$radvdconf .= "\t};\n";
124
		}
125
126 c3099b79 Chris Buechler
		if (is_numericint($dhcpv6ifconf['raminrtradvinterval'])) {
127
			$radvdconf .= "\tMinRtrAdvInterval {$dhcpv6ifconf['raminrtradvinterval']};\n";
128
		} else {
129 762d3cc9 Viktor G
			$radvdconf .= "\tMinRtrAdvInterval 200;\n";
130 c3099b79 Chris Buechler
		}
131
132
		if (is_numericint($dhcpv6ifconf['ramaxrtradvinterval'])) {
133 54b3109f Viktor G
			$ramaxrtradvinterval = $dhcpv6ifconf['ramaxrtradvinterval'];
134 c3099b79 Chris Buechler
		} else {
135 762d3cc9 Viktor G
			$ramaxrtradvinterval = 600;
136 c3099b79 Chris Buechler
		}
137 54b3109f Viktor G
		$radvdconf .= "\tMaxRtrAdvInterval {$ramaxrtradvinterval};\n";
138 d37bd612 Chris Buechler
		if (is_numericint($dhcpv6ifconf['raadvdefaultlifetime'])) {
139 54b3109f Viktor G
			$raadvdefaultlifetime = $dhcpv6ifconf['raadvdefaultlifetime'];
140
		} else {
141
			$raadvdefaultlifetime = $ramaxrtradvinterval * 3;
142 d37bd612 Chris Buechler
		}
143 54b3109f Viktor G
		$radvdconf .= "\tAdvDefaultLifetime {$raadvdefaultlifetime};\n";
144 c3099b79 Chris Buechler
145 a6bc492f Ermal
		$mtu = get_interface_mtu($realif);
146 61e047a5 Phil Davis
		if (is_numeric($mtu)) {
147 a6bc492f Ermal
			$radvdconf .= "\tAdvLinkMTU {$mtu};\n";
148 61e047a5 Phil Davis
		} else {
149 a6bc492f Ermal
			$radvdconf .= "\tAdvLinkMTU 1280;\n";
150 61e047a5 Phil Davis
		}
151
		switch ($dhcpv6ifconf['rapriority']) {
152 fe838158 smos
			case "low":
153
				$radvdconf .= "\tAdvDefaultPreference low;\n";
154
				break;
155
			case "high":
156
				$radvdconf .= "\tAdvDefaultPreference high;\n";
157 838a1ecb smos
				break;
158
			default:
159
				$radvdconf .= "\tAdvDefaultPreference medium;\n";
160
				break;
161 fe838158 smos
		}
162 61e047a5 Phil Davis
		switch ($dhcpv6ifconf['ramode']) {
163 656f1763 Seth Mos
			case "managed":
164 3f9cc8e4 smos
			case "assist":
165 8c78e692 plinss
				$radvdconf .= "\tAdvManagedFlag on;\n";
166 3f9cc8e4 smos
				$radvdconf .= "\tAdvOtherConfigFlag on;\n";
167
				break;
168 bd7ce993 aqueeb
			case "stateless_dhcp":
169
				$radvdconf .= "\tAdvManagedFlag off;\n";
170
				$radvdconf .= "\tAdvOtherConfigFlag on;\n";
171
				break;
172 3f9cc8e4 smos
		}
173 9462cc40 Viktor G
		$radvdconf .= "\tprefix {$subnetv6}/{$ifcfgsnv6} {\n";
174 ad27159f znerol
		if ($racarpif == true || $rasrcaddr) {
175 c0509674 Chris Buechler
			$radvdconf .= "\t\tDeprecatePrefix off;\n";
176
		} else {
177
			$radvdconf .= "\t\tDeprecatePrefix on;\n";
178
		}
179 61e047a5 Phil Davis
		switch ($dhcpv6ifconf['ramode']) {
180 3f9cc8e4 smos
			case "managed":
181
				$radvdconf .= "\t\tAdvOnLink on;\n";
182
				$radvdconf .= "\t\tAdvAutonomous off;\n";
183 826ac52c smos
				break;
184
			case "router":
185 3f9cc8e4 smos
				$radvdconf .= "\t\tAdvOnLink off;\n";
186
				$radvdconf .= "\t\tAdvAutonomous off;\n";
187 656f1763 Seth Mos
				break;
188 bd7ce993 aqueeb
			case "stateless_dhcp":
189 656f1763 Seth Mos
			case "assist":
190 3f9cc8e4 smos
				$radvdconf .= "\t\tAdvOnLink on;\n";
191
				$radvdconf .= "\t\tAdvAutonomous on;\n";
192 107e8acc Ovidiu Predescu
				break;
193 3f9cc8e4 smos
			case "unmanaged":
194
				$radvdconf .= "\t\tAdvOnLink on;\n";
195
				$radvdconf .= "\t\tAdvAutonomous on;\n";
196 61e047a5 Phil Davis
				break;
197 656f1763 Seth Mos
		}
198 352defe4 schinken
199 6a461f45 Phil Davis
		if (is_numericint($dhcpv6ifconf['ravalidlifetime'])) {
200 352defe4 schinken
		  $radvdconf .= "\t\tAdvValidLifetime {$dhcpv6ifconf['ravalidlifetime']};\n";
201 be4748a8 schinken
		} else {
202
		  $radvdconf .= "\t\tAdvValidLifetime 86400;\n";
203 352defe4 schinken
		}
204
205 6a461f45 Phil Davis
		if (is_numericint($dhcpv6ifconf['rapreferredlifetime'])) {
206 352defe4 schinken
		  $radvdconf .= "\t\tAdvPreferredLifetime {$dhcpv6ifconf['rapreferredlifetime']};\n";
207 be4748a8 schinken
		} else {
208
		  $radvdconf .= "\t\tAdvPreferredLifetime 14400;\n";
209 352defe4 schinken
		}
210
211 3f9cc8e4 smos
		$radvdconf .= "\t};\n";
212
213 686e53c0 Chris Buechler
		if (is_array($dhcpv6ifconf['subnets']['item'])) {
214
			foreach ($dhcpv6ifconf['subnets']['item'] as $subnet) {
215
				if (is_subnetv6($subnet)) {
216
					$radvdconf .= "\tprefix {$subnet} {\n";
217 2a5960b0 Luiz Otavio O Souza
					$radvdconf .= "\t\tDeprecatePrefix on;\n";
218 cf3904bd Phil Davis
					switch ($dhcpv6ifconf['ramode']) {
219 686e53c0 Chris Buechler
						case "managed":
220
							$radvdconf .= "\t\tAdvOnLink on;\n";
221
							$radvdconf .= "\t\tAdvAutonomous off;\n";
222
							break;
223
						case "router":
224
							$radvdconf .= "\t\tAdvOnLink off;\n";
225
							$radvdconf .= "\t\tAdvAutonomous off;\n";
226
							break;
227
						case "assist":
228
							$radvdconf .= "\t\tAdvOnLink on;\n";
229
							$radvdconf .= "\t\tAdvAutonomous on;\n";
230
							break;
231
						case "unmanaged":
232
							$radvdconf .= "\t\tAdvOnLink on;\n";
233
							$radvdconf .= "\t\tAdvAutonomous on;\n";
234 086cf944 Phil Davis
							break;
235 686e53c0 Chris Buechler
					}
236
					$radvdconf .= "\t};\n";
237
				}
238
			}
239
		}
240 2a5960b0 Luiz Otavio O Souza
		$radvdconf .= "\troute ::/0 {\n";
241 6aefdd43 Tomas Krajca
		switch ($dhcpv6ifconf['rapriority']) {
242
			case "low":
243
				$radvdconf .= "\t\tAdvRoutePreference low;\n";
244
				break;
245
			case "high":
246
				$radvdconf .= "\t\tAdvRoutePreference high;\n";
247
				break;
248
			default:
249
				$radvdconf .= "\t\tAdvRoutePreference medium;\n";
250
				break;
251
		}
252 55b55478 znerol
253
		if ($rasrcaddr) {
254
			$radvdconf .= "\t\tRemoveRoute off;\n";
255
		}
256
		else {
257
			$radvdconf .= "\t\tRemoveRoute on;\n";
258
		}
259 2a5960b0 Luiz Otavio O Souza
		$radvdconf .= "\t};\n";
260 8859c0a6 smos
261 f535d5a0 Seth Mos
		/* add DNS servers */
262 09646aef luckman212
		if ($dhcpv6ifconf['radvd-dns'] != 'disabled') {
263 e26ad76e luckman212
			$dnslist = array();
264
			if (isset($dhcpv6ifconf['rasamednsasdhcp6']) && is_array($dhcpv6ifconf['dnsserver']) && !empty($dhcpv6ifconf['dnsserver'])) {
265
				foreach ($dhcpv6ifconf['dnsserver'] as $server) {
266
					if (is_ipaddrv6($server)) {
267
						$dnslist[] = $server;
268
					}
269
				}
270
			} elseif (!isset($dhcpv6ifconf['rasamednsasdhcp6']) && isset($dhcpv6ifconf['radnsserver']) && is_array($dhcpv6ifconf['radnsserver'])) {
271
				foreach ($dhcpv6ifconf['radnsserver'] as $server) {
272
					if (is_ipaddrv6($server)) {
273
						$dnslist[] = $server;
274
					}
275
				}
276 532a1a0e Reid Linnemann
			} elseif (config_path_enabled('dnsmasq') || config_path_enabled('unbound')) {
277 e26ad76e luckman212
				$dnslist[] = get_interface_ipv6($realif);
278 532a1a0e Reid Linnemann
			} else {
279
				foreach (config_get_path('system/dnsserver', []) as $server) {
280 e26ad76e luckman212
					if (is_ipaddrv6($server)) {
281
						$dnslist[] = $server;
282
					}
283 61e047a5 Phil Davis
				}
284
			}
285 96270d7c Viktor G
			$raadvdnsslifetime = $ramaxrtradvinterval * 3;
286 e26ad76e luckman212
			if (count($dnslist) > 0) {
287
				$dnsstring = implode(" ", $dnslist);
288
				if ($dnsstring <> "") {
289 b63b534c Christian McDonald
					/*
290 54b3109f Viktor G
					 * The value of Lifetime SHOULD by default be at least
291
					 * 3 * MaxRtrAdvInterval, where MaxRtrAdvInterval is the
292
					 * maximum RA interval as defined in [RFC4861].
293 b63b534c Christian McDonald
					 * see https://redmine.pfsense.org/issues/11105
294 54b3109f Viktor G
					 */
295
					$radvdconf .= "\tRDNSS {$dnsstring} {\n";
296 96270d7c Viktor G
					$radvdconf .= "\t\tAdvRDNSSLifetime {$raadvdnsslifetime};\n";
297 54b3109f Viktor G
					$radvdconf .= "\t};\n";
298 61e047a5 Phil Davis
				}
299
			}
300 e26ad76e luckman212
301
			$searchlist = array();
302
			$domainsearchlist = explode(';', $dhcpv6ifconf['radomainsearchlist']);
303
			foreach ($domainsearchlist as $sd) {
304
				$sd = trim($sd);
305
				if (is_hostname($sd)) {
306
					$searchlist[] = $sd;
307 61e047a5 Phil Davis
				}
308 3aa114d5 Seth Mos
			}
309 e26ad76e luckman212
			if (count($searchlist) > 0) {
310
				$searchliststring = trim(implode(" ", $searchlist));
311 3a973ba4 znerol
			} else {
312
				$searchliststring = "";
313 61e047a5 Phil Davis
			}
314 532a1a0e Reid Linnemann
			$domain = config_get_path('system/domain');
315 e26ad76e luckman212
			if (!empty($dhcpv6ifconf['domain'])) {
316 b63b534c Christian McDonald
				/*
317 96270d7c Viktor G
				 * Lifetime SHOULD by default be at least 3 * MaxRtrAdvInterval
318 b63b534c Christian McDonald
				 * see https://redmine.pfsense.org/issues/12173
319 96270d7c Viktor G
				 */
320
				$radvdconf .= "\tDNSSL {$dhcpv6ifconf['domain']} {$searchliststring} {\n";
321
				$radvdconf .= "\t\tAdvDNSSLLifetime {$raadvdnsslifetime};\n";
322
				$radvdconf .= "\t};\n";
323 532a1a0e Reid Linnemann
			} elseif (!empty($domain)) {
324
				$radvdconf .= "\tDNSSL {$domain} {$searchliststring} {\n";
325 96270d7c Viktor G
				$radvdconf .= "\t\tAdvDNSSLLifetime {$raadvdnsslifetime};\n";
326
				$radvdconf .= "\t};\n";
327 1794ecbb jim-p
			}
328
		}
329 3f9cc8e4 smos
		$radvdconf .= "};\n";
330 ed395640 Seth Mos
	}
331
332 3f9cc8e4 smos
	/* handle DHCP-PD prefixes and 6RD dynamic interfaces */
333 ed395640 Seth Mos
	foreach ($Iflist as $if => $ifdescr) {
334 532a1a0e Reid Linnemann
		if (empty(config_get_path("interfaces/{$if}/track6-interface")) ||
335
		    config_get_path("interfaces/{$if}/ipaddrv6") != 'track6') {
336 ed395640 Seth Mos
			continue;
337 61e047a5 Phil Davis
		}
338 532a1a0e Reid Linnemann
		if (!config_path_enabled("interfaces/{$if}")) {
339 d7d2dc52 smos
			continue;
340 61e047a5 Phil Davis
		}
341 532a1a0e Reid Linnemann
		if (config_get_path("dhcpdv6/{$if}/ramode") == "disabled") {
342 8e187daa Chris Buechler
			continue;
343
		}
344 92977616 Ermal
		/* Do not put in the config an interface which is down */
345 61e047a5 Phil Davis
		if (isset($blacklist[$if])) {
346 92977616 Ermal
			continue;
347 61e047a5 Phil Davis
		}
348 532a1a0e Reid Linnemann
		$trackif = config_get_path("interfaces/{$if}/track6-interface");
349
		if (empty(config_get_path("interfaces/{$trackif}"))) {
350 60c05056 Ermal
			continue;
351 61e047a5 Phil Davis
		}
352 60c05056 Ermal
353 2a5960b0 Luiz Otavio O Souza
		$realif = get_real_interface($if, "inet6");
354 61e047a5 Phil Davis
355 3f9cc8e4 smos
		/* prevent duplicate entries, manual overrides */
356 61e047a5 Phil Davis
		if (isset($radvdifs[$realif])) {
357 7492f21d smos
			continue;
358 61e047a5 Phil Davis
		}
359 7492f21d smos
360 ed395640 Seth Mos
		$ifcfgipv6 = get_interface_ipv6($if);
361 61e047a5 Phil Davis
		if (!is_ipaddrv6($ifcfgipv6)) {
362 75aec77a Ermal
			$subnetv6 = "::";
363
			$ifcfgsnv6 = "64";
364
		} else {
365
			$ifcfgsnv6 = get_interface_subnetv6($if);
366
			$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
367
		}
368 60c05056 Ermal
		$radvdifs[$realif] = $realif;
369 c18a10cc smos
370 532a1a0e Reid Linnemann
		$autotype = config_get_path("interfaces/{$trackif}/ipaddrv6");
371 61e047a5 Phil Davis
372 2568e151 Christian McDonald
		if (g_get('debug')) {
373 dfac167c Ermal
			log_error("configuring RA on {$if} for type {$autotype} radvd subnet {$subnetv6}/{$ifcfgsnv6}");
374 61e047a5 Phil Davis
		}
375 668e8961 smos
376 60c05056 Ermal
		$radvdconf .= "# Generated config for {$autotype} delegation from {$trackif} on {$if}\n";
377
		$radvdconf .= "interface {$realif} {\n";
378
		$radvdconf .= "\tAdvSendAdvert on;\n";
379 c3099b79 Chris Buechler
		if (is_numericint($dhcpv6ifconf['raminrtradvinterval'])) {
380
			$radvdconf .= "\tMinRtrAdvInterval {$dhcpv6ifconf['raminrtradvinterval']};\n";
381
		} else {
382 a1eef308 jim-p
			$radvdconf .= "\tMinRtrAdvInterval 200;\n";
383 c3099b79 Chris Buechler
                }
384
		if (is_numericint($dhcpv6ifconf['ramaxrtradvinterval'])) {
385
			$radvdconf .= "\tMaxRtrAdvInterval {$dhcpv6ifconf['ramaxrtradvinterval']};\n";
386 99dfecb7 Renato Botelho do Couto
			$raadvdnsslifetime = $dhcpv6ifconf['ramaxrtradvinterval'] * 3;
387 c3099b79 Chris Buechler
		} else {
388 a1eef308 jim-p
			$radvdconf .= "\tMaxRtrAdvInterval 600;\n";
389 7628b091 jim-p
			$raadvdnsslifetime = 1800;
390 c3099b79 Chris Buechler
		}
391 60c05056 Ermal
		$mtu = get_interface_mtu($realif);
392 61e047a5 Phil Davis
		if (is_numeric($mtu)) {
393 60c05056 Ermal
			$radvdconf .= "\tAdvLinkMTU {$mtu};\n";
394 61e047a5 Phil Davis
		} else {
395 60c05056 Ermal
			$radvdconf .= "\tAdvLinkMTU 1280;\n";
396 61e047a5 Phil Davis
		}
397 60c05056 Ermal
		$radvdconf .= "\tAdvOtherConfigFlag on;\n";
398 f8f8d131 Chris Buechler
		$radvdconf .= "\tprefix {$subnetv6}/{$ifcfgsnv6} {\n";
399 60c05056 Ermal
		$radvdconf .= "\t\tAdvOnLink on;\n";
400
		$radvdconf .= "\t\tAdvAutonomous on;\n";
401
		$radvdconf .= "\t};\n";
402
403
		/* add DNS servers */
404 09646aef luckman212
		if ($dhcpv6ifconf['radvd-dns'] != 'disabled') {
405 e26ad76e luckman212
			$dnslist = array();
406 532a1a0e Reid Linnemann
			if (config_path_enabled('dnsmasq') || config_path_enabled('unbound')) {
407 e26ad76e luckman212
				$dnslist[] = $ifcfgipv6;
408 532a1a0e Reid Linnemann
			} else {
409
				foreach (config_get_path('system/dnsserver', []) as $server) {
410 e26ad76e luckman212
					if (is_ipaddrv6($server)) {
411
						$dnslist[] = $server;
412
					}
413 61e047a5 Phil Davis
				}
414 60c05056 Ermal
			}
415 e26ad76e luckman212
			if (count($dnslist) > 0) {
416
				$dnsstring = implode(" ", $dnslist);
417
				if (!empty($dnsstring)) {
418
					$radvdconf .= "\tRDNSS {$dnsstring} { };\n";
419
				}
420
			}
421 532a1a0e Reid Linnemann
			$domain = config_get_path('system/domain');
422
			if (!empty($domain)) {
423
				$radvdconf .= "\tDNSSL {$domain} {\n";
424 96270d7c Viktor G
				$radvdconf .= "\t\tAdvDNSSLLifetime {$raadvdnsslifetime};\n";
425
				$radvdconf .= "\t};\n";
426 61e047a5 Phil Davis
			}
427 60c05056 Ermal
		}
428
		$radvdconf .= "};\n";
429 668e8961 smos
	}
430
431 928d4416 Ermal
	/* write radvd.conf */
432 abdd01f5 Ermal
	if (!@file_put_contents("{$g['varetc_path']}/radvd.conf", $radvdconf)) {
433 e8c516a0 Phil Davis
		log_error(gettext("Error: cannot open radvd.conf in services_radvd_configure()."));
434 816fef25 Marcos Mendoza
		if (is_platform_booting()) {
435 abdd01f5 Ermal
			printf("Error: cannot open radvd.conf in services_radvd_configure().\n");
436 61e047a5 Phil Davis
		}
437 abdd01f5 Ermal
	}
438 928d4416 Ermal
	unset($radvdconf);
439 d57293a4 Seth Mos
440 abdd01f5 Ermal
	if (count($radvdifs) > 0) {
441 61e047a5 Phil Davis
		if (isvalidpid("{$g['varrun_path']}/radvd.pid")) {
442 abdd01f5 Ermal
			sigkillbypid("{$g['varrun_path']}/radvd.pid", "HUP");
443 61e047a5 Phil Davis
		} else {
444 abdd01f5 Ermal
			mwexec("/usr/local/sbin/radvd -p {$g['varrun_path']}/radvd.pid -C {$g['varetc_path']}/radvd.conf -m syslog");
445 61e047a5 Phil Davis
		}
446 6afeb202 smos
	} else {
447
		/* we need to shut down the radvd cleanly, it will send out the prefix
448
		 * information with a lifetime of 0 to notify clients of a (possible) new prefix */
449 abdd01f5 Ermal
		if (isvalidpid("{$g['varrun_path']}/radvd.pid")) {
450 4864d7f6 Josh Soref
			log_error(gettext("Shutting down Router Advertisement daemon cleanly"));
451 abdd01f5 Ermal
			killbypid("{$g['varrun_path']}/radvd.pid");
452 dfac167c Ermal
			@unlink("{$g['varrun_path']}/radvd.pid");
453 6afeb202 smos
		}
454 d57293a4 Seth Mos
	}
455
	return 0;
456
}
457
458 a4cd7de1 Christian McDonald
function kea_is_hexstring(string $string): bool {
459 d1b4e731 R. Christian McDonald
    $string = trim($string);
460
461
    /* kea accepts hex strings starting with 0x */
462
    if (str_starts_with($string, '0x')) {
463
        return ctype_xdigit(substr($string, 2));
464
    }
465
466
    /* ... and also bytes separated by colon or space */
467
    foreach ([':', ' '] as $sep) {
468
        if (str_contains($string, $sep)) {
469
            foreach (explode($sep, $string) as $piece) {
470
                if ((strlen($piece) > 2) || !ctype_xdigit($piece)) {
471
                    return false;
472
                }
473
            }
474
475
	    /* looks okay */
476
            return true;
477
         }
478
    }
479
480
    /* not a valid kea hex string */
481
    return false;
482
}
483
484 a4cd7de1 Christian McDonald
function services_kea6_configure() {
485 9bd56e9d Christian McDonald
	$kea_var_run = g_get('varrun_path') . '/kea';
486
	$kea_var_lib = '/var/lib/kea';
487
	$kea_var_db = '/var/db/kea';
488
489
	if (g_get('services_dhcp_server_enable') == false) {
490
		return;
491
	}
492
493
	if (config_path_enabled('system','developerspew')) {
494
		$mt = microtime();
495
		echo "services_kea6_configure() being called $mt\n";
496
	}
497
498
	/* kill any running dhcpleases6 */
499
	$pid_file = g_get('varrun_path') . '/dhcpleases6.pid';
500
	if (isvalidpid($pid_file)) {
501
		killbypid($pid_file);
502
	}
503
504
	/* DHCP enabled on any interfaces? */
505
	if (!is_dhcpv6_server_enabled()) {
506
		return 0;
507
	}
508
509
	/* bail if not Kea backend */
510
	if (!dhcp_is_backend('kea')) {
511
		return 0;
512
	}
513
514
	foreach ([$kea_var_run, $kea_var_lib, $kea_var_db] as $path) {
515
		if (!file_exists($path)) {
516
			mkdir($path, 0777, true);
517
		}
518
	}
519
520
	$syscfg = config_get_path('system');
521 63d6bb4f Marcos Mendoza
	config_init_path('dhcpdv6');
522 9bd56e9d Christian McDonald
	$dhcpdv6cfg = config_get_path('dhcpdv6');
523
	$Iflist = get_configured_interface_list();
524
	$Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces());
525
526 816fef25 Marcos Mendoza
	if (is_platform_booting()) {
527 9bd56e9d Christian McDonald
		echo "Starting Kea DHCPv6 service...";
528
	}
529
530
	/* configuration is built as a PHP array and converted to json */
531
	$keaconf = [];
532
	$keaconf['Dhcp6'] = [
533
		'interfaces-config' => [
534
			'interfaces' => []
535
		],
536
		'lease-database' => [
537
			'type' => 'memfile',
538
			'persist' => true,
539
			'name' => '/var/lib/kea/dhcp6.leases'
540
		],
541
		'loggers' => [[
542
			'name' => 'kea-dhcp6',
543
			'output_options' => [[
544
				'output' => 'syslog'
545
			]],
546
			'severity' => 'INFO'
547
		]],
548
		'valid-lifetime' => 7200,
549
		'max-valid-lifetime' => 86400,
550
		'hooks-libraries' => [],
551
		'control-socket' => [
552
			'socket-type' => 'unix',
553 a4cd7de1 Christian McDonald
			'socket-name' => '/var/run/kea6-ctrl-socket'
554 9bd56e9d Christian McDonald
		],
555
	];
556
557 f774120b R. Christian McDonald
	/* See https://redmine.pfsense.org/issues/15328 */
558
	$keaconf['Dhcp6']['sanity-checks'] = [
559
		'lease-checks' => 'fix-del'
560
	];
561
562 a4cd7de1 Christian McDonald
	/* required for HA support */
563
	$keaconf['Dhcp6']['hooks-libraries'][] = [
564 9bd56e9d Christian McDonald
		'library' => '/usr/local/lib/kea/hooks/libdhcp_lease_cmds.so',
565
	];
566
567 a4cd7de1 Christian McDonald
	if (config_path_enabled('kea6/ha')) {
568
		$scheme = (config_path_enabled('kea6/ha', 'tls') ? 'https://' : 'http://');
569
570
		/* local side */
571
		$local_role = config_get_path('kea6/ha/role', 'primary');
572
		$local_name = config_get_path('kea6/ha/localname', kea_defaults('name'));
573
		$local_address = config_get_path('kea6/ha/localip');
574
		if (is_ipaddrv6($local_address)) {
575
			$local_address = '[' . $local_address . ']';
576
		}
577
		$local_port = config_get_path('kea6/ha/localport', kea_defaults('listenport') + 1);
578
579
		$local_conf = [
580
			'name' => $local_name,
581
			'role' => $local_role,
582
			'url' => "{$scheme}{$local_address}:{$local_port}/",
583
			'auto-failover' => true
584
		];
585
586
		/* remote side */
587
		$remote_role = ($local_role === 'primary') ? 'standby' : 'primary';
588
		$remote_name = config_get_path('kea6/ha/remotename', kea_defaults('name'));
589
		$remote_address = config_get_path('kea6/ha/remoteip');
590
		if (is_ipaddrv6($remote_address)) {
591
			$remote_address = '[' . $remote_address . ']';
592
		}
593
		$remote_port = config_get_path('kea6/ha/remoteport', kea_defaults('listenport') + 1);
594
595
		$remote_conf = [
596
			'name' => $remote_name,
597
			'role' => $remote_role,
598
			'url' => "{$scheme}{$remote_address}:{$remote_port}/",
599
			'auto-failover' => true,
600
		];
601
602
		$ha_conf = [
603
			'this-server-name' => $local_name,
604
			'restrict-commands' => true,
605
			'mode' => 'hot-standby',
606
			'peers' => []
607
		];
608
609
		if (config_path_enabled('kea6/ha', 'tls')) {
610
			$cert = lookup_cert(config_get_path('kea6/ha/scertref'));
611
			$ca = ca_chain($cert['item']);
612
613
			$ha_conf['trust-anchor'] = '/usr/local/etc/kea/ha6_ca.pem';
614
			file_put_contents($ha_conf['trust-anchor'], $ca);
615
			chmod($ha_conf['trust-anchor'], 0644);
616
617
			$ha_conf['cert-file'] = '/usr/local/etc/kea/ha6_scert.crt';
618
			file_put_contents($ha_conf['cert-file'], base64_decode($cert['item']['crt']));
619
			chmod($ha_conf['cert-file'], 0644);
620
621
			$ha_conf['key-file'] = '/usr/local/etc/kea/ha6_scert.key';
622
			file_put_contents($ha_conf['key-file'], base64_decode($cert['item']['prv']));
623
			chmod($ha_conf['key-file'], 0600);
624
625
			$ha_conf['require-client-certs'] = false;
626
627
			if (config_path_enabled('kea6/ha', 'mutualtls')) {
628
				$cert = lookup_cert(config_get_path('kea6/ha/ccertref'));
629
630
				$remote_conf['cert-file'] = '/usr/local/etc/kea/ha6_ccert.crt';
631
				file_put_contents($remote_conf['cert-file'], base64_decode($cert['item']['crt']));
632
				chmod($remote_conf['cert-file'], 0644);
633
634
				$remote_conf['key-file'] = '/usr/local/etc/kea/ha6_ccert.key';
635
				file_put_contents($remote_conf['key-file'], base64_decode($cert['item']['prv']));
636
				chmod($remote_conf['key-file'], 0600);
637
638
				$ha_conf['require-client-certs'] = true;
639
			}
640
		}
641
642
		$ha_conf['peers'] = [
643
			$local_conf,
644
			$remote_conf
645
		];
646
647
		$ha_conf['heartbeat-delay'] = (int)config_get_path('kea6/ha/heartbeatdelay', kea_defaults('heartbeatdelay'));
648
		$ha_conf['max-response-delay'] = (int)config_get_path('kea6/ha/maxresponsedelay', kea_defaults('maxresponsedelay'));
649
		$ha_conf['max-ack-delay'] = (int)config_get_path('kea6/ha/maxackdelay', kea_defaults('maxackdelay'));
650
		$ha_conf['max-unacked-clients'] = (int)config_get_path('kea6/ha/maxunackedclients', kea_defaults('maxunackedclients'));
651
		$ha_conf['max-rejected-lease-updates'] = (int)config_get_path('kea6/ha/maxrejectedleaseupdates', kea_defaults('maxrejectedleaseupdates'));
652
653
		$kea_ha_hook = [
654
			'library' => '/usr/local/lib/kea/hooks/libdhcp_ha.so',
655
			'parameters' => [
656
				'high-availability' => [$ha_conf]
657
			]
658
		];
659
660
		$keaconf['Dhcp6']['hooks-libraries'][] = $kea_ha_hook;
661
	}
662 9bd56e9d Christian McDonald
663
	$dhcpdv6ifs = array();
664
665
	// $dhcpv6num = 0;
666
667
	$known_duids = [];
668
669
	$keasubnet_id = 1;
670
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
671
		if (empty($dhcpv6ifconf)) {
672
			continue;
673
		}
674
675
		$realif = get_real_interface($dhcpv6if, 'inet6');
676
677
		$ddns_zones = array();
678
679
		$ifcfgv6 = config_get_path("interfaces/{$dhcpv6if}");
680
681
		if (!isset($dhcpv6ifconf['enable']) || !isset($Iflist[$dhcpv6if]) ||
682
		    (!isset($ifcfgv6['enable']) && !preg_match("/poes/", $dhcpv6if))) {
683
			continue;
684
		}
685
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
686
		if (!is_ipaddrv6($ifcfgipv6) && !preg_match("/poes/", $dhcpv6if)) {
687
			continue;
688
		}
689
690
		$keaconf['Dhcp6']['interfaces-config']['interfaces'][] = $realif;
691
692
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
693
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
694
		$pdlen = 64;
695
696
		$keasubnet = [];
697
		$keasubnet['id'] = $keasubnet_id++;
698
		$keasubnet['interface'] = $realif;
699
		$keasubnet['subnet'] = $subnetv6 . '/' . $ifcfgsnv6;
700
701
		$all_pools = [];
702
		$all_pools[] = $dhcpv6ifconf;
703
		if (is_array($dhcpv6ifconf['pool'])) {
704
			$all_pools = array_merge($all_pools, $dhcpv6ifconf['pool']);
705
		}
706
707
		/* kea6 subnet options */
708
709
		// kea6 subnet default-lease-time
710
		if ($dhcpv6ifconf['defaultleasetime']) {
711
			$keasubnet['valid-lifetime'] = (int) $dhcpv6ifconf['defaultleasetime'];
712
		}
713
714
		// kea6 subnet max-lease-time
715
		if ($dhcpv6ifconf['maxleasetime']) {
716
			$keasubnet['max-valid-lifetime'] = (int) $dhcpv6ifconf['maxleasetime'];
717
		}
718
719
		/* kea6 subnet domain-search */
720
		$searchlist = [];
721
		if ($dhcpv6ifconf['domain']) {
722
			$searchlist[] = $dhcpv6ifconf['domain'];
723
		} else {
724
			$searchlist[] = $syscfg['domain'];
725
		}
726
727
		if ($dhcpv6ifconf['domainsearchlist'] <> "") {
728
			$searchlist = array_merge($searchlist, array_map('trim', explode(';', $dhcpv6ifconf['domainsearchlist'])));
729
		}
730
731
		if (!empty($searchlist)) {
732
			$keasubnet['option-data'][] = [
733
				'name' => 'domain-search',
734
				'data' => implode(', ', $searchlist)
735
			];
736
		}
737
738
		/* kea6 subnet dns-server */
739
		$dnslist = [];
740
		if ($dhcpv6ifconf['dhcp6c-dns'] != 'disabled') {
741
			if (is_array($dhcpv6ifconf['dnsserver']) && ($dhcpv6ifconf['dnsserver'][0])) {
742
				$dnslist = $dhcpv6ifconf['dnsserver'];
743
			} elseif (((config_path_enabled('dnsmasq')) || config_path_enabled('unbound')) && is_ipaddrv6($ifcfgipv6)) {
744
				$dnslist = [$ifcfgipv6];
745
			} elseif (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
746
				$dns_arrv6 = array();
747
				foreach ($syscfg['dnsserver'] as $dnsserver) {
748
					if (is_ipaddrv6($dnsserver)) {
749
						if ($ifcfgv6['ipaddrv6'] == 'track6' &&
750
						    Net_IPv6::isInNetmask($dnsserver, '::', $pdlen)) {
751
							$dnsserver = merge_ipv6_delegated_prefix($ifcfgipv6, $dnsserver, $pdlen);
752
						}
753
						$dns_arrv6[] = $dnsserver;
754
					}
755
				}
756
				if (!empty($dns_arrv6)) {
757
					$dnslist = $dns_arrv6;
758
				}
759
			}
760
		}
761
762
		if (!empty($dnslist)) {
763
			$keasubnet['option-data'][] = [
764
				'name' => 'dns-servers',
765
				'data' => implode(', ', $dnslist)
766
			];
767
		}
768
769
		/* kea6 subnet ntp-servers */
770
		if (is_array($dhcpv6ifconf['ntpserver']) && $dhcpv6ifconf['ntpserver'][0]) {
771
			$ntpservers = array();
772
			foreach ($dhcpv6ifconf['ntpserver'] as $ntpserver) {
773
				if (!is_ipaddrv6($ntpserver)) {
774
					continue;
775
				}
776
				if ($ifcfgv6['ipaddrv6'] == 'track6' &&
777
				    Net_IPv6::isInNetmask($ntpserver, '::', $pdlen)) {
778
					$ntpserver = merge_ipv6_delegated_prefix($ifcfgipv6, $ntpserver, $pdlen);
779
				}
780
				$ntpservers[] = $ntpserver;
781
			}
782
			if (count($ntpservers) > 0) {
783
				$keasubnet['option-data'][] = [
784
					'name' => 'sntp-servers',
785
					'data' => implode(', ', $ntpservers)
786
				];
787
			}
788
		}
789
790
791
		/* kea6 subnet netboot */
792
		if (isset($dhcpv6ifconf['netboot'])) {
793
			if (!empty($dhcpv6ifconf['bootfile_url'])) {
794
				$keasubnet['option-data'][] = [
795
					'name' => 'bootfile-url',
796
					'data' => $dhcpv6ifconf['bootfile_url']
797
				];
798
			}
799
		}
800
801
		/* the first pool is the primary subnet pool, we handle it a bit differently */
802
		$first_pool = true;
803
		foreach ($all_pools as $all_pools_idx => $poolconf) {
804
			$keapool = [];
805
806
			$range_from = $poolconf['range']['from'];
807
			$range_to = $poolconf['range']['to'];
808
			if ($ifcfgv6['ipaddrv6'] == 'track6') {
809
				$range_from = merge_ipv6_delegated_prefix($ifcfgipv6, $range_from, $pdlen);
810
				$range_to = merge_ipv6_delegated_prefix($ifcfgipv6, $range_to, $pdlen);
811
			}
812
813
			if (is_ipaddrv6($ifcfgipv6)) {
814
				$subnet_start = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
815
				$subnet_end = gen_subnetv6_max($ifcfgipv6, $ifcfgsnv6);
816
				if ((!is_inrange_v6($range_from, $subnet_start, $subnet_end)) ||
817
				    (!is_inrange_v6($range_to, $subnet_start, $subnet_end))) {
818
					log_error(gettext("The specified range lies outside of the current subnet. Skipping DHCP6 entry."));
819
					continue;
820
				}
821
			}
822
823
			if (!is_ipaddrv6($ifcfgipv6)) {
824
				$ifcfgsnv6 = "64";
825
				$subnetv6 = gen_subnetv6($range_from, $ifcfgsnv6);
826
			}
827
828
			if (!empty($range_from) && !empty($range_to)) {
829
				$keapool['pool'] = $range_from . ' - ' . $range_to;
830
			}
831
832
			$keapool['client-class'] = 'pool_' . $dhcpv6if . '_'. $all_pools_idx;
833
834
			$class_exprs = [];
835
836
			$fetch_global_reservations = false;
837
			if (isset($poolconf['denyunknown'])) {
838
				if ($poolconf['denyunknown'] == "class") {
839
					$class_exprs[] = 'member(\'KNOWN\')';
840
				} elseif ($poolconf['denyunknown'] != "disabled") {
841
					/** "catch-all" covering "enabled" value post-PR#4066, and covering non-upgraded
842
					 * boolean option (i.e. literal value "enabled"). The condition is a safeguard in
843
					 * case the engine ever changes such that: isset("disabled") == true.
844
					 */
845
					$class_exprs[] = 'member(\'KNOWN\')';
846
					$fetch_global_reservations = true;
847
				}
848
			}
849
850
			/* default allow all (e.g. member('ALL')) */
851
			$pool_client_class_test = 'member(\'ALL\')';
852
			if (!empty($class_exprs)) {
853
				$pool_client_class_test = implode(' and ', $class_exprs);
854
			}
855
856
			/* the primary pool inherits options from the subnet, so skip over pool-specific options */
857
			if ($first_pool) {
858
				$first_pool = false;
859
				goto kea6_skip_first_pool_options;
860
			}
861
862
			/* kea6 pool options */
863
864
			/* kea6 pool domain-search */
865
			$searchlist = [];
866
			if ($poolconf['domain']) {
867
				$searchlist[] = $poolconf['domain'];
868
			}
869
870
			if ($poolconf['domainsearchlist']) {
871
				$searchlist = array_merge($searchlist, array_map('trim', explode(';', $poolconf['domainsearchlist'])));
872
			}
873
874
			if (!empty($searchlist)) {
875
				$keapool['option-data'][] = [
876
					'name' => 'domain-search',
877
					'data' => implode(', ', $searchlist)
878
				];
879
			}
880
881
			/* kea6 pool dns-server */
882
			$dnslist = [];
883
			if ($dhcpv6ifconf['dhcp6c-dns'] != 'disabled') {
884
				if (is_array($poolconf['dnsserver']) && ($poolconf['dnsserver'][0])) {
885
					$dnslist = $poolconf['dnsserver'];
886
				}
887
			}
888
889
			if (!empty($dnslist)) {
890
				$keapool['option-data'][] = [
891
					'name' => 'dns-servers',
892
					'data' => implode(', ', $dnslist)
893
				];
894
			}
895
896
			// kea6 pool ntp-servers
897
			if (is_array($poolconf['ntpserver']) && $poolconf['ntpserver'][0]) {
898
				$ntpservers = array();
899
				foreach ($poolconf['ntpserver'] as $ntpserver) {
900
					if (!is_ipaddrv6($ntpserver)) {
901
						continue;
902
					}
903
					if ($ifcfgv6['ipaddrv6'] == 'track6' &&
904
					    Net_IPv6::isInNetmask($ntpserver, '::', $pdlen)) {
905
						$ntpserver = merge_ipv6_delegated_prefix($ifcfgipv6, $ntpserver, $pdlen);
906
					}
907
					$ntpservers[] = $ntpserver;
908
				}
909
				if (count($ntpservers) > 0) {
910
					$keapool['option-data'][] = [
911
						'name' => 'sntp-servers',
912
						'data' => implode(', ', $ntpservers)
913
					];
914
				}
915
			}
916
917
			/* kea6 pool netboot */
918
			if (isset($poolconf['netboot'])) {
919
				if (!empty($poolconf['bootfile_url'])) {
920
					$keapool['option-data'][] = [
921
						'name' => 'bootfile-url',
922
						'data' => $poolconf['bootfile_url']
923
					];
924
				}
925
			}
926
927
kea6_skip_first_pool_options:
928
			$keaconf['Dhcp6']['client-classes'][] = [
929
				'name' => $keapool['client-class'],
930
				'test' => $pool_client_class_test
931
			];
932
933
			$keasubnet['pools'][] = $keapool;
934
		}
935
936
		/* add static mappings */
937
		/* Needs to use DUID */
938
		if (is_array($dhcpv6ifconf['staticmap'])) {
939
			$i = 0;
940
			foreach ($dhcpv6ifconf['staticmap'] as $sm) {
941
				if (empty($sm)) {
942
					continue;
943
				}
944
945
				$keares = [];
946
				$keares['duid'] = $sm['duid'];
947
948
				$known_duids[$sm['duid']] = true;
949
950
				if ($sm['ipaddrv6']) {
951
					$ipaddrv6 = $sm['ipaddrv6'];
952
					if ($ifcfgv6['ipaddrv6'] == 'track6') {
953
						$ipaddrv6 = merge_ipv6_delegated_prefix($ifcfgipv6, $ipaddrv6, $pdlen);
954
					}
955
					$keares['ip-addresses'][] = $ipaddrv6;
956
				}
957
958
				if ($sm['hostname']) {
959
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
960
					$dhhostname = str_replace(".", "_", $dhhostname);
961
					$keares['hostname'] = $dhhostname;
962
				}
963
964
				$keasubnet['reservations'][] = $keares;
965
				$i++;
966
			}
967
		}
968
969
970
		if ($dhcpv6ifconf['ddnsdomain']) {
971
			$dhcpdv6conf .= dhcpdkey($dhcpv6ifconf);
972
			$dhcpdv6conf .= dhcpdzones($ddns_zones);
973
		}
974
975
		if ((config_get_path("dhcpdv6/{$dhcpv6if}/ramode") != "unmanaged") &&
976
		    (config_path_enabled("interfaces/{$dhcpv6if}") ||
977
		    preg_match("/poes/", $dhcpv6if))) {
978
			if (preg_match("/poes/si", $dhcpv6if)) {
979
				/* magic here */
980
				$dhcpdv6ifs = array_merge($dhcpdv6ifs, get_pppoes_child_interfaces($dhcpv6if));
981
			} else {
982
				$realif = get_real_interface($dhcpv6if, "inet6");
983
				if (stristr("$realif", "bridge")) {
984
					$mac = get_interface_mac($realif);
985
					$v6address = generate_ipv6_from_mac($mac);
986
					/* Create link local address for bridges */
987
					mwexec("/sbin/ifconfig {$realif} inet6 {$v6address}");
988
				}
989
				$realif = escapeshellcmd($realif);
990
				$dhcpdv6ifs[] = $realif;
991
			}
992
		}
993
994
		if ($fetch_global_reservations) {
995
			$keasubnet['reservations-global'] = true;
996
		}
997
		$keasubnet['reservations-in-subnet'] = true;
998
999
		$keaconf['Dhcp6']['subnet6'][] = $keasubnet;
1000
	}
1001
1002
	$known_duids = array_keys($known_duids);
1003
	foreach ($known_duids as $duid) {
1004
		$keaconf['Dhcp6']['reservations'][] = [
1005
			'duid' => $duid
1006
		];
1007
	}
1008
1009
	$keaconf_path = '/usr/local/etc/kea/kea-dhcp6.conf';
1010
1011
	/* render kea-dhcp6.conf json */
1012
	if (($keaconf = json_encode($keaconf, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)) === false) {
1013 816fef25 Marcos Mendoza
		log_error(sprintf(gettext('error: cannot render json for %s in %s'), $keaconf_path, __FUNCTION__));
1014 9bd56e9d Christian McDonald
		return 1;
1015
	}
1016
1017
	/* write kea-dhcp6.conf */
1018
	if (!file_put_contents($keaconf_path, $keaconf)) {
1019 816fef25 Marcos Mendoza
		log_error(sprintf(gettext('error: cannot write %s in %s()'), $keaconf_path, __FUNCTION__));
1020 9bd56e9d Christian McDonald
		return 1;
1021
	}
1022
1023
	/* create an empty leases database */
1024
	$kea_lease_db = $kea_var_lib . '/dhcp6.leases';
1025
	if (!file_exists($kea_lease_db)) {
1026
		touch($kea_lease_db);
1027
	}
1028
1029
	$kea_bin = '/usr/local/sbin/kea-dhcp6';
1030
	mwexec_bg(sprintf('%s -c %s', $kea_bin, $keaconf_path));
1031
1032 816fef25 Marcos Mendoza
	if (is_platform_booting()) {
1033 9bd56e9d Christian McDonald
		print gettext('done') . ".\n";
1034
	}
1035
1036
	return 0;
1037
}
1038
1039 a4cd7de1 Christian McDonald
function services_dhcpd_kill_all($family = 'all') {
1040 9bd56e9d Christian McDonald
	$dhcpd_var_run = g_get('dhcpd_chroot_path') . g_get('varrun_path');
1041
	$kea_var_run = g_get('varrun_path') . '/kea';
1042
1043 dc96586b jim-p
	$pids = [];
1044
	$pids4 = [
1045 9bd56e9d Christian McDonald
		$dhcpd_var_run . '/dhcpd.pid',
1046
		$kea_var_run . '/kea-dhcp4.kea-dhcp4.pid',
1047 dc96586b jim-p
	];
1048
	$pids6 = [
1049
		$dhcpd_var_run . '/dhcpdv6.pid',
1050 9bd56e9d Christian McDonald
		$kea_var_run . '/kea-dhcp6.kea-dhcp6.pid'
1051
	];
1052
1053 dc96586b jim-p
	if (($family === 'all') || ($family === 'inet')) {
1054
		$pids = array_merge($pids, $pids4);
1055
	}
1056
	if (($family === 'all') || ($family === 'inet6')) {
1057
		$pids = array_merge($pids, $pids6);
1058
	}
1059
1060 9bd56e9d Christian McDonald
	foreach ($pids as $pid) {
1061
		if (isvalidpid($pid)) {
1062
			killbypid($pid);
1063
			unlink_if_exists($pid);
1064
		}
1065
	}
1066
}
1067
1068 a4cd7de1 Christian McDonald
function services_dhcpd_configure($family = "all") {
1069 532a1a0e Reid Linnemann
	global $g;
1070 2fb056d8 Seth Mos
1071 9bd56e9d Christian McDonald
	/* block if dhcpd is already being configured */
1072
	$dhcpdconfigurelck = lock('dhcpdconfigure', LOCK_EX);
1073
1074 dc96586b jim-p
	services_dhcpd_kill_all($family);
1075 9bd56e9d Christian McDonald
1076
	if (dhcp_is_backend('isc')) {
1077
		$fd = fopen("{$g['tmp_path']}/dhcpd.sh", "w");
1078
		fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}\n");
1079
		fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/dev\n");
1080
		fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/etc\n");
1081
		fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/var/db\n");
1082
		fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/var/run\n");
1083
		fwrite($fd, "/usr/sbin/chown -R dhcpd:_dhcp {$g['dhcpd_chroot_path']}/*\n");
1084
1085
		/* only mount devfs if not already mounted */
1086
		fwrite($fd, "/bin/df {$g['dhcpd_chroot_path']}/dev | /usr/bin/grep -q {$g['dhcpd_chroot_path']}/dev || /sbin/mount -t devfs devfs {$g['dhcpd_chroot_path']}/dev\n");
1087
1088
		fclose($fd);
1089
		mwexec("/bin/sh {$g['tmp_path']}/dhcpd.sh");
1090
	}
1091
1092
	if (($family === 'all') || ($family === 'inet')) {
1093
		switch (dhcp_get_backend()) {
1094
		case 'kea':
1095
			services_kea4_configure();
1096
			break;
1097
		case 'isc':
1098
		default:
1099
			services_dhcpdv4_configure();
1100
			break;
1101
		}
1102
	}
1103
1104
	if (($family === 'all') || ($family === 'inet6')) {
1105
		switch (dhcp_get_backend()) {
1106
		case 'kea':
1107
			services_kea6_configure();
1108
			break;
1109
		case 'isc':
1110
		default:
1111
			services_dhcpdv6_configure();
1112
			break;
1113
		}
1114
		services_radvd_configure();
1115
	}
1116
1117
	unlock($dhcpdconfigurelck);
1118
}
1119
1120 a4cd7de1 Christian McDonald
function services_kea4_configure() {
1121 9bd56e9d Christian McDonald
	$need_ddns_updates = false;
1122
	$ddns_zones = array();
1123
1124
	$kea_var_run = g_get('varrun_path') . '/kea';
1125
	$kea_var_lib = '/var/lib/kea';
1126
1127
	if (g_get('services_dhcp_server_enable') == false) {
1128
		return;
1129
	}
1130
1131
	if (config_path_enabled('system','developerspew')) {
1132
		$mt = microtime();
1133
		echo "services_kea4_configure() being called $mt\n";
1134
	}
1135
1136
	/* DHCP enabled on any interfaces? */
1137
	if (!is_dhcp_server_enabled()) {
1138
		return 0;
1139
	}
1140
1141
	/* bail if not Kea backend */
1142
	if (!dhcp_is_backend('kea')) {
1143
		return 0;
1144
	}
1145
1146
	/* ensure we have a valid /var/run/kea directory */
1147
	if (!file_exists($kea_var_run)) {
1148
		mkdir($kea_var_run, 0777, true);
1149
	}
1150
1151
	/* ensure we have a valid /var/lib/kea directory */
1152
	if (!file_exists($kea_var_lib)) {
1153
		mkdir($kea_var_lib, 0777, true);
1154
	}
1155
1156
	$syscfg = config_get_path('system');
1157 63d6bb4f Marcos Mendoza
	config_init_path('dhcpd');
1158 9bd56e9d Christian McDonald
	$dhcpdcfg = config_get_path('dhcpd');
1159
	$Iflist = get_configured_interface_list();
1160
1161
	/* configuration is built as a PHP array and converted to json */
1162
	$keaconf = [];
1163
	$keaconf['Dhcp4'] = [
1164
		'interfaces-config' => [
1165
			'interfaces' => []
1166
		],
1167
		'lease-database' => [
1168
			'type' => 'memfile',
1169
			'persist' => true,
1170
			'name' => $kea_var_lib . '/dhcp4.leases'
1171
		],
1172
		'loggers' => [[
1173
			'name' => 'kea-dhcp4',
1174
			'output_options' => [[
1175
				'output' => 'syslog'
1176
			]],
1177
			'severity' => 'INFO'
1178
		]],
1179
		'valid-lifetime' => 7200,
1180
		'max-valid-lifetime' => 86400,
1181
		'ip-reservations-unique' => false,
1182 38e308db R. Christian McDonald
		'echo-client-id' => false, /* RFC6842 compatibility mode */
1183 9bd56e9d Christian McDonald
		'option-data' => [[
1184
			'name' => 'domain-name',
1185
			'data' => $syscfg['domain'],
1186
		]],
1187
		'option-def' => [[
1188
			'space' => 'dhcp4',
1189
			'name' => 'ldap-server',
1190
			'code' => 95,
1191
			'type' => 'string'
1192
		]],
1193
		'hooks-libraries' => [],
1194
		'control-socket' => [
1195
			'socket-type' => 'unix',
1196 a4cd7de1 Christian McDonald
			'socket-name' => '/var/run/kea4-ctrl-socket'
1197 9bd56e9d Christian McDonald
		],
1198
	];
1199
1200 f774120b R. Christian McDonald
	/* See https://redmine.pfsense.org/issues/15328 */
1201
	$keaconf['Dhcp4']['sanity-checks'] = [
1202
		'lease-checks' => 'fix-del'
1203
	];
1204
1205 a4cd7de1 Christian McDonald
	/* required for HA support */
1206
	$keaconf['Dhcp4']['hooks-libraries'][] = [
1207 9bd56e9d Christian McDonald
		'library' => '/usr/local/lib/kea/hooks/libdhcp_lease_cmds.so',
1208
	];
1209
1210 a4cd7de1 Christian McDonald
	if (config_path_enabled('kea/ha')) {
1211
		$scheme = (config_path_enabled('kea/ha', 'tls') ? 'https://' : 'http://');
1212
1213
		/* local side */
1214
		$local_role = config_get_path('kea/ha/role', 'primary');
1215
		$local_name = config_get_path('kea/ha/localname', kea_defaults('name'));
1216
		$local_address = config_get_path('kea/ha/localip');
1217
		if (is_ipaddrv6($local_address)) {
1218
			$local_address = '[' . $local_address . ']';
1219
		}
1220
		$local_port = config_get_path('kea/ha/localport', kea_defaults('listenport'));
1221
1222
		$local_conf = [
1223
			'name' => $local_name,
1224
			'role' => $local_role,
1225
			'url' => "{$scheme}{$local_address}:{$local_port}/",
1226
			'auto-failover' => true
1227
		];
1228
1229
		/* remote side */
1230
		$remote_role = ($local_role === 'primary') ? 'standby' : 'primary';
1231
		$remote_name = config_get_path('kea/ha/remotename', kea_defaults('name'));
1232
		$remote_address = config_get_path('kea/ha/remoteip');
1233
		if (is_ipaddrv6($remote_address)) {
1234
			$remote_address = '[' . $remote_address . ']';
1235
		}
1236
		$remote_port = config_get_path('kea/ha/remoteport', kea_defaults('listenport'));
1237
1238
		$remote_conf = [
1239
			'name' => $remote_name,
1240
			'role' => $remote_role,
1241
			'url' => "{$scheme}{$remote_address}:{$remote_port}/",
1242
			'auto-failover' => true,
1243
		];
1244
1245
		$ha_conf = [
1246
			'this-server-name' => $local_name,
1247
			'restrict-commands' => true,
1248
			'mode' => 'hot-standby',
1249
			'peers' => []
1250
		];
1251
1252
		if (config_path_enabled('kea/ha', 'tls')) {
1253
			$cert = lookup_cert(config_get_path('kea/ha/scertref'));
1254
			$ca = ca_chain($cert['item']);
1255
1256
			$ha_conf['trust-anchor'] = '/usr/local/etc/kea/ha_ca.pem';
1257
			file_put_contents($ha_conf['trust-anchor'], $ca);
1258
			chmod($ha_conf['trust-anchor'], 0644);
1259
1260
			$ha_conf['cert-file'] = '/usr/local/etc/kea/ha_scert.crt';
1261
			file_put_contents($ha_conf['cert-file'], base64_decode($cert['item']['crt']));
1262
			chmod($ha_conf['cert-file'], 0644);
1263
1264
			$ha_conf['key-file'] = '/usr/local/etc/kea/ha_scert.key';
1265
			file_put_contents($ha_conf['key-file'], base64_decode($cert['item']['prv']));
1266
			chmod($ha_conf['key-file'], 0600);
1267
1268
			$ha_conf['require-client-certs'] = false;
1269
1270
			if (config_path_enabled('kea/ha', 'mutualtls')) {
1271
				$cert = lookup_cert(config_get_path('kea/ha/ccertref'));
1272
1273
				$remote_conf['cert-file'] = '/usr/local/etc/kea/ha_ccert.crt';
1274
				file_put_contents($remote_conf['cert-file'], base64_decode($cert['item']['crt']));
1275
				chmod($remote_conf['cert-file'], 0644);
1276
1277
				$remote_conf['key-file'] = '/usr/local/etc/kea/ha_ccert.key';
1278
				file_put_contents($remote_conf['key-file'], base64_decode($cert['item']['prv']));
1279
				chmod($remote_conf['key-file'], 0600);
1280
1281
				$ha_conf['require-client-certs'] = true;
1282
			}
1283
		}
1284
1285
		$ha_conf['peers'] = [
1286
			$local_conf,
1287
			$remote_conf
1288
		];
1289
1290
		$ha_conf['heartbeat-delay'] = (int)config_get_path('kea/ha/heartbeatdelay', kea_defaults('heartbeatdelay'));
1291
		$ha_conf['max-response-delay'] = (int)config_get_path('kea/ha/maxresponsedelay', kea_defaults('maxresponsedelay'));
1292
		$ha_conf['max-ack-delay'] = (int)config_get_path('kea/ha/maxackdelay', kea_defaults('maxackdelay'));
1293
		$ha_conf['max-unacked-clients'] = (int)config_get_path('kea/ha/maxunackedclients', kea_defaults('maxunackedclients'));
1294
		$ha_conf['max-rejected-lease-updates'] = (int)config_get_path('kea/ha/maxrejectedleaseupdates', kea_defaults('maxrejectedleaseupdates'));
1295
1296
		$kea_ha_hook = [
1297
			'library' => '/usr/local/lib/kea/hooks/libdhcp_ha.so',
1298
			'parameters' => [
1299
				'high-availability' => [$ha_conf]
1300
			]
1301
		];
1302
1303
		$keaconf['Dhcp4']['hooks-libraries'][] = $kea_ha_hook;
1304
	}
1305 9bd56e9d Christian McDonald
1306
	/* Only consider DNS servers with IPv4 addresses for the IPv4 DHCP server. */
1307
	$dns_arrv4 = array();
1308
	if (is_array($syscfg['dnsserver'])) {
1309
		foreach ($syscfg['dnsserver'] as $dnsserver) {
1310
			if (is_ipaddrv4($dnsserver)) {
1311
				$dns_arrv4[] = $dnsserver;
1312
			}
1313
		}
1314
	}
1315
1316 816fef25 Marcos Mendoza
	if (is_platform_booting()) {
1317 9bd56e9d Christian McDonald
		echo gettext('Starting Kea DHCP service...');
1318
	}
1319
1320
	/* take these settings from the first DHCP configured interface,
1321
	 * see https://redmine.pfsense.org/issues/10270
1322
	 * TODO: Global Settings tab, see https://redmine.pfsense.org/issues/5080 */
1323
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
1324
		if (empty($dhcpifconf)) {
1325
			continue;
1326
		}
1327
1328
		if (!isset($dhcpifconf['disableauthoritative'])) {
1329
			$keaconf['Dhcp4']['authoritative'] = true;
1330
		}
1331
1332
		break;
1333
	}
1334
1335
	$enable_add_routers = false;
1336 4bbbcc36 Marcos Mendoza
	$gateways_arr = get_gateways();
1337 9bd56e9d Christian McDonald
	/* only add a routers line if the system has any IPv4 gateway at all */
1338
	/* a static route has a gateway, manually overriding this field always works */
1339
	foreach ($gateways_arr as $gwitem) {
1340
		if ($gwitem['ipprotocol'] == "inet") {
1341
			$enable_add_routers = true;
1342
			break;
1343
		}
1344
	}
1345
1346
	/* store known MACs and CIDs as we encounter them during the walk */
1347
	$known_macs = [];
1348
	$known_cids = [];
1349
1350
	$keasubnet_id = 1; /* kea subnet id must start at 1 */
1351
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
1352
		if (empty($dhcpifconf)) {
1353
			continue;
1354
		}
1355
1356
		$newzone = array();
1357
		$ifcfg = config_get_path("interfaces/{$dhcpif}");
1358
1359
		if (!isset($dhcpifconf['enable']) || !isset($Iflist[$dhcpif])) {
1360
			continue;
1361
		}
1362
1363
		$keasubnet = [];
1364
		$keasubnet['id'] = $keasubnet_id++;
1365
1366
		$ifcfgip = get_interface_ip($dhcpif);
1367
		$ifcfgsn = get_interface_subnet($dhcpif);
1368
		$subnet = gen_subnet($ifcfgip, $ifcfgsn);
1369
		// $subnetmask = gen_subnet_mask($ifcfgsn);
1370
1371
		if (!is_ipaddr($subnet)) {
1372
			continue;
1373
		}
1374
1375
		$keasubnet['subnet'] = $subnet . '/' . $ifcfgsn;
1376
1377
		$all_pools = array();
1378
		$all_pools[] = $dhcpifconf;
1379
		if (is_array($dhcpifconf['pool'])) {
1380
			$all_pools = array_merge($all_pools, $dhcpifconf['pool']);
1381
		}
1382
1383
		if ($dhcpifconf['domain']) {
1384
			$keasubnet['option-data'][] = [
1385
				'name' => 'domain-name',
1386
				'data' => $dhcpifconf['domain']
1387
			];
1388
		}
1389
1390
		if ($dhcpifconf['domainsearchlist'] <> "") {
1391
			$keasubnet['option-data'][] = [
1392
				'name' => 'domain-search',
1393
				'data' => implode(', ', array_map('trim', explode(';', $dhcpifconf['domainsearchlist'])))
1394
			];
1395
		}
1396
1397
		if (is_array($dhcpifconf['dnsserver']) && ($dhcpifconf['dnsserver'][0])) {
1398
			$keasubnet['option-data'][] = [
1399
				'name' => 'domain-name-servers',
1400
				'data' => implode(', ', $dhcpifconf['dnsserver'])
1401
			];
1402
			if ($newzone['domain-name']) {
1403
				$newzone['dns-servers'] = $dhcpifconf['dnsserver'];
1404
			}
1405
		} elseif (config_path_enabled('dnsmasq')) {
1406
			$keasubnet['option-data'][] = [
1407
				'name' => 'domain-name-servers',
1408
				'data' => $ifcfgip
1409
			];
1410
			if ($newzone['domain-name'] && is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
1411
				$newzone['dns-servers'] = $syscfg['dnsserver'];
1412
			}
1413
		} elseif (config_path_enabled('unbound')) {
1414
			$keasubnet['option-data'][] = [
1415
				'name' => 'domain-name-servers',
1416
				'data' => $ifcfgip
1417
			];
1418
		} elseif (!empty($dns_arrv4)) {
1419
			$keasubnet['option-data'][] = [
1420
				'name' => 'domain-name-servers',
1421
				'data' => implode(', ', $dns_arrv4)
1422
			];
1423
			if ($newzone['domain-name']) {
1424
				$newzone['dns-servers'] = $dns_arrv4;
1425
			}
1426
		}
1427
1428
		/* Create classes - These all contain comma separated lists. Join them into one
1429
		   big comma separated string then split them all up. */
1430
		$all_mac_strings = array();
1431
		if (is_array($dhcpifconf['pool'])) {
1432
			foreach ($all_pools as $poolconf) {
1433
				$all_mac_strings[] = $poolconf['mac_allow'];
1434
				$all_mac_strings[] = $poolconf['mac_deny'];
1435
			}
1436
		}
1437
1438
		$all_mac_strings[] = $dhcpifconf['mac_allow'];
1439
		$all_mac_strings[] = $dhcpifconf['mac_deny'];
1440
		if (!empty($all_mac_strings)) {
1441
			$all_mac_list = array_unique(explode(',', implode(',', $all_mac_strings)));
1442
			foreach ($all_mac_list as $mac) {
1443
				if (empty($mac)) {
1444
					continue;
1445
				}
1446
1447
				/* create client classes (mac_0123456789ab) for MAC address matching */
1448
				$umacstr = strtoupper(str_replace(':', '', $mac));
1449
				$lmacstr = strtolower($umacstr);
1450
				$lmacstr_len = strlen($lmacstr);
1451
				$test_string = 'substring(hexstring(pkt4.mac, \'\'), 0, %s) == \'%s\'';
1452
				$keaconf['Dhcp4']['client-classes'][] = [
1453
					'name' => 'mac_'.$umacstr,
1454
					'test' => sprintf($test_string, $lmacstr_len, $lmacstr)
1455
				];
1456
			}
1457
		}
1458
1459
		// Setup pool options
1460
		foreach ($all_pools as $all_pools_idx => $poolconf) {
1461
			$keapool = [];
1462
1463
			if (!(ip_in_subnet($poolconf['range']['from'], "{$subnet}/{$ifcfgsn}") && ip_in_subnet($poolconf['range']['to'], "{$subnet}/{$ifcfgsn}"))) {
1464
				// If the user has changed the subnet from the interfaces page and applied,
1465
				// but has not updated the DHCP range, then the range to/from of the pool can be outside the subnet.
1466
				// This can also happen when implementing the batch of changes when the setup wizard reloads the new settings.
1467
				$error_msg = sprintf(gettext('Invalid DHCP pool %1$s - %2$s for %3$s subnet %4$s/%5$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);
1468
				$do_file_notice = true;
1469
				$conf_ipv4_address = $ifcfg['ipaddr'];
1470
				$conf_ipv4_subnetmask = $ifcfg['subnet'];
1471
				if (is_ipaddrv4($conf_ipv4_address) && is_subnet("{$conf_ipv4_address}/{$conf_ipv4_subnetmask}")) {
1472
					$conf_subnet_base = gen_subnet($conf_ipv4_address, $conf_ipv4_subnetmask);
1473
					if (ip_in_subnet($poolconf['range']['from'], "{$conf_subnet_base}/{$conf_ipv4_subnetmask}") &&
1474
					    ip_in_subnet($poolconf['range']['to'], "{$conf_subnet_base}/{$conf_ipv4_subnetmask}")) {
1475
						// Even though the running interface subnet does not match the pool range,
1476
						// the interface subnet in the config file contains the pool range.
1477
						// We are somewhere part-way through a settings reload, e.g. after running the setup wizard.
1478
						// services_dhcpdv4_configure will be called again later when the new interface settings from
1479
						// the config are applied and at that time everything will match up.
1480
						// Ignore this pool on this interface for now and just log the error to the system log.
1481
						log_error($error_msg);
1482
						$do_file_notice = false;
1483
					}
1484
				}
1485
				if ($do_file_notice) {
1486
					file_notice("DHCP", $error_msg);
1487
				}
1488
				continue;
1489
			}
1490
1491
			$keapool['pool'] = $poolconf['range']['from'] . ' - '. $poolconf['range']['to'];
1492
			$keapool['client-class'] = 'pool_' . $dhcpif . '_'. $all_pools_idx;
1493
1494
			$class_exprs = $mac_exprs = [];
1495
1496
			/* mac_allow processing */
1497
			$mac_allow_list = array_unique(explode(',', $poolconf['mac_allow']));
1498
			$mac_allow_exprs = [];
1499
			foreach ($mac_allow_list as $mac) {
1500
				if (!empty($mac)) {
1501
					$class_name = 'mac_' . strtoupper(str_replace(':', '', $mac));
1502
					$mac_allow_exprs[] = sprintf('member(\'%s\')', $class_name);
1503
				}
1504
			}
1505
			if (!empty($mac_allow_exprs)) {
1506
				$mac_exprs[] = '(' . implode(' or ', $mac_allow_exprs) . ')';
1507
			}
1508
1509
			/* mac_deny processing */
1510
			$mac_deny_list = array_unique(explode(',', $poolconf['mac_deny']));
1511
			$mac_deny_exprs = [];
1512
			foreach ($mac_deny_list as $mac) {
1513
				if (!empty($mac)) {
1514
					$class_name = 'mac_' . strtoupper(str_replace(':', '', $mac));
1515
					$mac_deny_exprs[] = sprintf('not member(\'%s\')', $class_name);
1516
				}
1517
			}
1518
			if (!empty($mac_deny_exprs)) {
1519
				$mac_exprs[] = '(' . implode(' and ', $mac_deny_exprs) . ')';
1520
			}
1521
1522
			/* do we have a useful class test? */
1523
			if (!empty($mac_exprs)) {
1524
				$class_exprs[] = '(' . implode(' and ', $mac_exprs) . ')';
1525
			}
1526
1527
			// set pool MAC limitations
1528
			if (isset($poolconf['denyunknown'])) {
1529
				if ($poolconf['denyunknown'] == "class") {
1530
					$class_exprs[] = 'member(\'KNOWN\')';
1531
				} elseif ($poolconf['denyunknown'] != "disabled") {
1532
					/** "catch-all" covering "enabled" value post-PR#4066, and covering non-upgraded
1533
					 * boolean option (i.e. literal value "enabled"). The condition is a safeguard in
1534
					 * case the engine ever changes such that: isset("disabled") == true.
1535
					 */
1536
					$class_exprs[] = 'member(\'KNOWN\')';
1537
					$keasubnet['reservations-global'] = true;
1538
				}
1539
			}
1540
1541
			/* default allow all (e.g. member('ALL')) */
1542
			$pool_client_class_test = 'member(\'ALL\')';
1543
			if (!empty($class_exprs)) {
1544
				$pool_client_class_test = implode(' and ', $class_exprs);
1545
			}
1546
1547
			if (is_array($poolconf['dnsserver']) && $poolconf['dnsserver'][0] <> "") {
1548
				$keapool['option-data'][] = [
1549
					'name' => 'domain-name-servers',
1550
					'data' => implode(', ', $poolconf['dnsserver'])
1551
				];
1552
			}
1553
1554
			if ($poolconf['gateway'] && $poolconf['gateway'] != "none" && ($poolconf['gateway'] != $dhcpifconf['gateway'])) {
1555
				$keapool['option-data'][] = [
1556
					'name' => 'routers',
1557
					'data' => $poolconf['gateway']
1558
				];
1559
			}
1560
1561
			if ($poolconf['domain'] && ($poolconf['domain'] != $dhcpifconf['domain'])) {
1562
				$keapool['option-data'][] = [
1563
					'name' => 'domain-name',
1564
					'data' => $poolconf['domain']
1565
				];
1566
			}
1567
1568
			if (!empty($poolconf['domainsearchlist']) && ($poolconf['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
1569
				$keapool['option-data'][] = [
1570
					'name' => 'domain-search',
1571
					'data' => implode(', ', array_map('trim', explode(';', $poolconf['domainsearchlist'])))
1572
				];
1573
			}
1574
1575
			// default-lease-time
1576
			if ($poolconf['defaultleasetime'] && ($poolconf['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) {
1577
				$keapool['valid-lifetime'] = $poolconf['defaultleasetime'];
1578
			}
1579
1580
			// max-lease-time
1581
			if ($poolconf['maxleasetime'] && ($poolconf['maxleasetime'] != $dhcpifconf['maxleasetime'])) {
1582
				$keapool['max-valid-lifetime'] = $poolconf['maxleasetime'];
1583
			}
1584
1585
			// ignore-client-uids
1586
			if (isset($poolconf['ignoreclientuids'])) {
1587
				$keasubnet['match-client-id'] = false;
1588
			}
1589
1590
			// netbios-name*
1591
			if (is_array($poolconf['winsserver']) && $poolconf['winsserver'][0] && ($poolconf['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
1592
				$keapool['option-data'][] = [
1593
					'name' => 'netbios-name-servers',
1594 faf9f096 jim-p
					'data' => implode(', ', $poolconf['winsserver'])
1595 9bd56e9d Christian McDonald
				];
1596
				$keapool['option-data'][] = [
1597
					'name' => 'netbios-node-type',
1598
					'data' => '8'
1599
				];
1600
			}
1601
1602
			// ntp-servers
1603
			if (is_array($poolconf['ntpserver']) && $poolconf['ntpserver'][0] && ($poolconf['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
1604
				$keapool['option-data'][] = [
1605
					'name' => 'ntp-servers',
1606 216df8ac jim-p
					'data' => implode(', ', array_filter($poolconf['ntpserver'], 'is_ipaddrv4'))
1607 9bd56e9d Christian McDonald
				];
1608
			}
1609
1610
			// tftp-server-name
1611
			if (!empty($poolconf['tftp'])) {
1612
				$keapool['option-data'][] = [
1613
					'name' => 'tftp-server-name',
1614
					'data' => $poolconf['tftp']
1615
				];
1616
			}
1617
1618
			// Handle pool-specific options
1619
			// Ignore the first pool, which is the "overall" pool when $all_pools_idx is 0 - those are put outside the pool block later
1620
			$idx = 0;
1621
			$httpclient = false;
1622
			if (isset($poolconf['numberoptions']['item']) && is_array($poolconf['numberoptions']['item']) && ($all_pools_idx > 0)) {
1623
				// Use the "real" pool index from the config, excluding the "overall" pool, and based from 0.
1624
				// This matches the way $poolidx was used when generating the $custoptions string earlier.
1625
				// $poolidx = $all_pools_idx - 1;
1626
				foreach ($poolconf['numberoptions']['item'] as $itemidx => $item) {
1627
					/*
1628
					$item_value = base64_decode($item['value']);
1629
					if (empty($item['type']) || $item['type'] == "text") {
1630
						$dhcpdconf .= "		option custom-{$dhcpif}-{$poolidx}-{$itemidx} \"{$item_value}\";\n";
1631
					} else {
1632
						$dhcpdconf .= "		option custom-{$dhcpif}-{$poolidx}-{$itemidx} {$item_value};\n";
1633
					}
1634
					*/
1635
					if (($item['type'] == "text") &&
1636
					    ($item['number'] == 60) &&
1637
					    (base64_decode($item['value']) == "HTTPClient")) {
1638
						$httpclient = true;
1639
					}
1640
					$idx++;
1641
				}
1642
			}
1643
			if (!empty($poolconf['uefihttpboot']) && isset($poolconf['netboot']) && !$httpclient &&
1644
			    (!isset($dhcpifconf['uefihttpboot']) ||
1645
			    ($poolconf['uefihttpboot'] != $dhcpifconf['uefihttpboot']))) {
1646
			//	$dhcpdconf .= "		option custom-{$dhcpif}-{$poolidx}-{$idx} \"HTTPClient\";\n";
1647
			}
1648
1649
			// ldap-server
1650
			if (!empty($poolconf['ldap'])) {
1651
				$keapool['option-data'][] = [
1652
					'name' => 'ldap-server',
1653
					'data' => $poolconf['ldap']
1654
				];
1655
			}
1656
1657
			// net boot information
1658
			if (isset($poolconf['netboot'])) {
1659
				$archs = [];
1660
				$pxe_files = array();
1661
				if (!empty($poolconf['uefihttpboot'])) {
1662
					$archs[] = [
1663
						'name' => 'uefihttp',
1664
						'test' => 'substring(option[60].text, 0, 10) == \'HTTPClient\'',
1665
						'filename' => $poolconf['uefihttpboot'],
1666
					];
1667
				}
1668
				if (!empty($poolconf['filename32'])) {
1669
					$archs[] = [
1670
						'name' => '32',
1671
						'test' => 'option[93].hex == 0x0006',
1672
						'filename' => $poolconf['filename32']
1673
					];
1674
				}
1675
				if (!empty($poolconf['filename64'])) {
1676
					$archs[] = [
1677
						'name' => '64',
1678
						'test' => 'option[93].hex == 0x0007 or option[93].hex == 0x0009',
1679
						'filename' => $poolconf['filename64']
1680
					];
1681
				}
1682
				if (!empty($poolconf['filename32arm'])) {
1683
					$archs[] = [
1684
						'name' => '32arm',
1685
						'test' => 'option[93].hex == 0x000a',
1686
						'filename' => $poolconf['filename32arm']
1687
					];
1688
				}
1689
				if (!empty($poolconf['filename64arm'])) {
1690
					$archs[] = [
1691
						'name' => '64arm',
1692
						'test' => 'option[93].hex == 0x000b',
1693
						'filename' => $poolconf['filename64arm']
1694
					];
1695
				}
1696
1697
				foreach ($archs as $arch) {
1698
					$name = implode('_', ['ipxe', $arch['name'], $dhcpif, 'pool', $all_pools_idx]);
1699
					$keaconf['Dhcp4']['client-classes'][] = [
1700
						'name' => $name,
1701
						'test' => $arch['test'],
1702
						'only-if-required' => true,
1703
						'option-data' => [[
1704
							'name' => 'boot-file-name',
1705
							'data' => $arch['filename']
1706
						]]
1707
					];
1708
					$keapool['require-client-classes'][] = $name;
1709
				}
1710
1711
				if (!empty($poolconf['filename'])) {
1712
					$legacy_test = 'member(\'ALL\')';
1713
					if (!empty($archs)) {
1714
						$legacy_exprs = [];
1715
						foreach ($archs as $arch) {
1716
							$name = implode('_', ['ipxe', $arch['name'], $dhcpif, 'pool', $all_pools_idx]);
1717
							$legacy_exprs[] = sprintf('not member(\'%s\')', $name);
1718
						}
1719
						$legacy_test = implode(' and ', $legacy_exprs);
1720
					}
1721
					$name = implode('_', ['ipxe', 'legacy', $dhcpif, 'pool', $all_pools_idx]);
1722
					$keaconf['Dhcp4']['client-classes'][] = [
1723
						'name' => $name,
1724
						'test' => $legacy_test,
1725
						'only-if-required' => true,
1726
						'option-data' => [[
1727
							'name' => 'boot-file-name',
1728 f3ec053b R. Christian McDonald
							'data' => $poolconf['filename']
1729 9bd56e9d Christian McDonald
						]]
1730
					];
1731
					if (!is_array($keapool['require-client-classes'])) {
1732
						$keapool['require-client-classes'] = [];
1733
					}
1734 d027f903 jim-p
					$keapool['require-client-classes'][] = $name;
1735 9bd56e9d Christian McDonald
				}
1736
1737
				if (!empty($poolconf['rootpath'])) {
1738
					$keapool['option-data'][] = [
1739
						'name' => 'root-path',
1740
						'data' => $poolconf['rootpath']
1741
					];
1742
				}
1743
			}
1744
1745
			$keaconf['Dhcp4']['client-classes'][] = [
1746
				'name' => $keapool['client-class'],
1747
				'test' => $pool_client_class_test
1748
			];
1749
1750
			$keasubnet['pools'][] = $keapool;
1751
1752
		}
1753
// End of settings inside pools
1754
1755
		if ($dhcpifconf['gateway'] && $dhcpifconf['gateway'] != "none") {
1756
			$routers = $dhcpifconf['gateway'];
1757
			$add_routers = true;
1758
		} elseif ($dhcpifconf['gateway'] == "none") {
1759
			$add_routers = false;
1760
		} else {
1761
			$add_routers = $enable_add_routers;
1762
			$routers = $ifcfgip;
1763
		}
1764
		if ($add_routers) {
1765
			$keasubnet['option-data'][] = [
1766
				'name' => 'routers',
1767
				'data' => $routers,
1768
			];
1769
		}
1770
1771
		// default-lease-time
1772
		if ($dhcpifconf['defaultleasetime']) {
1773
			$keasubnet['valid-lifetime'] = (int)$dhcpifconf['defaultleasetime'];
1774
		}
1775
1776
		// max-lease-time
1777
		if ($dhcpifconf['maxleasetime']) {
1778
			$keasubnet['max-valid-lifetime'] = (int)$dhcpifconf['maxleasetime'];
1779
		}
1780
1781
		// netbios-name*
1782
		if (is_array($dhcpifconf['winsserver']) && $dhcpifconf['winsserver'][0]) {
1783
			$keasubnet['option-data'][] = [
1784
				'name' => 'netbios-name-servers',
1785
				'data' => implode(', ', $dhcpifconf['winsserver'])
1786
			];
1787
			$keasubnet['option-data'][] = [
1788
				'name' => 'netbios-node-type',
1789
				'data' => '8'
1790
			];
1791
		}
1792
1793
		// ntp-servers
1794
		if (is_array($dhcpifconf['ntpserver']) && $dhcpifconf['ntpserver'][0]) {
1795
			$keasubnet['option-data'][] = [
1796
				'name' => 'ntp-servers',
1797 216df8ac jim-p
				'data' => implode(', ', array_filter($dhcpifconf['ntpserver'], 'is_ipaddrv4'))
1798 9bd56e9d Christian McDonald
			];
1799
		}
1800
1801
		// Handle option, number rowhelper values
1802
		//$dhcpdconf .= "\n";
1803
		$idx = 0;
1804
		$httpclient = false;
1805
		if (isset($dhcpifconf['numberoptions']['item']) && is_array($dhcpifconf['numberoptions']['item'])) {
1806
			foreach ($dhcpifconf['numberoptions']['item'] as $itemidx => $item) {
1807
				/*
1808
				$item_value = base64_decode($item['value']);
1809
				if (empty($item['type']) || $item['type'] == "text") {
1810
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} \"{$item_value}\";\n";
1811
				} else {
1812
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} {$item_value};\n";
1813
				}
1814
				*/
1815
				if (($item['type'] == "text") &&
1816
				    ($item['number'] == 60) &&
1817
				    (base64_decode($item['value']) == "HTTPClient")) {
1818
					$httpclient = true;
1819
				}
1820
				$idx++;
1821
			}
1822
		}
1823
1824
		// net boot information
1825
		if (isset($dhcpifconf['netboot'])) {
1826
			if ($dhcpifconf['nextserver'] <> "") {
1827
				$keasubnet['next-server'] = $dhcpifconf['nextserver'];
1828
			}
1829
1830
			$archs = [];
1831
			$pxe_files = array();
1832
			if (!empty($dhcpifconf['uefihttpboot'])) {
1833
				$archs[] = [
1834
					'name' => 'uefihttp',
1835
					'test' => 'substring(option[60].text, 0, 10) == \'HTTPClient\'',
1836
					'filename' => $dhcpifconf['uefihttpboot'],
1837
				];
1838
			}
1839
			if (!empty($dhcpifconf['filename32'])) {
1840
				$archs[] = [
1841
					'name' => '32',
1842
					'test' => 'option[93].hex == 0x0006',
1843
					'filename' => $dhcpifconf['filename32']
1844
				];
1845
			}
1846
			if (!empty($dhcpifconf['filename64'])) {
1847
				$archs[] = [
1848
					'name' => '64',
1849
					'test' => 'option[93].hex == 0x0007 or option[93].hex == 0x0009',
1850
					'filename' => $dhcpifconf['filename64']
1851
				];
1852
			}
1853
			if (!empty($dhcpifconf['filename32arm'])) {
1854
				$archs[] = [
1855
					'name' => '32arm',
1856
					'test' => 'option[93].hex == 0x000a',
1857
					'filename' => $dhcpifconf['filename32arm']
1858
				];
1859
			}
1860
			if (!empty($dhcpifconf['filename64arm'])) {
1861
				$archs[] = [
1862
					'name' => '64arm',
1863
					'test' => 'option[93].hex == 0x000b',
1864
					'filename' => $dhcpifconf['filename64arm']
1865
				];
1866
			}
1867
1868
			foreach ($archs as $arch) {
1869
				$name = implode('_', ['ipxe', $arch['name'], $dhcpif]);
1870
				$keaconf['Dhcp4']['client-classes'][] = [
1871
					'name' => $name,
1872
					'test' => $arch['test'],
1873
					'only-if-required' => true,
1874
					'option-data' => [[
1875
						'name' => 'boot-file-name',
1876
						'data' => $arch['filename']
1877
					]]
1878
				];
1879
				$keasubnet['require-client-classes'][] = $name;
1880
			}
1881
1882
			if (!empty($dhcpifconf['filename'])) {
1883
				$legacy_test = 'member(\'ALL\')';
1884
				if (!empty($archs)) {
1885
					$legacy_exprs = [];
1886
					foreach ($archs as $arch) {
1887
						$name = implode('_', ['ipxe', $arch['name'], $dhcpif]);
1888
						$legacy_exprs[] = sprintf('not member(\'%s\')', $name);
1889
					}
1890
					$legacy_test = implode(' and ', $legacy_exprs);
1891
				}
1892
				$name = implode('_', ['ipxe', 'legacy', $dhcpif]);
1893
				$keaconf['Dhcp4']['client-classes'][] = [
1894
					'name' => $name,
1895
					'test' => $legacy_test,
1896
					'only-if-required' => true,
1897
					'option-data' => [[
1898
						'name' => 'boot-file-name',
1899
						'data' => $dhcpifconf['filename']
1900
					]]
1901
				];
1902
				if (!is_array($keasubnet['require-client-classes'])) {
1903
					$keasubnet['require-client-classes'] = [];
1904
				}
1905 d027f903 jim-p
				$keasubnet['require-client-classes'][] = $name;
1906 9bd56e9d Christian McDonald
			}
1907
1908
			if (!empty($dhcpifconf['rootpath'])) {
1909
				$keasubnet['option-data'][] = [
1910
					'name' => 'root-path',
1911
					'data' => $dhcpifconf['rootpath']
1912
				];
1913
			}
1914
		}
1915
1916
		/* add static mappings */
1917
		if (is_array($dhcpifconf['staticmap'])) {
1918
			$i = 0;
1919
			$sm_newzone[] = array();
1920
			$need_sm_ddns_updates = false;
1921
			foreach ($dhcpifconf['staticmap'] as $sm) {
1922
				if (empty($sm)) {
1923
					continue;
1924
				}
1925
1926
				$keares = [];
1927
1928 3b2e7ed2 R. Christian McDonald
				$has_mac = false;
1929 d1b4e731 R. Christian McDonald
				$sm['mac'] = strtolower(trim($sm['mac']));
1930 9bd56e9d Christian McDonald
				if ($sm['mac']) {
1931 3b2e7ed2 R. Christian McDonald
					$has_mac = true;
1932 d1b4e731 R. Christian McDonald
					$keares['hw-address'] = $sm['mac'];
1933 9bd56e9d Christian McDonald
1934
					/* keys are unique */
1935 d1b4e731 R. Christian McDonald
					$known_macs[$sm['mac']] = true;
1936 9bd56e9d Christian McDonald
				}
1937
1938 d1b4e731 R. Christian McDonald
				$sm['cid'] = strtolower(trim($sm['cid']));
1939 9bd56e9d Christian McDonald
				if ($sm['cid']) {
1940 d1b4e731 R. Christian McDonald
					/* wrap in single quotes if not a valid kea hex string */
1941
					if (!kea_is_hexstring($sm['cid'])) {
1942
						$sm['cid'] = '\''.$sm['cid'].'\'';
1943
					}
1944
1945 3b2e7ed2 R. Christian McDonald
					if (!$has_mac) {
1946
						/* only set client-id if no mac address */
1947 d1b4e731 R. Christian McDonald
						$keares['client-id'] = $sm['cid'];
1948 3b2e7ed2 R. Christian McDonald
					}
1949 9bd56e9d Christian McDonald
1950
					/* keys are unique */
1951 d1b4e731 R. Christian McDonald
					$known_cids[$sm['cid']] = true;
1952 9bd56e9d Christian McDonald
				}
1953
1954
				if ($sm['ipaddr']) {
1955
					$keares['ip-address'] = $sm['ipaddr'];
1956
				}
1957
1958
				if ($sm['hostname']) {
1959
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
1960
					$dhhostname = str_replace(".", "_", $dhhostname);
1961
					$keares['hostname'] = $dhhostname;
1962
					$keagres['hostname'] = $dhhostname;
1963
					/*
1964
					if ((isset($dhcpifconf['ddnsupdate']) || isset($sm['ddnsupdate'])) && (isset($dhcpifconf['ddnsforcehostname']) || isset($sm['ddnsforcehostname']))) {
1965
						$dhcpdconf .= "	ddns-hostname \"{$dhhostname}\";\n";
1966
					}
1967
					*/
1968
				}
1969
				/*
1970
				if ($sm['filename']) {
1971
					$dhcpdconf .= "	filename \"{$sm['filename']}\";\n";
1972
				}
1973
1974
				if ($sm['rootpath']) {
1975
					$dhcpdconf .= "	option root-path \"{$sm['rootpath']}\";\n";
1976
				}
1977
				*/
1978
1979
				/* routers */
1980
				if ($sm['gateway'] && ($sm['gateway'] != $dhcpifconf['gateway'])) {
1981
					$keares['option-data'][] = [
1982
						'name' => 'routers',
1983
						'data' => $sm['gateway']
1984
					];
1985
				}
1986
1987
				/* domain-name */
1988
				if ($sm['domain'] && ($sm['domain'] != $dhcpifconf['domain'])) {
1989
					$keares['option-data'][] = [
1990
						'name' => 'domain-name',
1991
						'data' => $sm['domain']
1992
					];
1993
				}
1994
1995
				/* domain-search */
1996
				if (!empty($sm['domainsearchlist']) && ($sm['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
1997
					$keares['option-data'][] = [
1998
						'name' => 'domain-search',
1999
						'data' => implode(', ', array_map('trim', explode(';', $sm['domainsearchlist'])))
2000
					];
2001
				}
2002
2003
				/*
2004
				if (isset($sm['ddnsupdate'])) {
2005
					if (($sm['ddnsdomain'] <> "") && ($sm['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
2006
						$smdnscfg .= "	ddns-domainname \"{$sm['ddnsdomain']}\";\n";
2007
				 		$need_sm_ddns_updates = true;
2008
					}
2009
					$smdnscfg .= "	ddns-update-style interim;\n";
2010
				}
2011
				*/
2012
2013
				if (is_array($sm['dnsserver']) && ($sm['dnsserver'][0]) && ($sm['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
2014
					$keares['option-data'][] = [
2015
						'name' => 'domain-name-servers',
2016
						'data' => implode(', ', $sm['dnsserver'])
2017
					];
2018
				}
2019
2020
				// netbios-name*
2021
				if (is_array($sm['winsserver']) && $sm['winsserver'][0] && ($sm['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
2022
					$keares['option-data'][] = [
2023
						'name' => 'netbios-name-servers',
2024
						'data' => implode(', ', $sm['winsserver'])
2025
					];
2026
					$keares['option-data'][] = [
2027
						'name' => 'netbios-node-type',
2028
						'data' => '8'
2029
					];
2030
				}
2031
2032
				// ntp-servers
2033
				if (is_array($sm['ntpserver']) && $sm['ntpserver'][0] && ($sm['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
2034
					$keares['option-data'][] = [
2035
						'name' => 'ntp-servers',
2036 216df8ac jim-p
						'data' => implode(', ', array_filter($sm['ntpserver'], 'is_ipaddrv4'))
2037 9bd56e9d Christian McDonald
					];
2038
				}
2039
2040
				// tftp-server-name
2041
				if (!empty($sm['tftp']) && ($sm['tftp'] != $dhcpifconf['tftp'])) {
2042
					$keares['option-data'][] = [
2043
						'name' => 'tftp-server-name',
2044
						'data' => $sm['tftp']
2045
					];
2046
				}
2047
2048
				// Handle option, number rowhelper values
2049
				// $dhcpdconf .= "\n";
2050
				$idx = 0;
2051
				$httpclient = false;
2052
				if (isset($sm['numberoptions']['item']) && is_array($sm['numberoptions']['item'])) {
2053
					foreach ($sm['numberoptions']['item'] as $itemidx => $item) {
2054
						/*
2055
						$item_value = base64_decode($item['value']);
2056
						if (empty($item['type']) || $item['type'] == "text") {
2057
							$dhcpdconf .= "	option custom-s_{$dhcpif}_{$i}-{$itemidx} \"{$item_value}\";\n";
2058
						} else {
2059
							$dhcpdconf .= "	option custom-s_{$dhcpif}_{$i}-{$itemidx} {$item_value};\n";
2060
						}
2061
						*/
2062
					}
2063
					if (($item['type'] == "text") &&
2064
					    ($item['number'] == 60) &&
2065
					    (base64_decode($item['value']) == "HTTPClient")) {
2066
						$httpclient = true;
2067
					}
2068
					$idx++;
2069
				}
2070
				/*
2071
				if (!empty($sm['uefihttpboot']) && isset($sm['netboot']) && !$httpclient) {
2072
					$dhcpdconf .= "	option custom-s_{$dhcpif}_{$i}-{$idx} \"HTTPClient\";\n";
2073
				}
2074
				*/
2075
2076
				// ldap-server
2077
				if (!empty($sm['ldap']) && ($sm['ldap'] != $dhcpifconf['ldap'])) {
2078
					$keares['option-data'][] = [
2079
						'name' => 'ldap-server',
2080
						'data' => $sm['ldap']
2081
					];
2082
				}
2083
2084
				// net boot information
2085
				if (isset($sm['netboot'])) {
2086
2087
					$pxe_files = array();
2088
					if (!empty($sm['filename'])) {
2089
						$filename = $sm['filename'];
2090
					}
2091
					if (!empty($sm['uefihttpboot'])) {
2092
						$pxe_files[] = array('HTTPClient', $sm['uefihttpboot']);
2093
					}
2094
					if (!empty($sm['filename32'])) {
2095
						$pxe_files[] = array('00:06', $sm['filename32']);
2096
					}
2097
					if (!empty($sm['filename64'])) {
2098
						$pxe_files[] = array('00:07', $sm['filename64']);
2099
						$pxe_files[] = array('00:09', $sm['filename64']);
2100
					}
2101
					if (!empty($sm['filename32arm'])) {
2102
						$pxe_files[] = array('00:0a', $sm['filename32arm']);
2103
					}
2104
					if (!empty($sm['filename64arm'])) {
2105
						$pxe_files[] = array('00:0b', $sm['filename64arm']);
2106
					}
2107
2108
					$pxeif = false;
2109
					if (is_array($pxe_files) && !empty($pxe_files)) {
2110
						foreach ($pxe_files as $pxe) {
2111
							/*
2112
							if ($pxe[0] == 'HTTPClient') {
2113
								$expr = "substring (option vendor-class-identifier, 0, 10) = \"HTTPClient\" {\n";
2114
							} else {
2115
								$expr = "option arch = {$pxe[0]} {\n";
2116
							}
2117
							*/
2118
							if (!$pxeif) {
2119
								// $dhcpdconf .= "	if " . $expr;
2120
								$pxeif = true;
2121
							} /* else {
2122
								$dhcpdconf .= " elseif " . $expr;
2123
							}
2124
							$dhcpdconf .= "		filename \"{$pxe[1]}\";\n";
2125
							$dhcpdconf .= "	}";
2126
							*/
2127
						}
2128
						/*
2129
						if ($filename) {
2130
							$dhcpdconf .= " else {\n";
2131
							$dhcpdconf .= "		filename \"{$filename}\";\n";
2132
							$dhcpdconf .= "	}";
2133
						}
2134
						$dhcpdconf .= "\n\n";
2135
						*/
2136
					} /* elseif (!empty($filename)) {
2137
						$dhcpdconf .= "	filename \"{$filename}\";\n";
2138
					}
2139
					*/
2140
					unset($filename);
2141
					/*
2142
					if (!empty($dhcpifconf['rootpath'])) {
2143
						$dhcpdconf .= "	option root-path \"{$sm['rootpath']}\";\n";
2144
					}
2145
					*/
2146
				}
2147
2148
				// $dhcpdconf .= "}\n";
2149
2150
				// add zone DDNS key/server to $ddns_zone[] if required
2151
				if ($need_sm_ddns_updates) {
2152
					$ddnsduplicate = false;
2153
					foreach ($ddns_zones as $ddnszone) {
2154
						if ($ddnszone['domain-name'] == $sm['ddnsdomain']) {
2155
							$ddnsduplicate = true;
2156
						}
2157
					}
2158
					if (!$ddnsduplicate) {
2159
						$sm_newzone['dns-servers'] = array($sm['ddnsdomainprimary'], $sm['ddnsdomainsecondary']);
2160
						$sm_newzone['domain-name'] = $sm['ddnsdomain'];
2161
						$sm_newzone['ddnsdomainkeyname'] = $sm['ddnsdomainkeyname'];
2162
						$sm_newzone['ddnsdomainkeyalgorithm'] = $sm['ddnsdomainkeyalgorithm'];
2163
						$sm_newzone['ddnsdomainkey'] = $sm['ddnsdomainkey'];
2164
						// $dhcpdconf .= dhcpdkey($sm_newzone);
2165
						$ddns_zones[] = $sm_newzone;
2166
						$need_ddns_updates = true;
2167
					}
2168
				}
2169
2170
				/*
2171
				// subclass for DHCP limiting
2172
				if (!empty($sm['mac'])) {
2173
					// assuming ALL addresses are ethernet hardware type ("1:" prefix)
2174
					$dhcpdconf .= "subclass \"s_{$dhcpif}\" 1:{$sm['mac']};\n";
2175
				}
2176
				if (!empty($cid)) {
2177
					$dhcpdconf .= "subclass \"s_{$dhcpif}\" \"{$cid}\";\n";
2178
				}
2179
				*/
2180
2181
				$i++;
2182
2183
				$keasubnet['reservations'][] = $keares;
2184
			}
2185
		}
2186
2187
		$keasubnet['reservations-in-subnet'] = true;
2188
2189
		$keaconf['Dhcp4']['subnet4'][] = $keasubnet;
2190
		$keaconf['Dhcp4']['interfaces-config']['interfaces'][] = get_real_interface($dhcpif);
2191
		if ($newzone['domain-name']) {
2192
			if ($need_ddns_updates) {
2193
				$newzone['dns-servers'] = array($dhcpifconf['ddnsdomainprimary'], $dhcpifconf['ddnsdomainsecondary']);
2194
				$newzone['ddnsdomainkeyname'] = $dhcpifconf['ddnsdomainkeyname'];
2195
				$newzone['ddnsdomainkeyalgorithm'] = $dhcpifconf['ddnsdomainkeyalgorithm'];
2196
				$newzone['ddnsdomainkey'] = $dhcpifconf['ddnsdomainkey'];
2197
				// $dhcpdconf .= dhcpdkey($dhcpifconf);
2198
			}
2199
			$ddns_zones[] = $newzone;
2200
		}
2201
	}
2202 c69ea005 NewEraCracker
2203 9bd56e9d Christian McDonald
	/* add global reservations for known macs */
2204
	$known_macs = array_keys($known_macs);
2205
	foreach ($known_macs as $mac) {
2206
		$keaconf['Dhcp4']['reservations'][] = [
2207
			'hw-address' => $mac
2208
		];
2209
	}
2210 fd391b0c Christian McDonald
2211 9bd56e9d Christian McDonald
	/* add global reservations for known cids */
2212
	$known_cids = array_keys($known_cids);
2213
	foreach ($known_cids as $cid) {
2214
		$keaconf['Dhcp4']['reservations'][] = [
2215
			'client-id' => $cid
2216
		];
2217
	}
2218 fd391b0c Christian McDonald
2219 9bd56e9d Christian McDonald
	/* render json */
2220
	if (($keaconf = json_encode($keaconf, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)) === false) {
2221 816fef25 Marcos Mendoza
		log_error(sprintf(gettext('error: unable to render json for kea-dhcp4.conf in %s()'), __FUNCTION__));
2222 9bd56e9d Christian McDonald
		return 1;
2223
	}
2224 7ddc0080 Christian McDonald
2225 9bd56e9d Christian McDonald
	/* write kea-dhcp4.conf */
2226
	$keaconf_path = '/usr/local/etc/kea/kea-dhcp4.conf';
2227
	if (!file_put_contents($keaconf_path, $keaconf)) {
2228 816fef25 Marcos Mendoza
		log_error(sprintf(gettext('error: cannot open %s in %s()'), $keaconf_path, __FUNCTION__));
2229 9bd56e9d Christian McDonald
		return 1;
2230
	}
2231 2fb056d8 Seth Mos
2232 9bd56e9d Christian McDonald
	/* create an empty leases database */
2233
	$kea_lease_db = $kea_var_lib . '/dhcp4.leases';
2234
	if (!file_exists($kea_lease_db)) {
2235
		touch($kea_lease_db);
2236 61e047a5 Phil Davis
	}
2237 9bd56e9d Christian McDonald
2238
	/* start kea-dhcp4 */
2239
	$kea_bin = '/usr/local/sbin/kea-dhcp4';
2240
	mwexec_bg(sprintf('%s -c %s', $kea_bin, $keaconf_path));
2241
2242 816fef25 Marcos Mendoza
	if (is_platform_booting()) {
2243 9bd56e9d Christian McDonald
		print "done.\n";
2244 3084ba6e Ermal
	}
2245 c69ea005 NewEraCracker
2246 9bd56e9d Christian McDonald
	return 0;
2247 2fb056d8 Seth Mos
}
2248 1c903aa4 Ermal
2249 2fb056d8 Seth Mos
function services_dhcpdv4_configure() {
2250 532a1a0e Reid Linnemann
	global $g;
2251 3df2dbfd jim-p
	$need_ddns_updates = false;
2252
	$ddns_zones = array();
2253 107e8acc Ovidiu Predescu
2254 2568e151 Christian McDonald
	if (g_get('services_dhcp_server_enable') == false) {
2255 e3a13b00 Scott Ullrich
		return;
2256 61e047a5 Phil Davis
	}
2257 e3a13b00 Scott Ullrich
2258 532a1a0e Reid Linnemann
	if (config_path_enabled('system','developerspew')) {
2259 acd910bf Scott Ullrich
		$mt = microtime();
2260 9bd56e9d Christian McDonald
		echo "services_dhcpdv4_configure() being called $mt\n";
2261 61e047a5 Phil Davis
	}
2262 a25183c5 Scott Ullrich
2263 15be1722 Ermal Luçi
	/* DHCP enabled on any interfaces? */
2264 61e047a5 Phil Davis
	if (!is_dhcp_server_enabled()) {
2265 15be1722 Ermal Luçi
		return 0;
2266 61e047a5 Phil Davis
	}
2267 15be1722 Ermal Luçi
2268 9bd56e9d Christian McDonald
	if (!dhcp_is_backend('isc')) {
2269
		return 0;
2270
	}
2271
2272 532a1a0e Reid Linnemann
	$syscfg = config_get_path('system');
2273 63d6bb4f Marcos Mendoza
	config_init_path('dhcpd');
2274 532a1a0e Reid Linnemann
	$dhcpdcfg = config_get_path('dhcpd');
2275 6f9b8073 Ermal Luçi
	$Iflist = get_configured_interface_list();
2276 107e8acc Ovidiu Predescu
2277 3ad6b569 Phil Davis
	/* Only consider DNS servers with IPv4 addresses for the IPv4 DHCP server. */
2278
	$dns_arrv4 = array();
2279 68169a55 jim-p
	if (is_array($syscfg['dnsserver'])) {
2280 61e047a5 Phil Davis
		foreach ($syscfg['dnsserver'] as $dnsserver) {
2281 68169a55 jim-p
			if (is_ipaddrv4($dnsserver)) {
2282
				$dns_arrv4[] = $dnsserver;
2283
			}
2284 3ad6b569 Phil Davis
		}
2285
	}
2286
2287 816fef25 Marcos Mendoza
	if (is_platform_booting()) {
2288 f1a44a3a Carlos Eduardo Ramos
		echo gettext("Starting DHCP service...");
2289 61e047a5 Phil Davis
	} else {
2290 5b237745 Scott Ullrich
		sleep(1);
2291 61e047a5 Phil Davis
	}
2292 a25183c5 Scott Ullrich
2293 518030b3 Scott Ullrich
	$custoptions = "";
2294 107e8acc Ovidiu Predescu
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
2295 fae6b2c0 jim-p
		if (empty($dhcpifconf)) {
2296
			continue;
2297
		}
2298 284878d7 Viktor G
		$idx = 0;
2299
		$httpclient = false;
2300 61e047a5 Phil Davis
		if (is_array($dhcpifconf['numberoptions']) && is_array($dhcpifconf['numberoptions']['item'])) {
2301
			foreach ($dhcpifconf['numberoptions']['item'] as $itemidx => $item) {
2302
				if (!empty($item['type'])) {
2303 678dfd0f Erik Fonnesbeck
					$itemtype = $item['type'];
2304 61e047a5 Phil Davis
				} else {
2305 678dfd0f Erik Fonnesbeck
					$itemtype = "text";
2306 61e047a5 Phil Davis
				}
2307 678dfd0f Erik Fonnesbeck
				$custoptions .= "option custom-{$dhcpif}-{$itemidx} code {$item['number']} = {$itemtype};\n";
2308 284878d7 Viktor G
				if (($item['type'] == "text") &&
2309
				    ($item['number'] == 60) &&
2310
				    (base64_decode($item['value']) == "HTTPClient")) {
2311
					$httpclient = true;
2312 b63b534c Christian McDonald
				}
2313 284878d7 Viktor G
				$idx++;
2314 518030b3 Scott Ullrich
			}
2315
		}
2316 b68d8fe6 Viktor G
		if (!empty($dhcpifconf['uefihttpboot']) && isset($dhcpifconf['netboot']) && !$httpclient) {
2317 284878d7 Viktor G
			$custoptions .= "option custom-{$dhcpif}-{$idx} code 60 = text;\n";
2318
		}
2319 28598720 Phil Davis
		if (is_array($dhcpifconf['pool'])) {
2320
			foreach ($dhcpifconf['pool'] as $poolidx => $poolconf) {
2321 284878d7 Viktor G
				$idx = 0;
2322
				$httpclient = false;
2323 28598720 Phil Davis
				if (is_array($poolconf['numberoptions']) && is_array($poolconf['numberoptions']['item'])) {
2324
					foreach ($poolconf['numberoptions']['item'] as $itemidx => $item) {
2325
						if (!empty($item['type'])) {
2326
							$itemtype = $item['type'];
2327
						} else {
2328
							$itemtype = "text";
2329
						}
2330
						$custoptions .= "option custom-{$dhcpif}-{$poolidx}-{$itemidx} code {$item['number']} = {$itemtype};\n";
2331 284878d7 Viktor G
						if (($item['type'] == "text") &&
2332
						    ($item['number'] == 60) &&
2333
						    (base64_decode($item['value']) == "HTTPClient")) {
2334
							$httpclient = true;
2335 b63b534c Christian McDonald
						}
2336 284878d7 Viktor G
						$idx++;
2337 28598720 Phil Davis
					}
2338
				}
2339 b68d8fe6 Viktor G
				if (!empty($poolconf['uefihttpboot']) && isset($poolconf['netboot']) && !$httpclient) {
2340 284878d7 Viktor G
					$custoptions .= "option custom-{$dhcpif}-{$poolidx}-{$idx} code 60 = text;\n";
2341
				}
2342 28598720 Phil Davis
			}
2343
		}
2344 3dd5090f Viktor Gurov
		if (is_array($dhcpifconf['staticmap'])) {
2345
			$i = 0;
2346
			foreach ($dhcpifconf['staticmap'] as $sm) {
2347 8645d4c2 jim-p
				if (empty($sm)) {
2348
					continue;
2349
				}
2350 5c5a7bc8 Viktor G
				$idx = 0;
2351
				$httpclient = false;
2352 3dd5090f Viktor Gurov
				if (is_array($sm['numberoptions']) && is_array($sm['numberoptions']['item'])) {
2353
					foreach ($sm['numberoptions']['item'] as $itemidx => $item) {
2354
						if (!empty($item['type'])) {
2355
							$itemtype = $item['type'];
2356
						} else {
2357
							$itemtype = "text";
2358
						}
2359
						$custoptions .= "option custom-s_{$dhcpif}_{$i}-{$itemidx} code {$item['number']} = {$itemtype};\n";
2360
					}
2361 5c5a7bc8 Viktor G
					if (($item['type'] == "text") &&
2362
					    ($item['number'] == 60) &&
2363
					    (base64_decode($item['value']) == "HTTPClient")) {
2364
						$httpclient = true;
2365 b63b534c Christian McDonald
					}
2366 5c5a7bc8 Viktor G
					$idx++;
2367
				}
2368 b68d8fe6 Viktor G
				if (!empty($sm['uefihttpboot']) && isset($sm['netboot']) && !$httpclient) {
2369 5c5a7bc8 Viktor G
					$custoptions .= "option custom-s_{$dhcpif}_{$i}-{$idx} code 60 = text;\n";
2370 3dd5090f Viktor Gurov
				}
2371
				$i++;
2372
			}
2373
		}
2374 518030b3 Scott Ullrich
	}
2375 4cab31d0 Scott Ullrich
2376 5b237745 Scott Ullrich
	$dhcpdconf = <<<EOD
2377 107e8acc Ovidiu Predescu
2378 5b237745 Scott Ullrich
option domain-name "{$syscfg['domain']}";
2379 6c23757b Martin Fuchs
option ldap-server code 95 = text;
2380 9be23653 Martin Fuchs
option domain-search-list code 119 = text;
2381 fdb116a9 Donald A. Cupp Jr
option arch code 93 = unsigned integer 16; # RFC4578
2382 518030b3 Scott Ullrich
{$custoptions}
2383 5b237745 Scott Ullrich
default-lease-time 7200;
2384
max-lease-time 86400;
2385
log-facility local7;
2386 175fe82b Scott Ullrich
one-lease-per-client true;
2387 436a0f50 Scott Ullrich
deny duplicates;
2388 87019fc4 Andres Petralli
update-conflict-detection false;
2389 5b237745 Scott Ullrich
2390
EOD;
2391 a25183c5 Scott Ullrich
2392 a6e1c192 Viktor G
	/* take these settings from the first DHCP configured interface,
2393 b63b534c Christian McDonald
	 * see https://redmine.pfsense.org/issues/10270
2394 a6e1c192 Viktor G
	 * TODO: Global Settings tab, see https://redmine.pfsense.org/issues/5080 */
2395
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
2396 fae6b2c0 jim-p
		if (empty($dhcpifconf)) {
2397
			continue;
2398
		}
2399 a6e1c192 Viktor G
		if (!isset($dhcpifconf['disableauthoritative'])) {
2400
			$dhcpdconf .= "authoritative;\n";
2401
		}
2402 d8912c6b Chris Buechler
2403 a6e1c192 Viktor G
		if (isset($dhcpifconf['alwaysbroadcast'])) {
2404
			$dhcpdconf .= "always-broadcast on\n";
2405
		}
2406 5252b98d Scott Ullrich
2407 a6e1c192 Viktor G
		// OMAPI Settings
2408
		if (isset($dhcpifconf['omapi_port']) && is_numeric($dhcpifconf['omapi_port'])) {
2409
			$dhcpdconf .= <<<EOD
2410 6df10582 Erik Schaeffer
2411
key omapi_key {
2412
  algorithm {$dhcpifconf['omapi_key_algorithm']};
2413
  secret "{$dhcpifconf['omapi_key']}";
2414
};
2415
omapi-port {$dhcpifconf['omapi_port']};
2416
omapi-key omapi_key;
2417
2418
EOD;
2419 a6e1c192 Viktor G
2420
		}
2421
		break;
2422 6df10582 Erik Schaeffer
	}
2423
2424 5b237745 Scott Ullrich
	$dhcpdifs = array();
2425 3f141c9d Phil Davis
	$enable_add_routers = false;
2426 4bbbcc36 Marcos Mendoza
	$gateways_arr = get_gateways();
2427 c08a5659 smos
	/* only add a routers line if the system has any IPv4 gateway at all */
2428
	/* a static route has a gateway, manually overriding this field always works */
2429 61e047a5 Phil Davis
	foreach ($gateways_arr as $gwitem) {
2430
		if ($gwitem['ipprotocol'] == "inet") {
2431 3f141c9d Phil Davis
			$enable_add_routers = true;
2432 c08a5659 smos
			break;
2433
		}
2434
	}
2435 c7f44ae0 Scott Ullrich
2436 4494cf6a Chris Buechler
	/*    loop through and determine if we need to setup
2437 8fa56d1f Scott Ullrich
	 *    failover peer "bleh" entries
2438
	 */
2439
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
2440 fae6b2c0 jim-p
		if (empty($dhcpifconf)) {
2441
			continue;
2442
		}
2443 53f32329 Scott Ullrich
2444 532a1a0e Reid Linnemann
		if (!config_path_enabled("interfaces/{$dhcpif}")) {
2445 57006646 Renato Botelho
			continue;
2446 61e047a5 Phil Davis
		}
2447 57006646 Renato Botelho
2448 09f11c71 jim-p
		interfaces_staticarp_configure($dhcpif);
2449
2450 61e047a5 Phil Davis
		if (!isset($dhcpifconf['enable'])) {
2451 6f9b8073 Ermal Luçi
			continue;
2452 61e047a5 Phil Davis
		}
2453 6f9b8073 Ermal Luçi
2454 61e047a5 Phil Davis
		if ($dhcpifconf['failover_peerip'] <> "") {
2455 a01f8bfc Ermal
			$intip = get_interface_ip($dhcpif);
2456 8fa56d1f Scott Ullrich
			/*
2457
			 *    yep, failover peer is defined.
2458
			 *    does it match up to a defined vip?
2459
			 */
2460 d2edbd8a Scott Ullrich
			$skew = 110;
2461 532a1a0e Reid Linnemann
			$vips = config_get_path('virtualip/vip', []);
2462
			if (!empty($vips)) {
2463
				foreach ($vips as $vipent) {
2464 97e6ec09 Luiz Souza
					if ($vipent['mode'] != 'carp') {
2465
						continue;
2466
					}
2467 61e047a5 Phil Davis
					if ($vipent['interface'] == $dhcpif) {
2468 532a1a0e Reid Linnemann
						$ipaddr = config_get_path("interfaces/{$dhcpif}/ipaddr");
2469
						$subnet = config_get_path("interfaces/{$dhcpif}/subnet");
2470
						$carp_nw = gen_subnet($ipaddr,$subnet);
2471
						$carp_nw .= "/{$subnet}";
2472 97e6ec09 Luiz Souza
						if (ip_in_subnet($dhcpifconf['failover_peerip'], $carp_nw)) {
2473 a01f8bfc Ermal
							/* this is the interface! */
2474 61e047a5 Phil Davis
							if (is_numeric($vipent['advskew']) && (intval($vipent['advskew']) < 20)) {
2475 a01f8bfc Ermal
								$skew = 0;
2476
								break;
2477
							}
2478
						}
2479 6181b36f Scott Ullrich
					}
2480 8fa56d1f Scott Ullrich
				}
2481 25066204 Scott Ullrich
			} else {
2482 959dc96b Chris Buechler
				log_error(gettext("Warning!  DHCP Failover setup and no CARP virtual IPs defined!"));
2483 8fa56d1f Scott Ullrich
			}
2484 61e047a5 Phil Davis
			if ($skew > 10) {
2485 8fa56d1f Scott Ullrich
				$type = "secondary";
2486 0e93097a Scott Ullrich
				$my_port = "520";
2487
				$peer_port = "519";
2488 faab657a jim-p
				$dhcpdconf_pri = '';
2489 8fa56d1f Scott Ullrich
			} else {
2490 0e93097a Scott Ullrich
				$my_port = "519";
2491
				$peer_port = "520";
2492 8fa56d1f Scott Ullrich
				$type = "primary";
2493 4de8f7ba Phil Davis
				$dhcpdconf_pri = "split 128;\n";
2494 1a0bb737 Scott Ullrich
				$dhcpdconf_pri .= "  mclt 600;\n";
2495 8fa56d1f Scott Ullrich
			}
2496 a01f8bfc Ermal
2497
			if (is_ipaddrv4($intip)) {
2498 61e047a5 Phil Davis
				$dhcpdconf .= <<<EOPP
2499 d5e4f7c9 jim-p
failover peer "dhcp_{$dhcpif}" {
2500 8fa56d1f Scott Ullrich
  {$type};
2501
  address {$intip};
2502 0e93097a Scott Ullrich
  port {$my_port};
2503 8fa56d1f Scott Ullrich
  peer address {$dhcpifconf['failover_peerip']};
2504 0e93097a Scott Ullrich
  peer port {$peer_port};
2505 2cd5ce14 Scott Ullrich
  max-response-delay 10;
2506 b865d178 Scott Ullrich
  max-unacked-updates 10;
2507
  {$dhcpdconf_pri}
2508 b259d1c6 Scott Ullrich
  load balance max seconds 3;
2509 8fa56d1f Scott Ullrich
}
2510 959dc96b Chris Buechler
\n
2511 8fa56d1f Scott Ullrich
EOPP;
2512 a01f8bfc Ermal
			}
2513 8fa56d1f Scott Ullrich
		}
2514
	}
2515
2516 5b237745 Scott Ullrich
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
2517 fae6b2c0 jim-p
		if (empty($dhcpifconf)) {
2518
			continue;
2519
		}
2520 a25183c5 Scott Ullrich
2521 3df2dbfd jim-p
		$newzone = array();
2522 532a1a0e Reid Linnemann
		$ifcfg = config_get_path("interfaces/{$dhcpif}");
2523 a25183c5 Scott Ullrich
2524 61e047a5 Phil Davis
		if (!isset($dhcpifconf['enable']) || !isset($Iflist[$dhcpif])) {
2525 5b237745 Scott Ullrich
			continue;
2526 61e047a5 Phil Davis
		}
2527 a55e9c70 Ermal Lu?i
		$ifcfgip = get_interface_ip($dhcpif);
2528
		$ifcfgsn = get_interface_subnet($dhcpif);
2529
		$subnet = gen_subnet($ifcfgip, $ifcfgsn);
2530
		$subnetmask = gen_subnet_mask($ifcfgsn);
2531 a25183c5 Scott Ullrich
2532 61e047a5 Phil Davis
		if (!is_ipaddr($subnet)) {
2533 85e3f445 Ermal
			continue;
2534 61e047a5 Phil Davis
		}
2535 85e3f445 Ermal
2536 cba980f6 jim-p
		$all_pools = array();
2537
		$all_pools[] = $dhcpifconf;
2538
		if (is_array($dhcpifconf['pool'])) {
2539
			$all_pools = array_merge($all_pools, $dhcpifconf['pool']);
2540
		}
2541
2542 5b237745 Scott Ullrich
		$dnscfg = "";
2543 a25183c5 Scott Ullrich
2544 5b237745 Scott Ullrich
		if ($dhcpifconf['domain']) {
2545
			$dnscfg .= "	option domain-name \"{$dhcpifconf['domain']}\";\n";
2546
		}
2547 107e8acc Ovidiu Predescu
2548 61e047a5 Phil Davis
		if ($dhcpifconf['domainsearchlist'] <> "") {
2549 a3de8b9e Pierre POMES
			$dnscfg .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpifconf['domainsearchlist'])) . "\";\n";
2550 84931046 jim-p
		}
2551 9be23653 Martin Fuchs
2552 4e9cd828 Seth Mos
		if (isset($dhcpifconf['ddnsupdate'])) {
2553 3df2dbfd jim-p
			$need_ddns_updates = true;
2554
			$newzone = array();
2555 61e047a5 Phil Davis
			if ($dhcpifconf['ddnsdomain'] <> "") {
2556 3df2dbfd jim-p
				$newzone['domain-name'] = $dhcpifconf['ddnsdomain'];
2557 4e9cd828 Seth Mos
				$dnscfg .= "	ddns-domainname \"{$dhcpifconf['ddnsdomain']}\";\n";
2558 3df2dbfd jim-p
			} else {
2559 532a1a0e Reid Linnemann
				$newzone['domain-name'] = config_get_path('system/domain');
2560 3df2dbfd jim-p
			}
2561 cf688344 Renato Botelho
2562 67784aa6 Steve Beaver
			if (empty($dhcpifconf['ddnsclientupdates'])) {
2563
				$ddnsclientupdates = 'allow';
2564
			} else {
2565
				$ddnsclientupdates = $dhcpifconf['ddnsclientupdates'];
2566
			}
2567
2568
			$dnscfg .= "	{$ddnsclientupdates} client-updates;\n";
2569
2570 cf688344 Renato Botelho
			$revsubnet = array_reverse(explode('.',$subnet));
2571
2572 d95e86dc Viktor Gurov
			$subnet_mask_bits = 32 - $ifcfgsn;
2573
			$start_octet = $subnet_mask_bits >> 3;
2574
			$octet_mask_bits = $subnet_mask_bits & ($subnet_mask_bits % 8);
2575
			if ($octet_mask_bits) {
2576
			    $octet_mask = (1 << $octet_mask_bits) - 1;
2577
			    $octet_start = $revsubnet[$start_octet] & ~$octet_mask;
2578
			    $revsubnet[$start_octet] = $octet_start . "-" . ($octet_start + $octet_mask);
2579 cf688344 Renato Botelho
			}
2580
2581
			$ptr_domain = '';
2582
			for ($octet = 0; $octet <= 3; $octet++) {
2583
				if ($octet < $start_octet) {
2584
					continue;
2585 61e047a5 Phil Davis
				}
2586 c9fd7ee0 Chris Buechler
				$ptr_domain .= ((empty($ptr_domain) && $ptr_domain !== "0") ? '' : '.');
2587 cf688344 Renato Botelho
				$ptr_domain .= $revsubnet[$octet];
2588 4e9cd828 Seth Mos
			}
2589 cf688344 Renato Botelho
			$ptr_domain .= ".in-addr.arpa";
2590
			$newzone['ptr-domain'] = $ptr_domain;
2591
			unset($ptr_domain, $revsubnet, $start_octet);
2592 4e9cd828 Seth Mos
		}
2593
2594 aff9d6ab Scott Ullrich
		if (is_array($dhcpifconf['dnsserver']) && ($dhcpifconf['dnsserver'][0])) {
2595 8ee01642 Scott Ullrich
			$dnscfg .= "	option domain-name-servers " . join(",", $dhcpifconf['dnsserver']) . ";";
2596 61e047a5 Phil Davis
			if ($newzone['domain-name']) {
2597 3df2dbfd jim-p
				$newzone['dns-servers'] = $dhcpifconf['dnsserver'];
2598 61e047a5 Phil Davis
			}
2599 532a1a0e Reid Linnemann
		} else if (config_path_enabled('dnsmasq')) {
2600 a55e9c70 Ermal Lu?i
			$dnscfg .= "	option domain-name-servers {$ifcfgip};";
2601 61e047a5 Phil Davis
			if ($newzone['domain-name'] && is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
2602 3df2dbfd jim-p
				$newzone['dns-servers'] = $syscfg['dnsserver'];
2603 61e047a5 Phil Davis
			}
2604 532a1a0e Reid Linnemann
		} else if (config_path_enabled('unbound')) {
2605 88a0937d Chris Buechler
			$dnscfg .= "	option domain-name-servers {$ifcfgip};";
2606 3ad6b569 Phil Davis
		} else if (!empty($dns_arrv4)) {
2607
			$dnscfg .= "	option domain-name-servers " . join(",", $dns_arrv4) . ";";
2608 61e047a5 Phil Davis
			if ($newzone['domain-name']) {
2609 3ad6b569 Phil Davis
				$newzone['dns-servers'] = $dns_arrv4;
2610 61e047a5 Phil Davis
			}
2611 aff9d6ab Scott Ullrich
		}
2612
2613 61e047a5 Phil Davis
		/* Create classes - These all contain comma separated lists. Join them into one
2614 cba980f6 jim-p
		   big comma separated string then split them all up. */
2615
		$all_mac_strings = array();
2616
		if (is_array($dhcpifconf['pool'])) {
2617 61e047a5 Phil Davis
			foreach ($all_pools as $poolconf) {
2618 cba980f6 jim-p
				$all_mac_strings[] = $poolconf['mac_allow'];
2619
				$all_mac_strings[] = $poolconf['mac_deny'];
2620
			}
2621
		}
2622
		$all_mac_strings[] = $dhcpifconf['mac_allow'];
2623
		$all_mac_strings[] = $dhcpifconf['mac_deny'];
2624 7fd93993 Ermal LUÇI
		if (!empty($all_mac_strings)) {
2625 4de8f7ba Phil Davis
			$all_mac_list = array_unique(explode(',', implode(',', $all_mac_strings)));
2626
			foreach ($all_mac_list as $mac) {
2627
				if (empty($mac)) {
2628
					continue;
2629
				}
2630
				$dhcpdconf .= 'class "' . str_replace(':', '', $mac) . '" {' . "\n";
2631
				// Skip the first octet of the MAC address - for media type, typically Ethernet ("01") and match the rest.
2632
				$dhcpdconf .= '	match if substring (hardware, 1, ' . (substr_count($mac, ':') + 1) . ') = ' . $mac . ';' . "\n";
2633
				$dhcpdconf .= '}' . "\n";
2634 61e047a5 Phil Davis
			}
2635 1f1a08c8 jim-p
		}
2636
2637 35bc0edf reb00tz
		// instantiate class before pool definition
2638
		$dhcpdconf .= "class \"s_{$dhcpif}\" {\n	match pick-first-value (option dhcp-client-identifier, hardware);\n}\n";
2639
2640 85e3f445 Ermal
		$dhcpdconf .= "subnet {$subnet} netmask {$subnetmask} {\n";
2641 c7f44ae0 Scott Ullrich
2642 61e047a5 Phil Davis
		// Setup pool options
2643 28598720 Phil Davis
		foreach ($all_pools as $all_pools_idx => $poolconf) {
2644 9228166f Phil Davis
			if (!(ip_in_subnet($poolconf['range']['from'], "{$subnet}/{$ifcfgsn}") && ip_in_subnet($poolconf['range']['to'], "{$subnet}/{$ifcfgsn}"))) {
2645
				// If the user has changed the subnet from the interfaces page and applied,
2646
				// but has not updated the DHCP range, then the range to/from of the pool can be outside the subnet.
2647 f8cc55bf Phil Davis
				// This can also happen when implementing the batch of changes when the setup wizard reloads the new settings.
2648 1579e70f Phil Davis
				$error_msg = sprintf(gettext('Invalid DHCP pool %1$s - %2$s for %3$s subnet %4$s/%5$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);
2649 f8cc55bf Phil Davis
				$do_file_notice = true;
2650
				$conf_ipv4_address = $ifcfg['ipaddr'];
2651
				$conf_ipv4_subnetmask = $ifcfg['subnet'];
2652
				if (is_ipaddrv4($conf_ipv4_address) && is_subnet("{$conf_ipv4_address}/{$conf_ipv4_subnetmask}")) {
2653
					$conf_subnet_base = gen_subnet($conf_ipv4_address, $conf_ipv4_subnetmask);
2654
					if (ip_in_subnet($poolconf['range']['from'], "{$conf_subnet_base}/{$conf_ipv4_subnetmask}") &&
2655
					    ip_in_subnet($poolconf['range']['to'], "{$conf_subnet_base}/{$conf_ipv4_subnetmask}")) {
2656
						// Even though the running interface subnet does not match the pool range,
2657
						// the interface subnet in the config file contains the pool range.
2658
						// We are somewhere part-way through a settings reload, e.g. after running the setup wizard.
2659
						// services_dhcpdv4_configure will be called again later when the new interface settings from
2660
						// the config are applied and at that time everything will match up.
2661
						// Ignore this pool on this interface for now and just log the error to the system log.
2662
						log_error($error_msg);
2663
						$do_file_notice = false;
2664
					}
2665
				}
2666
				if ($do_file_notice) {
2667
					file_notice("DHCP", $error_msg);
2668
				}
2669 9228166f Phil Davis
				continue;
2670
			}
2671 cba980f6 jim-p
			$dhcpdconf .= "	pool {\n";
2672
			/* is failover dns setup? */
2673
			if (is_array($poolconf['dnsserver']) && $poolconf['dnsserver'][0] <> "") {
2674
				$dhcpdconf .= "		option domain-name-servers {$poolconf['dnsserver'][0]}";
2675 61e047a5 Phil Davis
				if ($poolconf['dnsserver'][1] <> "") {
2676 cba980f6 jim-p
					$dhcpdconf .= ",{$poolconf['dnsserver'][1]}";
2677 61e047a5 Phil Davis
				}
2678
				if ($poolconf['dnsserver'][2] <> "") {
2679 8cbb140a Phil Davis
					$dhcpdconf .= ",{$poolconf['dnsserver'][2]}";
2680 61e047a5 Phil Davis
				}
2681
				if ($poolconf['dnsserver'][3] <> "") {
2682 8cbb140a Phil Davis
					$dhcpdconf .= ",{$poolconf['dnsserver'][3]}";
2683 61e047a5 Phil Davis
				}
2684 cba980f6 jim-p
				$dhcpdconf .= ";\n";
2685
			}
2686
2687
			/* allow/deny MACs */
2688
			$mac_allow_list = array_unique(explode(',', $poolconf['mac_allow']));
2689
			foreach ($mac_allow_list as $mac) {
2690 61e047a5 Phil Davis
				if (empty($mac)) {
2691 cba980f6 jim-p
					continue;
2692 61e047a5 Phil Davis
				}
2693 cba980f6 jim-p
				$dhcpdconf .= "		allow members of \"" . str_replace(':', '', $mac) . "\";\n";
2694
			}
2695 3475eb04 Andrew Pilloud
			$deny_action = "deny";
2696 8209517d jim-p
			if (isset($poolconf['nonak']) && empty($poolconf['failover_peerip'])) {
2697 3475eb04 Andrew Pilloud
				$deny_action = "ignore";
2698
			}
2699 cba980f6 jim-p
			$mac_deny_list = array_unique(explode(',', $poolconf['mac_deny']));
2700
			foreach ($mac_deny_list as $mac) {
2701 61e047a5 Phil Davis
				if (empty($mac)) {
2702 cba980f6 jim-p
					continue;
2703 61e047a5 Phil Davis
				}
2704 773902ef Viktor G
				$dhcpdconf .= "		deny members of \"" . str_replace(':', '', $mac) . "\";\n";
2705 cba980f6 jim-p
			}
2706
2707 61e047a5 Phil Davis
			if ($poolconf['failover_peerip'] <> "") {
2708 3475eb04 Andrew Pilloud
				$dhcpdconf .= "		$deny_action dynamic bootp clients;\n";
2709 61e047a5 Phil Davis
			}
2710 cba980f6 jim-p
2711 35bc0edf reb00tz
			// set pool MAC limitations
2712 61e047a5 Phil Davis
			if (isset($poolconf['denyunknown'])) {
2713 35bc0edf reb00tz
				if ($poolconf['denyunknown'] == "class") {
2714
					$dhcpdconf .= "		allow members of \"s_{$dhcpif}\";\n";
2715
					$dhcpdconf .= "		$deny_action unknown-clients;\n";
2716
				} else if ($poolconf['denyunknown'] == "disabled") {
2717
					// add nothing to $dhcpdconf; condition added to prevent next condition applying if ever engine changes such that: isset("disabled") == true
2718
				} else {	// "catch-all" covering "enabled" value post-PR#4066, and covering non-upgraded boolean option (i.e. literal value "enabled")
2719
					$dhcpdconf .= "		$deny_action unknown-clients;\n";
2720
				}
2721 61e047a5 Phil Davis
			}
2722 cba980f6 jim-p
2723 61e047a5 Phil Davis
			if ($poolconf['gateway'] && $poolconf['gateway'] != "none" && ($poolconf['gateway'] != $dhcpifconf['gateway'])) {
2724 f9f6f7d4 jim-p
				$dhcpdconf .= "		option routers {$poolconf['gateway']};\n";
2725 61e047a5 Phil Davis
			}
2726 cba980f6 jim-p
2727 61e047a5 Phil Davis
			if ($dhcpifconf['failover_peerip'] <> "") {
2728 d5e4f7c9 jim-p
				$dhcpdconf .= "		failover peer \"dhcp_{$dhcpif}\";\n";
2729 cba980f6 jim-p
			}
2730
2731
			$pdnscfg = "";
2732
2733
			if ($poolconf['domain'] && ($poolconf['domain'] != $dhcpifconf['domain'])) {
2734
				$pdnscfg .= "		option domain-name \"{$poolconf['domain']}\";\n";
2735
			}
2736
2737 61e047a5 Phil Davis
			if (!empty($poolconf['domainsearchlist']) && ($poolconf['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
2738 cba980f6 jim-p
				$pdnscfg .= "		option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $poolconf['domainsearchlist'])) . "\";\n";
2739
			}
2740
2741 7309ff39 Renato Botelho
			if (isset($poolconf['ddnsupdate'])) {
2742 61e047a5 Phil Davis
				if (($poolconf['ddnsdomain'] <> "") && ($poolconf['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
2743 cba980f6 jim-p
					$pdnscfg .= "		ddns-domainname \"{$poolconf['ddnsdomain']}\";\n";
2744 61e047a5 Phil Davis
				}
2745 cba980f6 jim-p
				$pdnscfg .= "		ddns-update-style interim;\n";
2746
			}
2747
2748
			$dhcpdconf .= "{$pdnscfg}";
2749 1f1a08c8 jim-p
2750 cba980f6 jim-p
			// default-lease-time
2751 61e047a5 Phil Davis
			if ($poolconf['defaultleasetime'] && ($poolconf['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) {
2752 cba980f6 jim-p
				$dhcpdconf .= "		default-lease-time {$poolconf['defaultleasetime']};\n";
2753 61e047a5 Phil Davis
			}
2754 cba980f6 jim-p
2755
			// max-lease-time
2756 61e047a5 Phil Davis
			if ($poolconf['maxleasetime'] && ($poolconf['maxleasetime'] != $dhcpifconf['maxleasetime'])) {
2757 cba980f6 jim-p
				$dhcpdconf .= "		max-lease-time {$poolconf['maxleasetime']};\n";
2758 61e047a5 Phil Davis
			}
2759 cba980f6 jim-p
2760 6d53301b Jose Luis Duran
			// ignore bootp
2761
			if (isset($poolconf['ignorebootp'])) {
2762
				$dhcpdconf .= "		ignore bootp;\n";
2763
			}
2764
2765 11ee0c6d Brett Keller
			// ignore-client-uids
2766
			if (isset($poolconf['ignoreclientuids'])) {
2767
				$dhcpdconf .= "		ignore-client-uids true;\n";
2768
			}
2769
2770 cba980f6 jim-p
			// netbios-name*
2771 fa7b825f Renato Botelho
			if (is_array($poolconf['winsserver']) && $poolconf['winsserver'][0] && ($poolconf['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
2772 cba980f6 jim-p
				$dhcpdconf .= "		option netbios-name-servers " . join(",", $poolconf['winsserver']) . ";\n";
2773
				$dhcpdconf .= "		option netbios-node-type 8;\n";
2774
			}
2775 c7f44ae0 Scott Ullrich
2776 cba980f6 jim-p
			// ntp-servers
2777 61e047a5 Phil Davis
			if (is_array($poolconf['ntpserver']) && $poolconf['ntpserver'][0] && ($poolconf['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
2778 cba980f6 jim-p
				$dhcpdconf .= "		option ntp-servers " . join(",", $poolconf['ntpserver']) . ";\n";
2779 61e047a5 Phil Davis
			}
2780 cba980f6 jim-p
2781
			// tftp-server-name
2782 61e047a5 Phil Davis
			if (!empty($poolconf['tftp']) && ($poolconf['tftp'] != $dhcpifconf['tftp'])) {
2783 cba980f6 jim-p
				$dhcpdconf .= "		option tftp-server-name \"{$poolconf['tftp']}\";\n";
2784 61e047a5 Phil Davis
			}
2785 cba980f6 jim-p
2786 28598720 Phil Davis
			// Handle pool-specific options
2787
			$dhcpdconf .= "\n";
2788
			// Ignore the first pool, which is the "overall" pool when $all_pools_idx is 0 - those are put outside the pool block later
2789 284878d7 Viktor G
			$idx = 0;
2790
			$httpclient = false;
2791 b88050bb jim-p
			if (isset($poolconf['numberoptions']['item']) && is_array($poolconf['numberoptions']['item']) && ($all_pools_idx > 0)) {
2792 28598720 Phil Davis
				// Use the "real" pool index from the config, excluding the "overall" pool, and based from 0.
2793
				// This matches the way $poolidx was used when generating the $custoptions string earlier.
2794
				$poolidx = $all_pools_idx - 1;
2795
				foreach ($poolconf['numberoptions']['item'] as $itemidx => $item) {
2796
					$item_value = base64_decode($item['value']);
2797
					if (empty($item['type']) || $item['type'] == "text") {
2798 c507161d Phil Davis
						$dhcpdconf .= "		option custom-{$dhcpif}-{$poolidx}-{$itemidx} \"{$item_value}\";\n";
2799 28598720 Phil Davis
					} else {
2800 c507161d Phil Davis
						$dhcpdconf .= "		option custom-{$dhcpif}-{$poolidx}-{$itemidx} {$item_value};\n";
2801 28598720 Phil Davis
					}
2802 284878d7 Viktor G
					if (($item['type'] == "text") &&
2803
					    ($item['number'] == 60) &&
2804
					    (base64_decode($item['value']) == "HTTPClient")) {
2805
						$httpclient = true;
2806 b63b534c Christian McDonald
					}
2807 284878d7 Viktor G
					$idx++;
2808 28598720 Phil Davis
				}
2809
			}
2810 b68d8fe6 Viktor G
			if (!empty($poolconf['uefihttpboot']) && isset($poolconf['netboot']) && !$httpclient &&
2811 284878d7 Viktor G
			    (!isset($dhcpifconf['uefihttpboot']) ||
2812
			    ($poolconf['uefihttpboot'] != $dhcpifconf['uefihttpboot']))) {
2813
				$dhcpdconf .= "		option custom-{$dhcpif}-{$poolidx}-{$idx} \"HTTPClient\";\n";
2814
			}
2815 28598720 Phil Davis
2816 cba980f6 jim-p
			// ldap-server
2817 61e047a5 Phil Davis
			if (!empty($poolconf['ldap']) && ($poolconf['ldap'] != $dhcpifconf['ldap'])) {
2818 cba980f6 jim-p
				$dhcpdconf .= "		option ldap-server \"{$poolconf['ldap']}\";\n";
2819 61e047a5 Phil Davis
			}
2820 cba980f6 jim-p
2821
			// net boot information
2822 61e047a5 Phil Davis
			if (isset($poolconf['netboot'])) {
2823 cba980f6 jim-p
				if (!empty($poolconf['nextserver']) && ($poolconf['nextserver'] != $dhcpifconf['nextserver'])) {
2824
					$dhcpdconf .= "		next-server {$poolconf['nextserver']};\n";
2825
				}
2826 9d775c75 Renato Botelho
2827 e5395534 Viktor G
				$pxe_files = array();
2828 9d775c75 Renato Botelho
				if (!empty($poolconf['filename']) &&
2829
				    (!isset($dhcpifconf['filename']) ||
2830
				    ($poolconf['filename'] != $dhcpifconf['filename']))) {
2831
					$filename = $poolconf['filename'];
2832
				}
2833 cf40cd17 Viktor G
				if (!empty($poolconf['uefihttpboot']) &&
2834
				    (!isset($dhcpifconf['uefihttpboot']) ||
2835
				    ($poolconf['uefihttpboot'] != $dhcpifconf['uefihttpboot']))) {
2836
					$pxe_files[] = array('HTTPClient', $poolconf['uefihttpboot']);
2837
				}
2838 9d775c75 Renato Botelho
				if (!empty($poolconf['filename32']) &&
2839
				    (!isset($dhcpifconf['filename32']) ||
2840
				    ($poolconf['filename32'] != $dhcpifconf['filename32']))) {
2841 e5395534 Viktor G
					$pxe_files[] = array('00:06', $poolconf['filename32']);
2842 9d775c75 Renato Botelho
				}
2843
				if (!empty($poolconf['filename64']) &&
2844
				    (!isset($dhcpifconf['filename64']) ||
2845
				    ($poolconf['filename64'] != $dhcpifconf['filename64']))) {
2846 e5395534 Viktor G
					$pxe_files[] = array('00:07', $poolconf['filename64']);
2847
					$pxe_files[] = array('00:09', $poolconf['filename64']);
2848 cba980f6 jim-p
				}
2849 4c85579b Wasurerarenai
				if (!empty($poolconf['filename32arm']) &&
2850
				    (!isset($dhcpifconf['filename32arm']) ||
2851
				    ($poolconf['filename32arm'] != $dhcpifconf['filename32arm']))) {
2852 e5395534 Viktor G
					$pxe_files[] = array('00:0a', $poolconf['filename32arm']);
2853 4c85579b Wasurerarenai
				}
2854
				if (!empty($poolconf['filename64arm']) &&
2855
				    (!isset($dhcpifconf['filename64arm']) ||
2856
				    ($poolconf['filename64arm'] != $dhcpifconf['filename64arm']))) {
2857 e5395534 Viktor G
					$pxe_files[] = array('00:0b', $poolconf['filename64arm']);
2858 4c85579b Wasurerarenai
				}
2859 9d775c75 Renato Botelho
2860 e5395534 Viktor G
				$pxeif = false;
2861
				if (is_array($pxe_files) && !empty($pxe_files)) {
2862
					foreach ($pxe_files as $pxe) {
2863 cf40cd17 Viktor G
						if ($pxe[0] == 'HTTPClient') {
2864
							$expr = "substring (option vendor-class-identifier, 0, 10) = \"HTTPClient\" {\n";
2865
						} else {
2866
							$expr = "option arch = {$pxe[0]} {\n";
2867
						}
2868 e5395534 Viktor G
						if (!$pxeif) {
2869 cf40cd17 Viktor G
							$dhcpdconf .= "		if " . $expr;
2870 e5395534 Viktor G
							$pxeif = true;
2871
						} else {
2872 cf40cd17 Viktor G
							$dhcpdconf .= " else if " . $expr;
2873 e5395534 Viktor G
						}
2874
						$dhcpdconf .= "			filename \"{$pxe[1]}\";\n";
2875
						$dhcpdconf .= "		}";
2876 4c85579b Wasurerarenai
					}
2877 e5395534 Viktor G
					if ($filename) {
2878
						$dhcpdconf .= " else {\n";
2879
						$dhcpdconf .= "			filename \"{$filename}\";\n";
2880
						$dhcpdconf .= "		}";
2881 4c85579b Wasurerarenai
					}
2882 e5395534 Viktor G
					$dhcpdconf .= "\n\n";
2883 9d775c75 Renato Botelho
				} elseif (!empty($filename)) {
2884
					$dhcpdconf .= "		filename \"{$filename}\";\n";
2885 cba980f6 jim-p
				}
2886 e5395534 Viktor G
				unset($filename);
2887 9d775c75 Renato Botelho
2888 cba980f6 jim-p
				if (!empty($poolconf['rootpath']) && ($poolconf['rootpath'] != $dhcpifconf['rootpath'])) {
2889
					$dhcpdconf .= "		option root-path \"{$poolconf['rootpath']}\";\n";
2890
				}
2891
			}
2892
			$dhcpdconf .= "		range {$poolconf['range']['from']} {$poolconf['range']['to']};\n";
2893
			$dhcpdconf .= "	}\n\n";
2894
		}
2895
// End of settings inside pools
2896 a25183c5 Scott Ullrich
2897 4208f7b1 timdufrane
		if ($dhcpifconf['gateway'] && $dhcpifconf['gateway'] != "none") {
2898 5b237745 Scott Ullrich
			$routers = $dhcpifconf['gateway'];
2899 c08a5659 smos
			$add_routers = true;
2900 4208f7b1 timdufrane
		} elseif ($dhcpifconf['gateway'] == "none") {
2901
			$add_routers = false;
2902 c08a5659 smos
		} else {
2903 3f141c9d Phil Davis
			$add_routers = $enable_add_routers;
2904 a55e9c70 Ermal Lu?i
			$routers = $ifcfgip;
2905 c08a5659 smos
		}
2906 61e047a5 Phil Davis
		if ($add_routers) {
2907 c08a5659 smos
			$dhcpdconf .= "	option routers {$routers};\n";
2908 61e047a5 Phil Davis
		}
2909 cba980f6 jim-p
2910 fb04e80e Marcos Mendoza
		// DDNS updates must be explicitly configured per subnet - see #13894
2911
		if ($need_ddns_updates) {
2912
			$dhcpdconf .= '	ddns-updates ' . (isset($dhcpifconf['ddnsupdate']) ? 'on' : 'off') . ";\n";
2913
		}
2914
2915 c08a5659 smos
		$dhcpdconf .= <<<EOD
2916 5b237745 Scott Ullrich
$dnscfg
2917
2918
EOD;
2919 61e047a5 Phil Davis
		// default-lease-time
2920
		if ($dhcpifconf['defaultleasetime']) {
2921 5b237745 Scott Ullrich
			$dhcpdconf .= "	default-lease-time {$dhcpifconf['defaultleasetime']};\n";
2922 61e047a5 Phil Davis
		}
2923 518030b3 Scott Ullrich
2924
		// max-lease-time
2925 61e047a5 Phil Davis
		if ($dhcpifconf['maxleasetime']) {
2926 5b237745 Scott Ullrich
			$dhcpdconf .= "	max-lease-time {$dhcpifconf['maxleasetime']};\n";
2927 5197e3e3 Renato Botelho do Couto
		}
2928
2929
		if (!isset($dhcpifconf['disablepingcheck'])) {
2930
			$dhcpdconf .= "	ping-check true;\n";
2931
		} else {
2932
			$dhcpdconf .= "	ping-check false;\n";
2933 61e047a5 Phil Davis
		}
2934 a25183c5 Scott Ullrich
2935 518030b3 Scott Ullrich
		// netbios-name*
2936 5b237745 Scott Ullrich
		if (is_array($dhcpifconf['winsserver']) && $dhcpifconf['winsserver'][0]) {
2937
			$dhcpdconf .= "	option netbios-name-servers " . join(",", $dhcpifconf['winsserver']) . ";\n";
2938
			$dhcpdconf .= "	option netbios-node-type 8;\n";
2939
		}
2940 a25183c5 Scott Ullrich
2941 518030b3 Scott Ullrich
		// ntp-servers
2942 61e047a5 Phil Davis
		if (is_array($dhcpifconf['ntpserver']) && $dhcpifconf['ntpserver'][0]) {
2943 ad171999 Seth Mos
			$dhcpdconf .= "	option ntp-servers " . join(",", $dhcpifconf['ntpserver']) . ";\n";
2944 61e047a5 Phil Davis
		}
2945 ad171999 Seth Mos
2946 518030b3 Scott Ullrich
		// tftp-server-name
2947 61e047a5 Phil Davis
		if ($dhcpifconf['tftp'] <> "") {
2948 6c23757b Martin Fuchs
			$dhcpdconf .= "	option tftp-server-name \"{$dhcpifconf['tftp']}\";\n";
2949 61e047a5 Phil Davis
		}
2950 6c23757b Martin Fuchs
2951 518030b3 Scott Ullrich
		// Handle option, number rowhelper values
2952
		$dhcpdconf .= "\n";
2953 284878d7 Viktor G
		$idx = 0;
2954
		$httpclient = false;
2955 b88050bb jim-p
		if (isset($dhcpifconf['numberoptions']['item']) && is_array($dhcpifconf['numberoptions']['item'])) {
2956 61e047a5 Phil Davis
			foreach ($dhcpifconf['numberoptions']['item'] as $itemidx => $item) {
2957 65cce9d7 Renato Botelho
				$item_value = base64_decode($item['value']);
2958 61e047a5 Phil Davis
				if (empty($item['type']) || $item['type'] == "text") {
2959 65cce9d7 Renato Botelho
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} \"{$item_value}\";\n";
2960 61e047a5 Phil Davis
				} else {
2961 65cce9d7 Renato Botelho
					$dhcpdconf .= "	option custom-{$dhcpif}-{$itemidx} {$item_value};\n";
2962 61e047a5 Phil Davis
				}
2963 284878d7 Viktor G
				if (($item['type'] == "text") &&
2964
				    ($item['number'] == 60) &&
2965
				    (base64_decode($item['value']) == "HTTPClient")) {
2966
					$httpclient = true;
2967 b63b534c Christian McDonald
				}
2968 284878d7 Viktor G
				$idx++;
2969 518030b3 Scott Ullrich
			}
2970
		}
2971 b68d8fe6 Viktor G
		if (!empty($dhcpifconf['uefihttpboot']) && isset($dhcpifconf['netboot']) && !$httpclient) {
2972 284878d7 Viktor G
			$dhcpdconf .= "	option custom-{$dhcpif}-{$idx} \"HTTPClient\";\n";
2973
		}
2974 518030b3 Scott Ullrich
2975
		// ldap-server
2976 61e047a5 Phil Davis
		if ($dhcpifconf['ldap'] <> "") {
2977 6c23757b Martin Fuchs
			$dhcpdconf .= "	option ldap-server \"{$dhcpifconf['ldap']}\";\n";
2978 61e047a5 Phil Davis
		}
2979 6c23757b Martin Fuchs
2980 518030b3 Scott Ullrich
		// net boot information
2981 61e047a5 Phil Davis
		if (isset($dhcpifconf['netboot'])) {
2982 a2578c27 Anthony Wrather
			if ($dhcpifconf['nextserver'] <> "") {
2983
				$dhcpdconf .= "	next-server {$dhcpifconf['nextserver']};\n";
2984
			}
2985 e5395534 Viktor G
2986
			$pxe_files = array();
2987
			if (!empty($dhcpifconf['filename'])) {
2988
				$filename = $dhcpifconf['filename'];
2989
			}
2990 cf40cd17 Viktor G
			if (!empty($dhcpifconf['uefihttpboot'])) {
2991
				$pxe_files[] = array('HTTPClient', $dhcpifconf['uefihttpboot']);
2992
			}
2993 e5395534 Viktor G
			if (!empty($dhcpifconf['filename32'])) {
2994
				$pxe_files[] = array('00:06', $dhcpifconf['filename32']);
2995
			}
2996
			if (!empty($dhcpifconf['filename64'])) {
2997
				$pxe_files[] = array('00:07', $dhcpifconf['filename64']);
2998
				$pxe_files[] = array('00:09', $dhcpifconf['filename64']);
2999
			}
3000
			if (!empty($dhcpifconf['filename32arm'])) {
3001
				$pxe_files[] = array('00:0a', $dhcpifconf['filename32arm']);
3002
			}
3003
			if (!empty($dhcpifconf['filename64arm'])) {
3004
				$pxe_files[] = array('00:0b', $dhcpifconf['filename64arm']);
3005
			}
3006
3007
			$pxeif = false;
3008
			if (is_array($pxe_files) && !empty($pxe_files)) {
3009
				foreach ($pxe_files as $pxe) {
3010 cf40cd17 Viktor G
					if ($pxe[0] == 'HTTPClient') {
3011
						$expr = "substring (option vendor-class-identifier, 0, 10) = \"HTTPClient\" {\n";
3012
					} else {
3013
						$expr = "option arch = {$pxe[0]} {\n";
3014
					}
3015 e5395534 Viktor G
					if (!$pxeif) {
3016 b63b534c Christian McDonald
						$dhcpdconf .= "	if " . $expr;
3017 e5395534 Viktor G
						$pxeif = true;
3018
					} else {
3019 b63b534c Christian McDonald
						$dhcpdconf .= " else if " . $expr;
3020 e5395534 Viktor G
					}
3021
					$dhcpdconf .= "		filename \"{$pxe[1]}\";\n";
3022
					$dhcpdconf .= "	}";
3023
				}
3024
				if ($filename) {
3025
					$dhcpdconf .= " else {\n";
3026
					$dhcpdconf .= "		filename \"{$filename}\";\n";
3027
					$dhcpdconf .= "	}";
3028
				}
3029
				$dhcpdconf .= "\n\n";
3030
			} elseif (!empty($filename)) {
3031 568fdc9f Viktor G
				$dhcpdconf .= "	filename \"{$filename}\";\n";
3032 4e9cd828 Seth Mos
			}
3033 568fdc9f Viktor G
			unset($filename);
3034 fdb116a9 Donald A. Cupp Jr
			if (!empty($dhcpifconf['rootpath'])) {
3035 ca126e03 Martin Fuchs
				$dhcpdconf .= "	option root-path \"{$dhcpifconf['rootpath']}\";\n";
3036 cba980f6 jim-p
			}
3037 4e9cd828 Seth Mos
		}
3038 107e8acc Ovidiu Predescu
3039 5b237745 Scott Ullrich
		$dhcpdconf .= <<<EOD
3040
}
3041
3042
EOD;
3043
3044
		/* add static mappings */
3045
		if (is_array($dhcpifconf['staticmap'])) {
3046 a25183c5 Scott Ullrich
3047 5b237745 Scott Ullrich
			$i = 0;
3048 e5eba380 Viktor G
			$sm_newzone[] = array();
3049
			$need_sm_ddns_updates = false;
3050 5b237745 Scott Ullrich
			foreach ($dhcpifconf['staticmap'] as $sm) {
3051 8645d4c2 jim-p
				if (empty($sm)) {
3052
					continue;
3053
				}
3054 00318445 Viktor G
				$cid = '';
3055 449f1dd2 Will Boyce
				$dhcpdconf .= "host s_{$dhcpif}_{$i} {\n";
3056
3057 61e047a5 Phil Davis
				if ($sm['mac']) {
3058 35bc0edf reb00tz
					$dhcpdconf .= "	hardware ethernet {$sm['mac']};\n";
3059 61e047a5 Phil Davis
				}
3060 449f1dd2 Will Boyce
3061 61e047a5 Phil Davis
				if ($sm['cid']) {
3062 00318445 Viktor G
					$cid = str_replace('"', '\"', $sm['cid']);
3063
					$dhcpdconf .= "	option dhcp-client-identifier \"{$cid}\";\n";
3064 61e047a5 Phil Davis
				}
3065 5b237745 Scott Ullrich
3066 61e047a5 Phil Davis
				if ($sm['ipaddr']) {
3067 5b237745 Scott Ullrich
					$dhcpdconf .= "	fixed-address {$sm['ipaddr']};\n";
3068 61e047a5 Phil Davis
				}
3069 a25183c5 Scott Ullrich
3070 ad30055f Ermal Lu?i
				if ($sm['hostname']) {
3071
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
3072 46c5b763 pierrepomes
					$dhhostname = str_replace(".", "_", $dhhostname);
3073 2f590513 smos
					$dhcpdconf .= "	option host-name \"{$dhhostname}\";\n";
3074 f0cce276 Ross Williams
					if ((isset($dhcpifconf['ddnsupdate']) || isset($sm['ddnsupdate'])) && (isset($dhcpifconf['ddnsforcehostname']) || isset($sm['ddnsforcehostname']))) {
3075
						$dhcpdconf .= "	ddns-hostname \"{$dhhostname}\";\n";
3076
					}
3077 ad30055f Ermal Lu?i
				}
3078 61e047a5 Phil Davis
				if ($sm['filename']) {
3079 a2578c27 Anthony Wrather
					$dhcpdconf .= "	filename \"{$sm['filename']}\";\n";
3080 61e047a5 Phil Davis
				}
3081 a2578c27 Anthony Wrather
3082 61e047a5 Phil Davis
				if ($sm['rootpath']) {
3083 a2578c27 Anthony Wrather
					$dhcpdconf .= "	option root-path \"{$sm['rootpath']}\";\n";
3084 61e047a5 Phil Davis
				}
3085 80717709 Martin Fuchs
3086 61e047a5 Phil Davis
				if ($sm['gateway'] && ($sm['gateway'] != $dhcpifconf['gateway'])) {
3087 7309ff39 Renato Botelho
					$dhcpdconf .= "	option routers {$sm['gateway']};\n";
3088 61e047a5 Phil Davis
				}
3089 7309ff39 Renato Botelho
3090
				$smdnscfg = "";
3091
3092
				if ($sm['domain'] && ($sm['domain'] != $dhcpifconf['domain'])) {
3093
					$smdnscfg .= "	option domain-name \"{$sm['domain']}\";\n";
3094
				}
3095
3096 61e047a5 Phil Davis
				if (!empty($sm['domainsearchlist']) && ($sm['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) {
3097 7309ff39 Renato Botelho
					$smdnscfg .= "	option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $sm['domainsearchlist'])) . "\";\n";
3098
				}
3099
3100
				if (isset($sm['ddnsupdate'])) {
3101 61e047a5 Phil Davis
					if (($sm['ddnsdomain'] <> "") && ($sm['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) {
3102 16ea962d Viktor G
						$smdnscfg .= "	ddns-domainname \"{$sm['ddnsdomain']}\";\n";
3103 b63b534c Christian McDonald
				 		$need_sm_ddns_updates = true;
3104 61e047a5 Phil Davis
					}
3105 16ea962d Viktor G
					$smdnscfg .= "	ddns-update-style interim;\n";
3106 7309ff39 Renato Botelho
				}
3107
3108
				if (is_array($sm['dnsserver']) && ($sm['dnsserver'][0]) && ($sm['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) {
3109
					$smdnscfg .= "	option domain-name-servers " . join(",", $sm['dnsserver']) . ";\n";
3110
				}
3111
				$dhcpdconf .= "{$smdnscfg}";
3112
3113
				// default-lease-time
3114 61e047a5 Phil Davis
				if ($sm['defaultleasetime'] && ($sm['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) {
3115 7309ff39 Renato Botelho
					$dhcpdconf .= "	default-lease-time {$sm['defaultleasetime']};\n";
3116 61e047a5 Phil Davis
				}
3117 7309ff39 Renato Botelho
3118
				// max-lease-time
3119 61e047a5 Phil Davis
				if ($sm['maxleasetime'] && ($sm['maxleasetime'] != $dhcpifconf['maxleasetime'])) {
3120 7309ff39 Renato Botelho
					$dhcpdconf .= "	max-lease-time {$sm['maxleasetime']};\n";
3121 61e047a5 Phil Davis
				}
3122 7309ff39 Renato Botelho
3123
				// netbios-name*
3124
				if (is_array($sm['winsserver']) && $sm['winsserver'][0] && ($sm['winsserver'][0] != $dhcpifconf['winsserver'][0])) {
3125
					$dhcpdconf .= "	option netbios-name-servers " . join(",", $sm['winsserver']) . ";\n";
3126
					$dhcpdconf .= "	option netbios-node-type 8;\n";
3127
				}
3128
3129
				// ntp-servers
3130 61e047a5 Phil Davis
				if (is_array($sm['ntpserver']) && $sm['ntpserver'][0] && ($sm['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) {
3131 7309ff39 Renato Botelho
					$dhcpdconf .= "	option ntp-servers " . join(",", $sm['ntpserver']) . ";\n";
3132 61e047a5 Phil Davis
				}
3133 7309ff39 Renato Botelho
3134
				// tftp-server-name
3135 61e047a5 Phil Davis
				if (!empty($sm['tftp']) && ($sm['tftp'] != $dhcpifconf['tftp'])) {
3136 7309ff39 Renato Botelho
					$dhcpdconf .= "	option tftp-server-name \"{$sm['tftp']}\";\n";
3137 61e047a5 Phil Davis
				}
3138 7309ff39 Renato Botelho
3139 3dd5090f Viktor Gurov
				// Handle option, number rowhelper values
3140
				$dhcpdconf .= "\n";
3141 5c5a7bc8 Viktor G
				$idx = 0;
3142
				$httpclient = false;
3143 3dd5090f Viktor Gurov
				if (isset($sm['numberoptions']['item']) && is_array($sm['numberoptions']['item'])) {
3144
					foreach ($sm['numberoptions']['item'] as $itemidx => $item) {
3145
						$item_value = base64_decode($item['value']);
3146
						if (empty($item['type']) || $item['type'] == "text") {
3147
							$dhcpdconf .= "	option custom-s_{$dhcpif}_{$i}-{$itemidx} \"{$item_value}\";\n";
3148
						} else {
3149
							$dhcpdconf .= "	option custom-s_{$dhcpif}_{$i}-{$itemidx} {$item_value};\n";
3150
						}
3151
					}
3152 5c5a7bc8 Viktor G
					if (($item['type'] == "text") &&
3153
					    ($item['number'] == 60) &&
3154
					    (base64_decode($item['value']) == "HTTPClient")) {
3155
						$httpclient = true;
3156 b63b534c Christian McDonald
					}
3157 5c5a7bc8 Viktor G
					$idx++;
3158
				}
3159 e5e1e8f9 jim-p
				if (!empty($sm['uefihttpboot']) && isset($sm['netboot']) && !$httpclient) {
3160 5c5a7bc8 Viktor G
					$dhcpdconf .= "	option custom-s_{$dhcpif}_{$i}-{$idx} \"HTTPClient\";\n";
3161 3dd5090f Viktor Gurov
				}
3162
3163
				// ldap-server
3164
				if (!empty($sm['ldap']) && ($sm['ldap'] != $dhcpifconf['ldap'])) {
3165
					$dhcpdconf .= "	option ldap-server \"{$sm['ldap']}\";\n";
3166
				}
3167
3168
				// net boot information
3169
				if (isset($sm['netboot'])) {
3170
					if ($sm['nextserver'] <> "") {
3171
						$dhcpdconf .= "	next-server {$sm['nextserver']};\n";
3172
					}
3173 c7599055 Viktor G
3174
					$pxe_files = array();
3175
					if (!empty($sm['filename'])) {
3176
						$filename = $sm['filename'];
3177
					}
3178 5c5a7bc8 Viktor G
					if (!empty($sm['uefihttpboot'])) {
3179
						$pxe_files[] = array('HTTPClient', $sm['uefihttpboot']);
3180
					}
3181 c7599055 Viktor G
					if (!empty($sm['filename32'])) {
3182
						$pxe_files[] = array('00:06', $sm['filename32']);
3183
					}
3184
					if (!empty($sm['filename64'])) {
3185
						$pxe_files[] = array('00:07', $sm['filename64']);
3186
						$pxe_files[] = array('00:09', $sm['filename64']);
3187
					}
3188
					if (!empty($sm['filename32arm'])) {
3189
						$pxe_files[] = array('00:0a', $sm['filename32arm']);
3190
					}
3191
					if (!empty($sm['filename64arm'])) {
3192
						$pxe_files[] = array('00:0b', $sm['filename64arm']);
3193
					}
3194
3195
					$pxeif = false;
3196
					if (is_array($pxe_files) && !empty($pxe_files)) {
3197
						foreach ($pxe_files as $pxe) {
3198 5c5a7bc8 Viktor G
							if ($pxe[0] == 'HTTPClient') {
3199
								$expr = "substring (option vendor-class-identifier, 0, 10) = \"HTTPClient\" {\n";
3200
							} else {
3201
								$expr = "option arch = {$pxe[0]} {\n";
3202
							}
3203 c7599055 Viktor G
							if (!$pxeif) {
3204 b63b534c Christian McDonald
								$dhcpdconf .= "	if " . $expr;
3205 c7599055 Viktor G
								$pxeif = true;
3206
							} else {
3207 b63b534c Christian McDonald
								$dhcpdconf .= " else if " . $expr;
3208 c7599055 Viktor G
							}
3209
							$dhcpdconf .= "		filename \"{$pxe[1]}\";\n";
3210
							$dhcpdconf .= "	}";
3211
						}
3212
						if ($filename) {
3213
							$dhcpdconf .= " else {\n";
3214
							$dhcpdconf .= "		filename \"{$filename}\";\n";
3215
							$dhcpdconf .= "	}";
3216
						}
3217
						$dhcpdconf .= "\n\n";
3218
					} elseif (!empty($filename)) {
3219 568fdc9f Viktor G
						$dhcpdconf .= "	filename \"{$filename}\";\n";
3220 3dd5090f Viktor Gurov
					}
3221 568fdc9f Viktor G
					unset($filename);
3222 3dd5090f Viktor Gurov
					if (!empty($dhcpifconf['rootpath'])) {
3223
						$dhcpdconf .= "	option root-path \"{$sm['rootpath']}\";\n";
3224
					}
3225
				}
3226
3227 5b237745 Scott Ullrich
				$dhcpdconf .= "}\n";
3228 35bc0edf reb00tz
3229 e5eba380 Viktor G
				// add zone DDNS key/server to $ddns_zone[] if required
3230
				if ($need_sm_ddns_updates) {
3231
					$ddnsduplicate = false;
3232
					foreach ($ddns_zones as $ddnszone) {
3233
						if ($ddnszone['domain-name'] == $sm['ddnsdomain']) {
3234
							$ddnsduplicate = true;
3235
						}
3236
					}
3237
					if (!$ddnsduplicate) {
3238 381f213e Viktor G
						$sm_newzone['dns-servers'] = array($sm['ddnsdomainprimary'], $sm['ddnsdomainsecondary']);
3239 e5eba380 Viktor G
						$sm_newzone['domain-name'] = $sm['ddnsdomain'];
3240
						$sm_newzone['ddnsdomainkeyname'] = $sm['ddnsdomainkeyname'];
3241
						$sm_newzone['ddnsdomainkeyalgorithm'] = $sm['ddnsdomainkeyalgorithm'];
3242
						$sm_newzone['ddnsdomainkey'] = $sm['ddnsdomainkey'];
3243
						$dhcpdconf .= dhcpdkey($sm_newzone);
3244
						$ddns_zones[] = $sm_newzone;
3245
						$need_ddns_updates = true;
3246
					}
3247
				}
3248
3249 35bc0edf reb00tz
				// subclass for DHCP limiting
3250
				if (!empty($sm['mac'])) {
3251
					// assuming ALL addresses are ethernet hardware type ("1:" prefix)
3252
					$dhcpdconf .= "subclass \"s_{$dhcpif}\" 1:{$sm['mac']};\n";
3253
				}
3254 00318445 Viktor G
				if (!empty($cid)) {
3255
					$dhcpdconf .= "subclass \"s_{$dhcpif}\" \"{$cid}\";\n";
3256 35bc0edf reb00tz
				}
3257
3258
3259 5b237745 Scott Ullrich
				$i++;
3260
			}
3261
		}
3262 a25183c5 Scott Ullrich
3263 6f9b8073 Ermal Luçi
		$dhcpdifs[] = get_real_interface($dhcpif);
3264 61e047a5 Phil Davis
		if ($newzone['domain-name']) {
3265
			if ($need_ddns_updates) {
3266 9fbd8f71 Viktor Gurov
				$newzone['dns-servers'] = array($dhcpifconf['ddnsdomainprimary'], $dhcpifconf['ddnsdomainsecondary']);
3267 7c1747a3 Chris Buechler
				$newzone['ddnsdomainkeyname'] = $dhcpifconf['ddnsdomainkeyname'];
3268 534d7d69 Joeri Capens
				$newzone['ddnsdomainkeyalgorithm'] = $dhcpifconf['ddnsdomainkeyalgorithm'];
3269 7c1747a3 Chris Buechler
				$newzone['ddnsdomainkey'] = $dhcpifconf['ddnsdomainkey'];
3270
				$dhcpdconf .= dhcpdkey($dhcpifconf);
3271 87019fc4 Andres Petralli
			}
3272 3df2dbfd jim-p
			$ddns_zones[] = $newzone;
3273 87019fc4 Andres Petralli
		}
3274 3df2dbfd jim-p
	}
3275
3276
	if ($need_ddns_updates) {
3277
		$dhcpdconf .= "ddns-update-style interim;\n";
3278 3cdef187 Andres Petralli
		$dhcpdconf .= "update-static-leases on;\n";
3279 87019fc4 Andres Petralli
3280 7c1747a3 Chris Buechler
		$dhcpdconf .= dhcpdzones($ddns_zones);
3281 5b237745 Scott Ullrich
	}
3282
3283 abdd01f5 Ermal
	/* write dhcpd.conf */
3284
	if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpd.conf", $dhcpdconf)) {
3285
		printf(gettext("Error: cannot open dhcpd.conf in services_dhcpdv4_configure().%s"), "\n");
3286
		unset($dhcpdconf);
3287
		return 1;
3288
	}
3289 928d4416 Ermal
	unset($dhcpdconf);
3290 2fb056d8 Seth Mos
3291
	/* create an empty leases database */
3292 61e047a5 Phil Davis
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases")) {
3293 abdd01f5 Ermal
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases");
3294 61e047a5 Phil Davis
	}
3295 2fb056d8 Seth Mos
3296 b075c1e2 Chris Buechler
	/* make sure there isn't a stale dhcpd.pid file, which can make dhcpd fail to start.   */
3297 61e047a5 Phil Davis
	/* if we get here, dhcpd has been killed and is not started yet                        */
3298 b075c1e2 Chris Buechler
	unlink_if_exists("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid");
3299
3300 2fb056d8 Seth Mos
	/* fire up dhcpd in a chroot */
3301 abdd01f5 Ermal
	if (count($dhcpdifs) > 0) {
3302 2fb056d8 Seth Mos
		mwexec("/usr/local/sbin/dhcpd -user dhcpd -group _dhcp -chroot {$g['dhcpd_chroot_path']} -cf /etc/dhcpd.conf -pf {$g['varrun_path']}/dhcpd.pid " .
3303
			join(" ", $dhcpdifs));
3304
	}
3305
3306 816fef25 Marcos Mendoza
	if (is_platform_booting()) {
3307 2fb056d8 Seth Mos
		print "done.\n";
3308 61e047a5 Phil Davis
	}
3309 2fb056d8 Seth Mos
3310
	return 0;
3311
}
3312
3313 086cf944 Phil Davis
function dhcpdkey($dhcpifconf) {
3314 87019fc4 Andres Petralli
	$dhcpdconf = "";
3315 7e3bdbaa Joeri Capens
	if (!empty($dhcpifconf['ddnsdomainkeyname']) && !empty($dhcpifconf['ddnsdomainkey'])) {
3316
		$algorithm = empty($dhcpifconf['ddnsdomainkeyalgorithm']) ? 'hmac-md5' : $dhcpifconf['ddnsdomainkeyalgorithm'];
3317 07588052 Viktor G
		$dhcpdconf .= "key \"{$dhcpifconf['ddnsdomainkeyname']}\" {\n";
3318 7e3bdbaa Joeri Capens
		$dhcpdconf .= "	algorithm {$algorithm};\n";
3319 87019fc4 Andres Petralli
		$dhcpdconf .= "	secret {$dhcpifconf['ddnsdomainkey']};\n";
3320
		$dhcpdconf .= "}\n";
3321
	}
3322
3323
	return $dhcpdconf;
3324
}
3325
3326 7c1747a3 Chris Buechler
function dhcpdzones($ddns_zones) {
3327 87019fc4 Andres Petralli
	$dhcpdconf = "";
3328
3329
	if (is_array($ddns_zones)) {
3330
		$added_zones = array();
3331
		foreach ($ddns_zones as $zone) {
3332 61e047a5 Phil Davis
			if (!is_array($zone) || empty($zone) || !is_array($zone['dns-servers'])) {
3333 87019fc4 Andres Petralli
				continue;
3334 61e047a5 Phil Davis
			}
3335 87019fc4 Andres Petralli
			$primary = $zone['dns-servers'][0];
3336
			$secondary = empty($zone['dns-servers'][1]) ? "" : $zone['dns-servers'][1];
3337
3338 9fbd8f71 Viktor Gurov
			// Make sure we aren't using any invalid servers.
3339
			if (!is_ipaddr($primary)) {
3340
				if (is_ipaddr($secondary)) {
3341 87019fc4 Andres Petralli
					$primary = $secondary;
3342
					$secondary = "";
3343
				} else {
3344
					continue;
3345
				}
3346
			}
3347
3348
			// We don't need to add zones multiple times.
3349
			if ($zone['domain-name'] && !in_array($zone['domain-name'], $added_zones)) {
3350
				$dhcpdconf .= "zone {$zone['domain-name']}. {\n";
3351 9fbd8f71 Viktor Gurov
				if (is_ipaddrv4($primary)) {
3352
					$dhcpdconf .= "	primary {$primary};\n";
3353
				} else {
3354
					$dhcpdconf .= "	primary6 {$primary};\n";
3355
				}
3356 61e047a5 Phil Davis
				if (is_ipaddrv4($secondary)) {
3357 87019fc4 Andres Petralli
					$dhcpdconf .= "	secondary {$secondary};\n";
3358 9fbd8f71 Viktor Gurov
				} elseif (is_ipaddrv6($secondary)) {
3359
					$dhcpdconf .= "	secondary6 {$secondary};\n";
3360 61e047a5 Phil Davis
				}
3361 7c1747a3 Chris Buechler
				if ($zone['ddnsdomainkeyname'] <> "" && $zone['ddnsdomainkey'] <> "") {
3362 07588052 Viktor G
					$dhcpdconf .= "	key \"{$zone['ddnsdomainkeyname']}\";\n";
3363 61e047a5 Phil Davis
				}
3364 87019fc4 Andres Petralli
				$dhcpdconf .= "}\n";
3365
				$added_zones[] = $zone['domain-name'];
3366
			}
3367
			if ($zone['ptr-domain'] && !in_array($zone['ptr-domain'], $added_zones)) {
3368 e733f5b2 Viktor G
				$dhcpdconf .= "zone {$zone['ptr-domain']}. {\n";
3369 9fbd8f71 Viktor Gurov
				if (is_ipaddrv4($primary)) {
3370
					$dhcpdconf .= "	primary {$primary};\n";
3371
				} else {
3372
					$dhcpdconf .= "	primary6 {$primary};\n";
3373
				}
3374 61e047a5 Phil Davis
				if (is_ipaddrv4($secondary)) {
3375 87019fc4 Andres Petralli
					$dhcpdconf .= "	secondary {$secondary};\n";
3376 9fbd8f71 Viktor Gurov
				} elseif (is_ipaddrv6($secondary)) {
3377
					$dhcpdconf .= "	secondary6 {$secondary};\n";
3378 61e047a5 Phil Davis
				}
3379 7c1747a3 Chris Buechler
				if ($zone['ddnsdomainkeyname'] <> "" && $zone['ddnsdomainkey'] <> "") {
3380 07588052 Viktor G
					$dhcpdconf .= "	key \"{$zone['ddnsdomainkeyname']}\";\n";
3381 61e047a5 Phil Davis
				}
3382 87019fc4 Andres Petralli
				$dhcpdconf .= "}\n";
3383
				$added_zones[] = $zone['ptr-domain'];
3384
			}
3385
		}
3386
	}
3387
3388
	return $dhcpdconf;
3389
}
3390
3391 92977616 Ermal
function services_dhcpdv6_configure($blacklist = array()) {
3392 532a1a0e Reid Linnemann
	global $g;
3393 107e8acc Ovidiu Predescu
3394 2568e151 Christian McDonald
	if (g_get('services_dhcp_server_enable') == false) {
3395 2fb056d8 Seth Mos
		return;
3396 61e047a5 Phil Davis
	}
3397 2fb056d8 Seth Mos
3398 532a1a0e Reid Linnemann
	if (config_path_enabled('system','developerspew')) {
3399 2fb056d8 Seth Mos
		$mt = microtime();
3400 9bd56e9d Christian McDonald
		echo "services_dhcpd_configure() being called $mt\n";
3401 61e047a5 Phil Davis
	}
3402 e9ab2ddb smos
3403 892d939e Christian McDonald
	/* kill any running dhcpleases6 */
3404
	if (isvalidpid("{$g['varrun_path']}/dhcpleases6.pid")) {
3405
		killbypid("{$g['varrun_path']}/dhcpleases6.pid");
3406
	}
3407
3408 2fb056d8 Seth Mos
	/* DHCP enabled on any interfaces? */
3409 61e047a5 Phil Davis
	if (!is_dhcpv6_server_enabled()) {
3410 2fb056d8 Seth Mos
		return 0;
3411 61e047a5 Phil Davis
	}
3412 2fb056d8 Seth Mos
3413 9bd56e9d Christian McDonald
	/* bail out if the backend isn't isc */
3414
	if (!dhcp_is_backend('isc')) {
3415
		return 0;
3416
	}
3417
3418 532a1a0e Reid Linnemann
	$syscfg = config_get_path('system');
3419 63d6bb4f Marcos Mendoza
	config_init_path('dhcpdv6');
3420 532a1a0e Reid Linnemann
	$dhcpdv6cfg = config_get_path('dhcpdv6');
3421 2fb056d8 Seth Mos
	$Iflist = get_configured_interface_list();
3422 e9ab2ddb smos
	$Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces());
3423
3424 107e8acc Ovidiu Predescu
3425 816fef25 Marcos Mendoza
	if (is_platform_booting()) {
3426 2fb056d8 Seth Mos
		echo "Starting DHCPv6 service...";
3427 61e047a5 Phil Davis
	} else {
3428 2fb056d8 Seth Mos
		sleep(1);
3429 61e047a5 Phil Davis
	}
3430 2fb056d8 Seth Mos
3431
	$custoptionsv6 = "";
3432 107e8acc Ovidiu Predescu
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
3433 fae6b2c0 jim-p
		if (empty($dhcpv6ifconf)) {
3434
			continue;
3435
		}
3436 61e047a5 Phil Davis
		if (is_array($dhcpv6ifconf['numberoptions']) && is_array($dhcpv6ifconf['numberoptions']['item'])) {
3437
			foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
3438 2fb056d8 Seth Mos
				$custoptionsv6 .= "option custom-{$dhcpv6if}-{$itemv6idx} code {$itemv6['number']} = text;\n";
3439
			}
3440
		}
3441
	}
3442
3443 61e047a5 Phil Davis
	if (isset($dhcpv6ifconf['netboot']) && !empty($dhcpv6ifconf['bootfile_url'])) {
3444 bd942860 Renato Botelho
		$custoptionsv6 .= "option dhcp6.bootfile-url code 59 = string;\n";
3445 61e047a5 Phil Davis
	}
3446 bd942860 Renato Botelho
3447 2fb056d8 Seth Mos
	$dhcpdv6conf = <<<EOD
3448 107e8acc Ovidiu Predescu
3449 2fb056d8 Seth Mos
option domain-name "{$syscfg['domain']}";
3450
option ldap-server code 95 = text;
3451
option domain-search-list code 119 = text;
3452 547f1e65 Renato Botelho
{$custoptionsv6}
3453 2fb056d8 Seth Mos
default-lease-time 7200;
3454
max-lease-time 86400;
3455
log-facility local7;
3456
one-lease-per-client true;
3457
deny duplicates;
3458
ping-check true;
3459 87019fc4 Andres Petralli
update-conflict-detection false;
3460 2fb056d8 Seth Mos
3461
EOD;
3462
3463 61e047a5 Phil Davis
	if (!isset($dhcpv6ifconf['disableauthoritative'])) {
3464 2fb056d8 Seth Mos
		$dhcpdv6conf .= "authoritative;\n";
3465 61e047a5 Phil Davis
	}
3466 2fb056d8 Seth Mos
3467 61e047a5 Phil Davis
	if (isset($dhcpv6ifconf['alwaysbroadcast'])) {
3468 2fb056d8 Seth Mos
		$dhcpdv6conf .= "always-broadcast on\n";
3469 61e047a5 Phil Davis
	}
3470 2fb056d8 Seth Mos
3471
	$dhcpdv6ifs = array();
3472
3473 693833cb Seth Mos
	$dhcpv6num = 0;
3474 87019fc4 Andres Petralli
	$nsupdate = false;
3475
3476 693833cb Seth Mos
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
3477 fae6b2c0 jim-p
		if (empty($dhcpv6ifconf)) {
3478
			continue;
3479
		}
3480 693833cb Seth Mos
3481 87019fc4 Andres Petralli
		$ddns_zones = array();
3482
3483 532a1a0e Reid Linnemann
		$ifcfgv6 = config_get_path("interfaces/{$dhcpv6if}");
3484 693833cb Seth Mos
3485 50b84727 Viktor G
		if (!isset($dhcpv6ifconf['enable']) || !isset($Iflist[$dhcpv6if]) ||
3486
		    (!isset($ifcfgv6['enable']) && !preg_match("/poes/", $dhcpv6if))) {
3487 693833cb Seth Mos
			continue;
3488 61e047a5 Phil Davis
		}
3489 693833cb Seth Mos
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
3490 50b84727 Viktor G
		if (!is_ipaddrv6($ifcfgipv6) && !preg_match("/poes/", $dhcpv6if)) {
3491 830ea39a Chris Buechler
			continue;
3492
		}
3493 693833cb Seth Mos
		$ifcfgsnv6 = get_interface_subnetv6($dhcpv6if);
3494 d57293a4 Seth Mos
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
3495 e89a17fb Phil Davis
		// We might have some prefix-delegation on WAN (e.g. /48),
3496
		// but then it is split and given out to individual interfaces
3497
		// (LAN, OPT1, OPT2...) as multiple /64 subnets. So the size
3498
		// of each subnet here is always /64.
3499
		$pdlen = 64;
3500 15db02a6 Renato Botelho
3501 3dc73d39 jim-p
		$range_from = $dhcpv6ifconf['range']['from'];
3502
		$range_to = $dhcpv6ifconf['range']['to'];
3503
		if ($ifcfgv6['ipaddrv6'] == 'track6') {
3504
			$range_from = merge_ipv6_delegated_prefix($ifcfgipv6, $range_from, $pdlen);
3505
			$range_to = merge_ipv6_delegated_prefix($ifcfgipv6, $range_to, $pdlen);
3506
		}
3507
3508 0c5cf0df Viktor G
		if (is_ipaddrv6($ifcfgipv6)) {
3509
			$subnet_start = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
3510
			$subnet_end = gen_subnetv6_max($ifcfgipv6, $ifcfgsnv6);
3511 3dc73d39 jim-p
			if ((!is_inrange_v6($range_from, $subnet_start, $subnet_end)) ||
3512
			    (!is_inrange_v6($range_to, $subnet_start, $subnet_end))) {
3513 0c5cf0df Viktor G
				log_error(gettext("The specified range lies outside of the current subnet. Skipping DHCP6 entry."));
3514
				continue;
3515
			}
3516
		}
3517
3518 693833cb Seth Mos
		$dnscfgv6 = "";
3519
3520
		if ($dhcpv6ifconf['domain']) {
3521 3c009080 Seth Mos
			$dnscfgv6 .= "	option domain-name \"{$dhcpv6ifconf['domain']}\";\n";
3522 693833cb Seth Mos
		}
3523 107e8acc Ovidiu Predescu
3524 61e047a5 Phil Davis
		if ($dhcpv6ifconf['domainsearchlist'] <> "") {
3525 afd8177f Florian Apolloner
			$dnscfgv6 .= "	option dhcp6.domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpv6ifconf['domainsearchlist'])) . "\";\n";
3526 61e047a5 Phil Davis
		}
3527 693833cb Seth Mos
3528
		if (isset($dhcpv6ifconf['ddnsupdate'])) {
3529 61e047a5 Phil Davis
			if ($dhcpv6ifconf['ddnsdomain'] <> "") {
3530 3c009080 Seth Mos
				$dnscfgv6 .= "	ddns-domainname \"{$dhcpv6ifconf['ddnsdomain']}\";\n";
3531 693833cb Seth Mos
			}
3532 391d63da Renato Botelho
			if (empty($dhcpv6ifconf['ddnsclientupdates'])) {
3533
				$ddnsclientupdates = 'allow';
3534
			} else {
3535
				$ddnsclientupdates = $dhcpv6ifconf['ddnsclientupdates'];
3536
			}
3537
			$dnscfgv6 .= "	{$ddnsclientupdates} client-updates;\n";
3538 87019fc4 Andres Petralli
			$nsupdate = true;
3539 391d63da Renato Botelho
		} else {
3540
			$dnscfgv6 .= "	do-forward-updates false;\n";
3541 693833cb Seth Mos
		}
3542
3543 09646aef luckman212
		if ($dhcpv6ifconf['dhcp6c-dns'] != 'disabled') {
3544 e26ad76e luckman212
			if (is_array($dhcpv6ifconf['dnsserver']) && ($dhcpv6ifconf['dnsserver'][0])) {
3545 610cbfdc Viktor G
				$dnscfgv6 .= "	option dhcp6.name-servers " . join(",", $dhcpv6ifconf['dnsserver']) . ";\n";
3546 532a1a0e Reid Linnemann
			} else if (((config_path_enabled('dnsmasq')) || config_path_enabled('unbound')) && is_ipaddrv6($ifcfgipv6)) {
3547 610cbfdc Viktor G
				$dnscfgv6 .= "	option dhcp6.name-servers {$ifcfgipv6};\n";
3548 e26ad76e luckman212
			} else if (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
3549
				$dns_arrv6 = array();
3550 9f6432f0 luckman212
				foreach ($syscfg['dnsserver'] as $dnsserver) {
3551 e26ad76e luckman212
					if (is_ipaddrv6($dnsserver)) {
3552 7339f154 Viktor G
						if ($ifcfgv6['ipaddrv6'] == 'track6' &&
3553
						    Net_IPv6::isInNetmask($dnsserver, '::', $pdlen)) {
3554
							$dnsserver = merge_ipv6_delegated_prefix($ifcfgipv6, $dnsserver, $pdlen);
3555
						}
3556 e26ad76e luckman212
						$dns_arrv6[] = $dnsserver;
3557
					}
3558
				}
3559
				if (!empty($dns_arrv6)) {
3560 610cbfdc Viktor G
					$dnscfgv6 .= "	option dhcp6.name-servers " . join(",", $dns_arrv6) . ";\n";
3561 2521266a Seth Mos
				}
3562
			}
3563 e26ad76e luckman212
		} else {
3564 610cbfdc Viktor G
			$dnscfgv6 .= "	#option dhcp6.name-servers --;\n";
3565 693833cb Seth Mos
		}
3566
3567 391d63da Renato Botelho
		if (!is_ipaddrv6($ifcfgipv6)) {
3568
			$ifcfgsnv6 = "64";
3569 3dc73d39 jim-p
			$subnetv6 = gen_subnetv6($range_from, $ifcfgsnv6);
3570 391d63da Renato Botelho
		}
3571
3572
		$dhcpdv6conf .= "subnet6 {$subnetv6}/{$ifcfgsnv6}";
3573
3574
		if (isset($dhcpv6ifconf['ddnsupdate']) &&
3575
		    !empty($dhcpv6ifconf['ddnsdomain'])) {
3576 87019fc4 Andres Petralli
			$newzone = array();
3577 391d63da Renato Botelho
			$newzone['domain-name'] = $dhcpv6ifconf['ddnsdomain'];
3578 9fbd8f71 Viktor Gurov
			$newzone['dns-servers'] = array($dhcpv6ifconf['ddnsdomainprimary'], $dhcpv6ifconf['ddnsdomainsecondary']);
3579 7c1747a3 Chris Buechler
			$newzone['ddnsdomainkeyname'] = $dhcpv6ifconf['ddnsdomainkeyname'];
3580
			$newzone['ddnsdomainkey'] = $dhcpv6ifconf['ddnsdomainkey'];
3581 87019fc4 Andres Petralli
			$ddns_zones[] = $newzone;
3582 391d63da Renato Botelho
			if (isset($dhcpv6ifconf['ddnsreverse'])) {
3583
				$ptr_zones = get_v6_ptr_zones($subnetv6, $ifcfgsnv6);
3584
				foreach ($ptr_zones as $ptr_zone) {
3585
					$reversezone = array();
3586 3b46a9cf Joeri Capens
					$reversezone['ptr-domain'] = $ptr_zone;
3587 9fbd8f71 Viktor Gurov
					$reversezone['dns-servers'] = array($dhcpv6ifconf['ddnsdomainprimary'], $dhcpv6ifconf['ddnsdomainsecondary']);
3588 3b46a9cf Joeri Capens
					$reversezone['ddnsdomainkeyname'] = $dhcpv6ifconf['ddnsdomainkeyname'];
3589
					$reversezone['ddnsdomainkey'] = $dhcpv6ifconf['ddnsdomainkey'];
3590 391d63da Renato Botelho
					$ddns_zones[] = $reversezone;
3591
				}
3592
			}
3593 87019fc4 Andres Petralli
		}
3594
3595 1944af41 Ermal
		$dhcpdv6conf .= " {\n";
3596 693833cb Seth Mos
3597 3dc73d39 jim-p
		if (!empty($range_from) && !empty($range_to)) {
3598 5c533d72 Viktor G
			$dhcpdv6conf .= "	range6 {$range_from} {$range_to};\n";
3599
		}
3600 693833cb Seth Mos
3601 5c533d72 Viktor G
		$dhcpdv6conf .= $dnscfgv6;
3602 bfb3e717 Seth Mos
3603 1944af41 Ermal
		if (is_ipaddrv6($dhcpv6ifconf['prefixrange']['from']) && is_ipaddrv6($dhcpv6ifconf['prefixrange']['to'])) {
3604 978b8f50 Chris Buechler
			$dhcpdv6conf .= "	prefix6 {$dhcpv6ifconf['prefixrange']['from']} {$dhcpv6ifconf['prefixrange']['to']} /{$dhcpv6ifconf['prefixrange']['prefixlength']};\n";
3605 bfb3e717 Seth Mos
		}
3606 70da4172 Jean Cyr
		if (is_ipaddrv6($dhcpv6ifconf['dns6ip'])) {
3607 2bf455ca Renato Botelho
			$dns6ip = $dhcpv6ifconf['dns6ip'];
3608
			if ($ifcfgv6['ipaddrv6'] == 'track6' &&
3609 15db02a6 Renato Botelho
			    Net_IPv6::isInNetmask($dns6ip, '::', $pdlen)) {
3610
				$dns6ip = merge_ipv6_delegated_prefix($ifcfgipv6, $dns6ip, $pdlen);
3611 2bf455ca Renato Botelho
			}
3612
			$dhcpdv6conf .= "	option dhcp6.name-servers {$dns6ip};\n";
3613 70da4172 Jean Cyr
		}
3614 fb04e80e Marcos Mendoza
3615
		// DDNS updates must be explicitly configured per subnet - see #13894
3616
		if ($nsupdate) {
3617
			$dhcpdv6conf .= '	ddns-updates ' . (isset($dhcpv6ifconf['ddnsupdate']) ? 'on' : 'off') . ";\n";
3618
		}
3619
3620 61e047a5 Phil Davis
		// default-lease-time
3621
		if ($dhcpv6ifconf['defaultleasetime']) {
3622 693833cb Seth Mos
			$dhcpdv6conf .= "	default-lease-time {$dhcpv6ifconf['defaultleasetime']};\n";
3623 61e047a5 Phil Davis
		}
3624 693833cb Seth Mos
3625
		// max-lease-time
3626 61e047a5 Phil Davis
		if ($dhcpv6ifconf['maxleasetime']) {
3627 693833cb Seth Mos
			$dhcpdv6conf .= "	max-lease-time {$dhcpv6ifconf['maxleasetime']};\n";
3628 61e047a5 Phil Davis
		}
3629 693833cb Seth Mos
3630
		// ntp-servers
3631 4096fe5d smos
		if (is_array($dhcpv6ifconf['ntpserver']) && $dhcpv6ifconf['ntpserver'][0]) {
3632
			$ntpservers = array();
3633 61e047a5 Phil Davis
			foreach ($dhcpv6ifconf['ntpserver'] as $ntpserver) {
3634 2bf455ca Renato Botelho
				if (!is_ipaddrv6($ntpserver)) {
3635
					continue;
3636
				}
3637
				if ($ifcfgv6['ipaddrv6'] == 'track6' &&
3638 15db02a6 Renato Botelho
				    Net_IPv6::isInNetmask($ntpserver, '::', $pdlen)) {
3639
					$ntpserver = merge_ipv6_delegated_prefix($ifcfgipv6, $ntpserver, $pdlen);
3640 61e047a5 Phil Davis
				}
3641 2bf455ca Renato Botelho
				$ntpservers[] = $ntpserver;
3642 4096fe5d smos
			}
3643 61e047a5 Phil Davis
			if (count($ntpservers) > 0) {
3644 2605e6d7 Chris Buechler
				$dhcpdv6conf .= "        option dhcp6.sntp-servers " . join(",", $dhcpv6ifconf['ntpserver']) . ";\n";
3645 61e047a5 Phil Davis
			}
3646 4096fe5d smos
		}
3647 693833cb Seth Mos
		// tftp-server-name
3648 7d504365 smos
		/* Needs ISC DHCPD support
3649 61e047a5 Phil Davis
		 if ($dhcpv6ifconf['tftp'] <> "") {
3650 693833cb Seth Mos
			$dhcpdv6conf .= "	option tftp-server-name \"{$dhcpv6ifconf['tftp']}\";\n";
3651 61e047a5 Phil Davis
		 }
3652 7d504365 smos
		*/
3653 693833cb Seth Mos
3654
		// Handle option, number rowhelper values
3655
		$dhcpdv6conf .= "\n";
3656 b88050bb jim-p
		if (isset($dhcpv6ifconf['numberoptions']['item']) && is_array($dhcpv6ifconf['numberoptions']['item'])) {
3657 61e047a5 Phil Davis
			foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) {
3658 65cce9d7 Renato Botelho
				$itemv6_value = base64_decode($itemv6['value']);
3659
				$dhcpdv6conf .= "	option custom-{$dhcpv6if}-{$itemv6idx} \"{$itemv6_value}\";\n";
3660 693833cb Seth Mos
			}
3661
		}
3662
3663
		// ldap-server
3664 61e047a5 Phil Davis
		if ($dhcpv6ifconf['ldap'] <> "") {
3665 2bf455ca Renato Botelho
			$ldapserver = $dhcpv6ifconf['ldap'];
3666
			if ($ifcfgv6['ipaddrv6'] == 'track6' &&
3667 15db02a6 Renato Botelho
			    Net_IPv6::isInNetmask($ldapserver, '::', $pdlen)) {
3668
				$ldapserver = merge_ipv6_delegated_prefix($ifcfgipv6, $ldapserver, $pdlen);
3669 2bf455ca Renato Botelho
			}
3670
			$dhcpdv6conf .= "	option ldap-server \"{$ldapserver}\";\n";
3671 61e047a5 Phil Davis
		}
3672 693833cb Seth Mos
3673
		// net boot information
3674 61e047a5 Phil Davis
		if (isset($dhcpv6ifconf['netboot'])) {
3675 bd942860 Renato Botelho
			if (!empty($dhcpv6ifconf['bootfile_url'])) {
3676
				$dhcpdv6conf .= "	option dhcp6.bootfile-url \"{$dhcpv6ifconf['bootfile_url']}\";\n";
3677 abdd01f5 Ermal
			}
3678
		}
3679 107e8acc Ovidiu Predescu
3680 1c903aa4 Ermal
		$dhcpdv6conf .= "}\n";
3681 693833cb Seth Mos
3682
		/* add static mappings */
3683 2fb056d8 Seth Mos
		/* Needs to use DUID */
3684 693833cb Seth Mos
		if (is_array($dhcpv6ifconf['staticmap'])) {
3685
			$i = 0;
3686
			foreach ($dhcpv6ifconf['staticmap'] as $sm) {
3687 8645d4c2 jim-p
				if (empty($sm)) {
3688
					continue;
3689
				}
3690 693833cb Seth Mos
				$dhcpdv6conf .= <<<EOD
3691
host s_{$dhcpv6if}_{$i} {
3692 2fb056d8 Seth Mos
	host-identifier option dhcp6.client-id {$sm['duid']};
3693 693833cb Seth Mos
3694
EOD;
3695 61e047a5 Phil Davis
				if ($sm['ipaddrv6']) {
3696 2bf455ca Renato Botelho
					$ipaddrv6 = $sm['ipaddrv6'];
3697
					if ($ifcfgv6['ipaddrv6'] == 'track6') {
3698 15db02a6 Renato Botelho
						$ipaddrv6 = merge_ipv6_delegated_prefix($ifcfgipv6, $ipaddrv6, $pdlen);
3699 2bf455ca Renato Botelho
					}
3700
					$dhcpdv6conf .= "	fixed-address6 {$ipaddrv6};\n";
3701 61e047a5 Phil Davis
				}
3702 693833cb Seth Mos
3703
				if ($sm['hostname']) {
3704
					$dhhostname = str_replace(" ", "_", $sm['hostname']);
3705
					$dhhostname = str_replace(".", "_", $dhhostname);
3706
					$dhcpdv6conf .= "	option host-name {$dhhostname};\n";
3707 bad77fc0 jim-p
					if (isset($dhcpv6ifconf['ddnsupdate']) &&
3708
					    isset($dhcpv6ifconf['ddnsforcehostname'])) {
3709
						$dhcpdv6conf .= "	ddns-hostname \"{$dhhostname}\";\n";
3710
					}
3711 693833cb Seth Mos
				}
3712 61e047a5 Phil Davis
				if ($sm['filename']) {
3713 a2578c27 Anthony Wrather
					$dhcpdv6conf .= "	filename \"{$sm['filename']}\";\n";
3714 61e047a5 Phil Davis
				}
3715 a2578c27 Anthony Wrather
3716 61e047a5 Phil Davis
				if ($sm['rootpath']) {
3717 a2578c27 Anthony Wrather
					$dhcpdv6conf .= "	option root-path \"{$sm['rootpath']}\";\n";
3718 61e047a5 Phil Davis
				}
3719 693833cb Seth Mos
3720
				$dhcpdv6conf .= "}\n";
3721
				$i++;
3722
			}
3723
		}
3724 107e8acc Ovidiu Predescu
3725 391d63da Renato Botelho
		if ($dhcpv6ifconf['ddnsdomain']) {
3726 87019fc4 Andres Petralli
			$dhcpdv6conf .= dhcpdkey($dhcpv6ifconf);
3727 7c1747a3 Chris Buechler
			$dhcpdv6conf .= dhcpdzones($ddns_zones);
3728 87019fc4 Andres Petralli
		}
3729
3730 532a1a0e Reid Linnemann
		if ((config_get_path("dhcpdv6/{$dhcpv6if}/ramode") != "unmanaged") &&
3731
		    (config_path_enabled("interfaces/{$dhcpv6if}") ||
3732 50b84727 Viktor G
		    preg_match("/poes/", $dhcpv6if))) {
3733 61e047a5 Phil Davis
			if (preg_match("/poes/si", $dhcpv6if)) {
3734 e9ab2ddb smos
				/* magic here */
3735
				$dhcpdv6ifs = array_merge($dhcpdv6ifs, get_pppoes_child_interfaces($dhcpv6if));
3736
			} else {
3737 1944af41 Ermal
				$realif = get_real_interface($dhcpv6if, "inet6");
3738 029b377a Ermal
				if (stristr("$realif", "bridge")) {
3739
					$mac = get_interface_mac($realif);
3740
					$v6address = generate_ipv6_from_mac($mac);
3741
					/* Create link local address for bridges */
3742 e9ab2ddb smos
					mwexec("/sbin/ifconfig {$realif} inet6 {$v6address}");
3743
				}
3744 029b377a Ermal
				$realif = escapeshellcmd($realif);
3745
				$dhcpdv6ifs[] = $realif;
3746 656f1763 Seth Mos
			}
3747 de140730 Seth Mos
		}
3748 693833cb Seth Mos
	}
3749
3750 61e047a5 Phil Davis
	if ($nsupdate) {
3751 87019fc4 Andres Petralli
		$dhcpdv6conf .= "ddns-update-style interim;\n";
3752 1a618dc0 Viktor Gurov
		$dhcpdv6conf .= "update-static-leases on;\n";
3753 61e047a5 Phil Davis
	} else {
3754 87019fc4 Andres Petralli
		$dhcpdv6conf .= "ddns-update-style none;\n";
3755
	}
3756
3757 abdd01f5 Ermal
	/* write dhcpdv6.conf */
3758
	if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf", $dhcpdv6conf)) {
3759 1c903aa4 Ermal
		log_error("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n");
3760 816fef25 Marcos Mendoza
		if (is_platform_booting()) {
3761 1c903aa4 Ermal
			printf("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n");
3762 61e047a5 Phil Davis
		}
3763 abdd01f5 Ermal
		unset($dhcpdv6conf);
3764
		return 1;
3765
	}
3766 928d4416 Ermal
	unset($dhcpdv6conf);
3767
3768 693833cb Seth Mos
	/* create an empty leases v6 database */
3769 61e047a5 Phil Davis
	if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases")) {
3770 abdd01f5 Ermal
		@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases");
3771 61e047a5 Phil Davis
	}
3772 107e8acc Ovidiu Predescu
3773 61e047a5 Phil Davis
	/* make sure there isn't a stale dhcpdv6.pid file, which may make dhcpdv6 fail to start.  */
3774
	/* if we get here, dhcpdv6 has been killed and is not started yet                         */
3775
	unlink_if_exists("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid");
3776 b075c1e2 Chris Buechler
3777 68a0e4fc Scott Ullrich
	/* fire up dhcpd in a chroot */
3778 abdd01f5 Ermal
	if (count($dhcpdv6ifs) > 0) {
3779 b63b534c Christian McDonald
		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 " . join(" ", $dhcpdv6ifs));
3780 892d939e Christian McDonald
		mwexec("/usr/local/sbin/dhcpleases6 -c \"/usr/local/bin/php-cgi -f /usr/local/sbin/prefixes.php\" -l {$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases");
3781 2a1bd027 Seth Mos
	}
3782 816fef25 Marcos Mendoza
	if (is_platform_booting()) {
3783 f1a44a3a Carlos Eduardo Ramos
		print gettext("done.") . "\n";
3784 61e047a5 Phil Davis
	}
3785 a25183c5 Scott Ullrich
3786 5b237745 Scott Ullrich
	return 0;
3787
}
3788
3789 1098cb94 Viktor G
function services_igmpproxy_configure($interface='') {
3790 532a1a0e Reid Linnemann
	global $g;
3791 41997fbb Ermal Luci
3792 532a1a0e Reid Linnemann
	if (!empty($interface) && !empty(config_get_path('igmpproxy/igmpentry'))) {
3793
		$igmpinf = "";
3794
		foreach (config_get_path('igmpproxy/igmpentry', []) as $igmpentry) {
3795 1098cb94 Viktor G
			if ($igmpentry['ifname'] == $interface) {
3796
				$igmpinf = true;
3797
				break;
3798
			}
3799
		}
3800
		if (!$igmpinf) {
3801
			return false;
3802
		}
3803
	}
3804 41997fbb Ermal Luci
3805 532a1a0e Reid Linnemann
	if (!config_path_enabled('igmpproxy')) {
3806 1098cb94 Viktor G
		if (isvalidproc("igmpproxy")) {
3807
			log_error(gettext("Stopping IGMP Proxy service."));
3808
			killbyname("igmpproxy");
3809
		}
3810
		return true;
3811 6b3e3bc5 PiBa-NL
	}
3812 532a1a0e Reid Linnemann
	if (count(config_get_path('igmpproxy/igmpentry', [])) == 0) {
3813 1098cb94 Viktor G
		return false;
3814
	}
3815
3816 816fef25 Marcos Mendoza
	if (is_platform_booting()) {
3817 1098cb94 Viktor G
		echo gettext("Starting IGMP Proxy service...");
3818
	}
3819
3820
	if (isvalidproc("igmpproxy")) {
3821
		log_error(gettext("Restarting IGMP Proxy service."));
3822
		killbyname("igmpproxy");
3823 61e047a5 Phil Davis
	}
3824 41997fbb Ermal Luci
3825 61e047a5 Phil Davis
	$iflist = get_configured_interface_list();
3826 f206afb5 Ermal
3827 61e047a5 Phil Davis
	$igmpconf = <<<EOD
3828 41997fbb Ermal Luci
3829
##------------------------------------------------------
3830
## Enable Quickleave mode (Sends Leave instantly)
3831
##------------------------------------------------------
3832
quickleave
3833
3834
EOD;
3835
3836 532a1a0e Reid Linnemann
	foreach (config_get_path('igmpproxy/igmpentry', []) as $igmpcf) {
3837
		if (empty(config_get_path("interfaces/{$igmpcf['ifname']}/ipaddr"))) {
3838 1098cb94 Viktor G
			continue;
3839
		}
3840 61e047a5 Phil Davis
		unset($iflist[$igmpcf['ifname']]);
3841
		$realif = get_real_interface($igmpcf['ifname']);
3842
		if (empty($igmpcf['threshold'])) {
3843
			$threshld = 1;
3844
		} else {
3845
			$threshld = $igmpcf['threshold'];
3846
		}
3847
		$igmpconf .= "phyint {$realif} {$igmpcf['type']} ratelimit 0 threshold {$threshld}\n";
3848
3849
		if ($igmpcf['address'] <> "") {
3850
			$item = explode(" ", $igmpcf['address']);
3851
			foreach ($item as $iww) {
3852
				$igmpconf .= "altnet {$iww}\n";
3853
			}
3854
		}
3855
		$igmpconf .= "\n";
3856 1098cb94 Viktor G
		if ($igmpcf['type'] == 'upstream') {
3857 b63b534c Christian McDonald
		       $upstream = true;
3858 1098cb94 Viktor G
		} else {
3859 b63b534c Christian McDonald
		       $downstream = true;
3860 1098cb94 Viktor G
		}
3861 61e047a5 Phil Davis
	}
3862
	foreach ($iflist as $ifn) {
3863
		$realif = get_real_interface($ifn);
3864
		$igmpconf .= "phyint {$realif} disabled\n";
3865
	}
3866 3bae60be Ermal
	$igmpconf .= "\n";
3867 41997fbb Ermal Luci
3868 1098cb94 Viktor G
	if (!$upstream || !$downstream) {
3869
		log_error(gettext("Could not find upstream or downstream IGMP Proxy interfaces!"));
3870
		return;
3871
	}
3872
3873 2568e151 Christian McDonald
	$igmpfl = fopen(g_get('varetc_path') . "/igmpproxy.conf", "w");
3874 61e047a5 Phil Davis
	if (!$igmpfl) {
3875 1098cb94 Viktor G
		log_error(gettext("Could not write IGMP Proxy configuration file!"));
3876 61e047a5 Phil Davis
		return;
3877
	}
3878
	fwrite($igmpfl, $igmpconf);
3879
	fclose($igmpfl);
3880 928d4416 Ermal
	unset($igmpconf);
3881 41997fbb Ermal Luci
3882 532a1a0e Reid Linnemann
	if (config_path_enabled('syslog','igmpxverbose')) {
3883 a51dd381 Luiz Otavio O Souza
		mwexec_bg("/usr/local/sbin/igmpproxy -v {$g['varetc_path']}/igmpproxy.conf");
3884 2bd0585e Stephen Beaver
	} else {
3885 a51dd381 Luiz Otavio O Souza
		mwexec_bg("/usr/local/sbin/igmpproxy {$g['varetc_path']}/igmpproxy.conf");
3886 2bd0585e Stephen Beaver
	}
3887
3888 1098cb94 Viktor G
	log_error(gettext("Started IGMP Proxy service."));
3889 41997fbb Ermal Luci
3890 816fef25 Marcos Mendoza
	if (is_platform_booting()) {
3891 1098cb94 Viktor G
		echo gettext("done.") . "\n";
3892
	}
3893
3894
	return true;
3895 41997fbb Ermal Luci
}
3896
3897 5b237745 Scott Ullrich
function services_dhcrelay_configure() {
3898 532a1a0e Reid Linnemann
	global $g;
3899 6fa9f38c Renato Botelho
3900 532a1a0e Reid Linnemann
	if (config_path_enabled('system','developerspew')) {
3901 acd910bf Scott Ullrich
		$mt = microtime();
3902 f19d3b7a Scott Ullrich
		echo "services_dhcrelay_configure() being called $mt\n";
3903 acd910bf Scott Ullrich
	}
3904 a25183c5 Scott Ullrich
3905 5b237745 Scott Ullrich
	/* kill any running dhcrelay */
3906
	killbypid("{$g['varrun_path']}/dhcrelay.pid");
3907 a25183c5 Scott Ullrich
3908 63d6bb4f Marcos Mendoza
	config_init_path('dhcrelay');
3909 532a1a0e Reid Linnemann
	$dhcrelaycfg = config_get_path('dhcrelay');
3910 a25183c5 Scott Ullrich
3911 5b237745 Scott Ullrich
	/* DHCPRelay enabled on any interfaces? */
3912 61e047a5 Phil Davis
	if (!isset($dhcrelaycfg['enable'])) {
3913 5b237745 Scott Ullrich
		return 0;
3914 61e047a5 Phil Davis
	}
3915 a25183c5 Scott Ullrich
3916 30169caa Viktor G
	/* Start/Restart DHCP Relay, if a CARP VIP is set, check its status and act
3917
	* appropriately. */
3918
	if (isset($dhcrelaycfg['carpstatusvip']) && ($dhcrelaycfg['carpstatusvip'] != "none")) {
3919
		$status = get_carp_interface_status($dhcrelaycfg['carpstatusvip']);
3920
		switch (strtoupper($status)) {
3921
			// Do not start DHCP Relay service if the VIP is in BACKUP or INIT state.
3922
			case "BACKUP":
3923
			case "INIT":
3924
				log_error("Stopping DHCP Relay (CARP BACKUP/INIT)");
3925
				return 0;
3926
				break;
3927
			// Start the service if the VIP is MASTER state.
3928
			case "MASTER":
3929
			// Assume it's up if the status can't be determined.
3930
			default:
3931
				break;
3932
		}
3933
	}
3934
3935 816fef25 Marcos Mendoza
	if (is_platform_booting()) {
3936 30169caa Viktor G
		echo gettext("Starting DHCP Relay service...");
3937 61e047a5 Phil Davis
	} else {
3938 5b237745 Scott Ullrich
		sleep(1);
3939 61e047a5 Phil Davis
	}
3940 a25183c5 Scott Ullrich
3941 2f06cc3f Ermal
	$iflist = get_configured_interface_list();
3942 a25183c5 Scott Ullrich
3943 f427d68d jim-p
	$dhcrelayifs = array();
3944 2f06cc3f Ermal
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
3945
	foreach ($dhcifaces as $dhcrelayif) {
3946 5285aa84 Viktor G
		if (!isset($iflist[$dhcrelayif])) {
3947 5b237745 Scott Ullrich
			continue;
3948 61e047a5 Phil Davis
		}
3949 a25183c5 Scott Ullrich
3950 5285aa84 Viktor G
		if (get_interface_ip($dhcrelayif)) {
3951 2f06cc3f Ermal
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
3952 61e047a5 Phil Davis
		}
3953 5b237745 Scott Ullrich
	}
3954 f427d68d jim-p
	$dhcrelayifs = array_unique($dhcrelayifs);
3955 5b237745 Scott Ullrich
3956 d7827421 Renato Botelho
	/*
3957
	 * In order for the relay to work, it needs to be active
3958
	 * on the interface in which the destination server sits.
3959
	 */
3960 2f06cc3f Ermal
	$srvips = explode(",", $dhcrelaycfg['server']);
3961 c58879a9 Ermal LUÇI
	if (!is_array($srvips)) {
3962 e8c516a0 Phil Davis
		log_error(gettext("No destination IP has been configured!"));
3963 c58879a9 Ermal LUÇI
		return;
3964 4de8f7ba Phil Davis
	}
3965 f427d68d jim-p
	$srvifaces = array();
3966 d7827421 Renato Botelho
	foreach ($srvips as $srcidx => $srvip) {
3967 9fa31c24 PiBa-NL
		$destif = guess_interface_from_ip($srvip);
3968 5285aa84 Viktor G
		if (!empty($destif) && !is_pseudo_interface($destif)) {
3969 f427d68d jim-p
			$srvifaces[] = $destif;
3970 d7827421 Renato Botelho
		}
3971
	}
3972 f427d68d jim-p
	$srvifaces = array_unique($srvifaces);
3973
3974 a76e6114 jim-p
	/* Check for relays in the same subnet as clients so they can bind for
3975
	 * either direction (up or down) */
3976
	$srvrelayifs = array_intersect($dhcrelayifs, $srvifaces);
3977
3978 f427d68d jim-p
	/* The server interface(s) should not be in this list */
3979
	$dhcrelayifs = array_diff($dhcrelayifs, $srvifaces);
3980 5b237745 Scott Ullrich
3981 a76e6114 jim-p
	/* Remove the dual-role interfaces from up and down lists */
3982
	$srvifaces = array_diff($srvifaces, $srvrelayifs);
3983
	$dhcrelayifs = array_diff($dhcrelayifs, $srvrelayifs);
3984
3985 5b237745 Scott Ullrich
	/* fire up dhcrelay */
3986 a76e6114 jim-p
	if (empty($dhcrelayifs) && empty($srvrelayifs)) {
3987 f427d68d jim-p
		log_error(gettext("No suitable downstream interfaces found for running dhcrelay!"));
3988
		return; /* XXX */
3989
	}
3990 a76e6114 jim-p
	if (empty($srvifaces) && empty($srvrelayifs)) {
3991 f427d68d jim-p
		log_error(gettext("No suitable upstream interfaces found for running dhcrelay!"));
3992 24997966 Ermal
		return; /* XXX */
3993
	}
3994
3995 f427d68d jim-p
	$cmd = "/usr/local/sbin/dhcrelay";
3996 a76e6114 jim-p
3997
	if (!empty($dhcrelayifs)) {
3998
		$cmd .= " -id " . implode(" -id ", $dhcrelayifs);
3999
	}
4000
	if (!empty($srvifaces)) {
4001
		$cmd .= " -iu " . implode(" -iu ", $srvifaces);
4002
	}
4003
	if (!empty($srvrelayifs)) {
4004
		$cmd .= " -i " . implode(" -i ", $srvrelayifs);
4005
	}
4006 5b237745 Scott Ullrich
4007 61e047a5 Phil Davis
	if (isset($dhcrelaycfg['agentoption'])) {
4008 4de8f7ba Phil Davis
		$cmd .= " -a -m replace";
4009 61e047a5 Phil Davis
	}
4010 5b237745 Scott Ullrich
4011 2f06cc3f Ermal
	$cmd .= " " . implode(" ", $srvips);
4012 5b237745 Scott Ullrich
	mwexec($cmd);
4013 928d4416 Ermal
	unset($cmd);
4014 a25183c5 Scott Ullrich
4015 5b237745 Scott Ullrich
	return 0;
4016
}
4017
4018 b7a15cf8 Seth Mos
function services_dhcrelay6_configure() {
4019 532a1a0e Reid Linnemann
	global $g;
4020 6fa9f38c Renato Botelho
4021 532a1a0e Reid Linnemann
	if (config_path_enabled('system','developerspew')) {
4022 b7a15cf8 Seth Mos
		$mt = microtime();
4023 874f099a Phil Davis
		echo "services_dhcrelay6_configure() being called $mt\n";
4024 b7a15cf8 Seth Mos
	}
4025
4026
	/* kill any running dhcrelay */
4027
	killbypid("{$g['varrun_path']}/dhcrelay6.pid");
4028
4029 63d6bb4f Marcos Mendoza
	config_init_path('dhcrelay6');
4030 532a1a0e Reid Linnemann
	$dhcrelaycfg = config_get_path('dhcrelay6');
4031 b7a15cf8 Seth Mos
4032
	/* DHCPv6 Relay enabled on any interfaces? */
4033 61e047a5 Phil Davis
	if (!isset($dhcrelaycfg['enable'])) {
4034 b7a15cf8 Seth Mos
		return 0;
4035 61e047a5 Phil Davis
	}
4036 b7a15cf8 Seth Mos
4037 30169caa Viktor G
	/* Start/Restart DHCPv6 Relay, if a CARP VIP is set, check its status and act
4038
	* appropriately. */
4039
	if (isset($dhcrelaycfg['carpstatusvip']) && ($dhcrelaycfg['carpstatusvip'] != "none")) {
4040
		$status = get_carp_interface_status($dhcrelaycfg['carpstatusvip']);
4041
		switch (strtoupper($status)) {
4042
			// Do not start DHCP Relay service if the VIP is in BACKUP or INIT state.
4043
			case "BACKUP":
4044
			case "INIT":
4045
				log_error("Stopping DHCPv6 Relay (CARP BACKUP/INIT)");
4046
				return 0;
4047
				break;
4048
			// Start the service if the VIP is MASTER state.
4049
			case "MASTER":
4050
			// Assume it's up if the status can't be determined.
4051
			default:
4052
				break;
4053
		}
4054
	}
4055
4056 816fef25 Marcos Mendoza
	if (is_platform_booting()) {
4057 30169caa Viktor G
		echo gettext("Starting DHCPv6 Relay service...");
4058 61e047a5 Phil Davis
	} else {
4059 b7a15cf8 Seth Mos
		sleep(1);
4060 61e047a5 Phil Davis
	}
4061 b7a15cf8 Seth Mos
4062
	$iflist = get_configured_interface_list();
4063
4064
	$dhcifaces = explode(",", $dhcrelaycfg['interface']);
4065
	foreach ($dhcifaces as $dhcrelayif) {
4066 5285aa84 Viktor G
		if (!isset($iflist[$dhcrelayif])) {
4067 b7a15cf8 Seth Mos
			continue;
4068 086cf944 Phil Davis
		}
4069 b7a15cf8 Seth Mos
4070 5285aa84 Viktor G
		if (get_interface_ipv6($dhcrelayif)) {
4071 b7a15cf8 Seth Mos
			$dhcrelayifs[] = get_real_interface($dhcrelayif);
4072 61e047a5 Phil Davis
		}
4073 b7a15cf8 Seth Mos
	}
4074 69dd7088 Michael Tharp
	$dhcrelayifs = array_unique($dhcrelayifs);
4075 b7a15cf8 Seth Mos
4076 d7827421 Renato Botelho
	/*
4077
	 * In order for the relay to work, it needs to be active
4078
	 * on the interface in which the destination server sits.
4079
	 */
4080 b7a15cf8 Seth Mos
	$srvips = explode(",", $dhcrelaycfg['server']);
4081 d7827421 Renato Botelho
	$srvifaces = array();
4082
	foreach ($srvips as $srcidx => $srvip) {
4083 9fa31c24 PiBa-NL
		$destif = guess_interface_from_ip($srvip);
4084 5285aa84 Viktor G
		if (!empty($destif) && !is_pseudo_interface($destif)) {
4085 d7827421 Renato Botelho
			$srvifaces[] = "{$srvip}%{$destif}";
4086
		}
4087 b7a15cf8 Seth Mos
	}
4088
4089
	/* fire up dhcrelay */
4090 61e047a5 Phil Davis
	if (empty($dhcrelayifs) || empty($srvifaces)) {
4091 e8c516a0 Phil Davis
		log_error(gettext("No suitable interface found for running dhcrelay -6!"));
4092 b7a15cf8 Seth Mos
		return; /* XXX */
4093
	}
4094
4095 54a9d71d Phil Davis
	$cmd = "/usr/local/sbin/dhcrelay -6 -pf \"{$g['varrun_path']}/dhcrelay6.pid\"";
4096 69dd7088 Michael Tharp
	foreach ($dhcrelayifs as $dhcrelayif) {
4097
		$cmd .= " -l {$dhcrelayif}";
4098
	}
4099
	foreach ($srvifaces as $srviface) {
4100
		$cmd .= " -u \"{$srviface}\"";
4101
	}
4102 b7a15cf8 Seth Mos
	mwexec($cmd);
4103 928d4416 Ermal
	unset($cmd);
4104 b7a15cf8 Seth Mos
4105
	return 0;
4106
}
4107
4108 181d7c95 Ermal Luçi
function services_dyndns_configure_client($conf) {
4109
4110 61e047a5 Phil Davis
	if (!isset($conf['enable'])) {
4111 65996399 Ermal
		return;
4112 61e047a5 Phil Davis
	}
4113 d2946062 Ermal
4114 181d7c95 Ermal Luçi
	/* load up the dyndns.class */
4115
	require_once("dyndns.class");
4116
4117
	$dns = new updatedns($dnsService = $conf['type'],
4118
		$dnsHost = $conf['host'],
4119 b24a0251 Chris Buechler
		$dnsDomain = $conf['domainname'],
4120 181d7c95 Ermal Luçi
		$dnsUser = $conf['username'],
4121
		$dnsPass = $conf['password'],
4122 3aa55bbe Phil Davis
		$dnsWildcard = $conf['wildcard'],
4123 e10d25b4 CarlGill
		$dnsProxied = $conf['proxied'],
4124 107e8acc Ovidiu Predescu
		$dnsMX = $conf['mx'],
4125 f3b2b2a4 Yehuda Katz
		$dnsIf = "{$conf['interface']}",
4126
		$dnsBackMX = NULL,
4127
		$dnsServer = NULL,
4128
		$dnsPort = NULL,
4129 37f3e704 Matt Corallo
		$dnsUpdateURL = "{$conf['updateurl']}",
4130 cd132e86 Edson Brandi
		$forceUpdate = $conf['force'],
4131 4de8f7ba Phil Davis
		$dnsZoneID = $conf['zoneid'],
4132
		$dnsTTL = $conf['ttl'],
4133 37f3e704 Matt Corallo
		$dnsResultMatch = "{$conf['resultmatch']}",
4134
		$dnsRequestIf = "{$conf['requestif']}",
4135 82caf945 Jaakko Kantojärvi
		$dnsMaxCacheAge = $conf['maxcacheage'],
4136 1e503870 Phil Davis
		$dnsID = "{$conf['id']}",
4137 aa79f351 Sebastian Chrostek
		$dnsVerboseLog = $conf['verboselog'],
4138
		$curlIpresolveV4 = $conf['curl_ipresolve_v4'],
4139 fd331bdc Viktor G
		$curlSslVerifypeer = $conf['curl_ssl_verifypeer'],
4140
		$curlProxy = $conf['curl_proxy']);
4141 181d7c95 Ermal Luçi
}
4142
4143 0be93267 Ermal Lu?i
function services_dyndns_configure($int = "") {
4144 532a1a0e Reid Linnemann
	if (config_path_enabled('system','developerspew')) {
4145 59a63553 Scott Ullrich
		$mt = microtime();
4146
		echo "services_dyndns_configure() being called $mt\n";
4147
	}
4148
4149 532a1a0e Reid Linnemann
	$dyndnscfg = config_get_path('dyndnses/dyndns');
4150
	if (empty($dyndnscfg)) {
4151 b88050bb jim-p
		return 0;
4152
	}
4153 43a9b03d PiBa-NL
	$gwgroups = return_gateway_groups_array(true);
4154 67ee1ec5 Ermal Luçi
	if (is_array($dyndnscfg)) {
4155 816fef25 Marcos Mendoza
		if (is_platform_booting()) {
4156 f1a44a3a Carlos Eduardo Ramos
			echo gettext("Starting DynDNS clients...");
4157 61e047a5 Phil Davis
		}
4158 181d7c95 Ermal Luçi
4159 67ee1ec5 Ermal Luçi
		foreach ($dyndnscfg as $dyndns) {
4160 00d3003d jim-p
			if (!is_array($dyndns) || empty($dyndns)) {
4161
				continue;
4162
			}
4163 e8382f7f Renato Botelho
			/*
4164
			 * If it's using a gateway group, check if interface is
4165
			 * the active gateway for that group
4166
			 */
4167
			$group_int = '';
4168 46583aba Renato Botelho
			$friendly_group_int = '';
4169 3b41c8f3 Pulcov
			$gwgroup_member = false;
4170 e8382f7f Renato Botelho
			if (is_array($gwgroups[$dyndns['interface']])) {
4171
				if (!empty($gwgroups[$dyndns['interface']][0]['vip'])) {
4172
					$group_int = $gwgroups[$dyndns['interface']][0]['vip'];
4173
				} else {
4174
					$group_int = $gwgroups[$dyndns['interface']][0]['int'];
4175 46583aba Renato Botelho
					$friendly_group_int =
4176 271fc45e jim-p
					    convert_real_interface_to_friendly_interface_name(
4177 46583aba Renato Botelho
						$group_int);
4178 3b41c8f3 Pulcov
					if (!empty($int)) {
4179
						$gwgroup_member =
4180
						    interface_gateway_group_member(get_real_interface($int),
4181
						    $dyndns['interface']);
4182
					}
4183 e8382f7f Renato Botelho
				}
4184
			}
4185 3b41c8f3 Pulcov
			if ((empty($int)) || ($int == $dyndns['interface']) || $gwgroup_member ||
4186 46583aba Renato Botelho
			    ($int == $group_int) || ($int == $friendly_group_int)) {
4187 1e503870 Phil Davis
				$dyndns['verboselog'] = isset($dyndns['verboselog']);
4188 d9bdc020 Sebastian Chrostek
				$dyndns['curl_ipresolve_v4'] = isset($dyndns['curl_ipresolve_v4']);
4189
				$dyndns['curl_ssl_verifypeer'] = isset($dyndns['curl_ssl_verifypeer']);
4190 88863533 Robert Resch
				$dyndns['proxied'] = isset($dyndns['proxied']);
4191 591b3cba Thomas Lovén
				$dyndns['wildcard'] = isset($dyndns['wildcard']);
4192 768eb89c smos
				services_dyndns_configure_client($dyndns);
4193
				sleep(1);
4194
			}
4195 67ee1ec5 Ermal Luçi
		}
4196 59a63553 Scott Ullrich
4197 816fef25 Marcos Mendoza
		if (is_platform_booting()) {
4198 f1a44a3a Carlos Eduardo Ramos
			echo gettext("done.") . "\n";
4199 61e047a5 Phil Davis
		}
4200 59a63553 Scott Ullrich
	}
4201
4202
	return 0;
4203
}
4204
4205 0e3aeb6b Phil Davis
function dyndnsCheckIP($int) {
4206 532a1a0e Reid Linnemann
	global $factory_default_checkipservice;
4207 0e3aeb6b Phil Davis
	$ip_address = get_interface_ip($int);
4208 475feb89 Christian McDonald
4209 0e3aeb6b Phil Davis
	if (is_private_ip($ip_address)) {
4210 873e4b28 phildd
		$gateways_status = return_gateways_status(true);
4211
		// If the gateway for this interface is down, then the external check cannot work.
4212
		// Avoid the long wait for the external check to timeout.
4213 17ad89f4 jim-p
		if (stristr(array_get_path($gateways_status, config_get_path("interfaces/{$int}/gateway") . "/status"), "down")) {
4214 873e4b28 phildd
			return "down";
4215 61e047a5 Phil Davis
		}
4216 39d2f39d NOYB
4217 1e7eb5d1 jim-p
		$available_ci_services = config_get_path('checkipservices/checkipservice', []);
4218 39d2f39d NOYB
		// Append the factory default check IP service to the list (if not disabled).
4219 532a1a0e Reid Linnemann
		if (!config_path_enabled('checkipservices','disable_factory_default')) {
4220 1e7eb5d1 jim-p
			$available_ci_services[] = $factory_default_checkipservice;
4221 39d2f39d NOYB
		}
4222
4223
		// Use the first enabled check IP service as the default.
4224 1e7eb5d1 jim-p
		foreach ($available_ci_services as $checkipservice) {
4225 532a1a0e Reid Linnemann
			if (isset($checkipservice['enable'])) {
4226
				$url = $checkipservice['url'];
4227
				$username = $checkipservice['username'];
4228
				$password = $checkipservice['password'];
4229
				$verifysslpeer = isset($checkipservice['verifysslpeer']);
4230
				$curl_proxy = isset($checkipservice['curl_proxy']);
4231
				break;
4232 39d2f39d NOYB
			}
4233
		}
4234
4235
		$hosttocheck = $url;
4236 5244c510 Florian Asche
		$ip_ch = curl_init($hosttocheck);
4237 0e3aeb6b Phil Davis
		curl_setopt($ip_ch, CURLOPT_RETURNTRANSFER, 1);
4238 39d2f39d NOYB
		curl_setopt($ip_ch, CURLOPT_SSL_VERIFYPEER, $verifysslpeer);
4239 e1eee3d2 Renato Botelho
		curl_setopt($ip_ch, CURLOPT_INTERFACE, 'host!' . $ip_address);
4240 181386d6 Florian Asche
		curl_setopt($ip_ch, CURLOPT_CONNECTTIMEOUT, '30');
4241
		curl_setopt($ip_ch, CURLOPT_TIMEOUT, 120);
4242 3c6f29c0 Florian Asche
		curl_setopt($ip_ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
4243 39d2f39d NOYB
		curl_setopt($ip_ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
4244
		curl_setopt($ip_ch, CURLOPT_USERPWD, "{$username}:{$password}");
4245 67fedb90 Viktor G
		if ($curl_proxy) {
4246
			set_curlproxy($ip_ch);
4247
		}
4248 0e3aeb6b Phil Davis
		$ip_result_page = curl_exec($ip_ch);
4249
		curl_close($ip_ch);
4250
		$ip_result_decoded = urldecode($ip_result_page);
4251
		preg_match('=Current IP Address: (.*)</body>=siU', $ip_result_decoded, $matches);
4252 c12f206d Johan van der Vyver
4253
		if ($matches[1]) {
4254
			$parsed_ip = trim($matches[1]);
4255
		} else {
4256
			$parsed_ip = trim($ip_result_decoded);
4257
		}
4258
4259
		preg_match('=((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])=', $parsed_ip, $matches);
4260
		if ($matches[0]) {
4261
			$ip_address = $matches[0];
4262
		}
4263 0e3aeb6b Phil Davis
	}
4264
	return $ip_address;
4265
}
4266
4267 57b5da70 jim-p
function services_dnsmasq_configure($restart_dhcp = true) {
4268 532a1a0e Reid Linnemann
	global $g;
4269 6a01ea44 Bill Marquette
	$return = 0;
4270 107e8acc Ovidiu Predescu
4271 683992fc stilez
	// hard coded args: will be removed to avoid duplication if specified in custom_options
4272
	$standard_args = array(
4273
		"dns-forward-max" => "--dns-forward-max=5000",
4274
		"cache-size" => "--cache-size=10000",
4275
		"local-ttl" => "--local-ttl=1"
4276
	);
4277
4278
4279 532a1a0e Reid Linnemann
	if (config_path_enabled('system','developerspew')) {
4280 acd910bf Scott Ullrich
		$mt = microtime();
4281 f19d3b7a Scott Ullrich
		echo "services_dnsmasq_configure() being called $mt\n";
4282 acd910bf Scott Ullrich
	}
4283
4284 5b237745 Scott Ullrich
	/* kill any running dnsmasq */
4285 61e047a5 Phil Davis
	if (file_exists("{$g['varrun_path']}/dnsmasq.pid")) {
4286 d224df18 Ermal
		sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM");
4287 61e047a5 Phil Davis
	}
4288 5b237745 Scott Ullrich
4289 532a1a0e Reid Linnemann
	if (config_path_enabled('dnsmasq')) {
4290 a25183c5 Scott Ullrich
4291 816fef25 Marcos Mendoza
		if (is_platform_booting()) {
4292 f1a44a3a Carlos Eduardo Ramos
			echo gettext("Starting DNS forwarder...");
4293 61e047a5 Phil Davis
		} else {
4294 5b237745 Scott Ullrich
			sleep(1);
4295 61e047a5 Phil Davis
		}
4296 5b237745 Scott Ullrich
4297 61e047a5 Phil Davis
		/* generate hosts file */
4298 4de8f7ba Phil Davis
		if (system_hosts_generate() != 0) {
4299 61e047a5 Phil Davis
			$return = 1;
4300
		}
4301 cbc6a13f Chris Buechler
4302 5b237745 Scott Ullrich
		$args = "";
4303 a25183c5 Scott Ullrich
4304 532a1a0e Reid Linnemann
		if (config_path_enabled('dnsmasq','regdhcp')) {
4305 fc84b222 Renato Botelho
			$args .= " --dhcp-hostsfile={$g['etc_path']}/hosts ";
4306 0261381a Ermal
		}
4307 107e8acc Ovidiu Predescu
4308 e6c49e3d jim-p
		/* Setup listen port, if non-default */
4309 532a1a0e Reid Linnemann
		$port = config_get_path('dnsmasq/port');
4310
		if (is_port($port)) {
4311
			$args .= " --port={$port} ";
4312 61e047a5 Phil Davis
		}
4313 e6c49e3d jim-p
4314 b4323f39 jim-p
		$listen_addresses = "";
4315 532a1a0e Reid Linnemann
4316
		$interfaces = explode(",", config_get_path('dnsmasq/interface', ""));
4317
		foreach ($interfaces as $interface) {
4318
			$if = get_real_interface($interface);
4319
			if (does_interface_exist($if)) {
4320
				$laddr = get_interface_ip($interface);
4321
				if (is_ipaddrv4($laddr)) {
4322
					$listen_addresses .= " --listen-address={$laddr} ";
4323
				}
4324
				$laddr6 = get_interface_ipv6($interface);
4325
				if (is_ipaddrv6($laddr6) && !config_path_enabled('dnsmasq','strictbind')) {
4326
					/*
4327
					 * XXX: Since dnsmasq does not support link-local address
4328
					 * with scope specified. These checks are being done.
4329
					 */
4330
					if (is_linklocal($laddr6) && strstr($laddr6, "%")) {
4331
						$tmpaddrll6 = explode("%", $laddr6);
4332
						$listen_addresses .= " --listen-address={$tmpaddrll6[0]} ";
4333
					} else {
4334
						$listen_addresses .= " --listen-address={$laddr6} ";
4335 b4323f39 jim-p
					}
4336
				}
4337
			}
4338 532a1a0e Reid Linnemann
		}
4339
		if (!empty($listen_addresses)) {
4340
			$args .= " {$listen_addresses} ";
4341
			if (config_path_enabled('dnsmasq','strictbind')) {
4342
				$args .= " --bind-interfaces ";
4343 b4323f39 jim-p
			}
4344
		}
4345
4346 fc27d3f4 Phil Davis
		/* If selected, then first forward reverse lookups for private IPv4 addresses to nowhere. */
4347 153613e3 Phil Davis
		/* Only make entries for reverse domains that do not have a matching domain override. */
4348 532a1a0e Reid Linnemann
		if (config_path_enabled('dnsmasq','no_private_reverse')) {
4349 0a7985ba Phil Davis
			/* Note: Carrier Grade NAT (CGN) addresses 100.64.0.0/10 are intentionally not here. */
4350
			/* End-users should not be aware of CGN addresses, so reverse lookups for these should not happen. */
4351
			/* Just the pfSense WAN might get a CGN address from an ISP. */
4352 153613e3 Phil Davis
4353
			// Build an array of domain overrides to help in checking for matches.
4354
			$override_a = array();
4355 532a1a0e Reid Linnemann
			foreach (config_get_path('dnsmasq/domainoverrides', []) as $override) {
4356
				$override_a[$override['domain']] = "y";
4357 0a7985ba Phil Davis
			}
4358 153613e3 Phil Davis
4359
			// Build an array of the private reverse lookup domain names
4360
			$reverse_domain_a = array("10.in-addr.arpa", "168.192.in-addr.arpa");
4361
			// Unfortunately the 172.16.0.0/12 range does not map nicely to the in-addr.arpa scheme.
4362 61e047a5 Phil Davis
			for ($subnet_num = 16; $subnet_num < 32; $subnet_num++) {
4363 153613e3 Phil Davis
				$reverse_domain_a[] = "$subnet_num.172.in-addr.arpa";
4364
			}
4365
4366
			// Set the --server parameter to nowhere for each reverse domain name that was not specifically specified in a domain override.
4367 61e047a5 Phil Davis
			foreach ($reverse_domain_a as $reverse_domain) {
4368
				if (!isset($override_a[$reverse_domain])) {
4369 63d6bb4f Marcos Mendoza
					$args .= " --server=/{$reverse_domain}/ ";
4370 61e047a5 Phil Davis
				}
4371 0a7985ba Phil Davis
			}
4372 153613e3 Phil Davis
			unset($override_a);
4373
			unset($reverse_domain_a);
4374 0a7985ba Phil Davis
		}
4375
4376 fc27d3f4 Phil Davis
		/* Setup forwarded domains */
4377 532a1a0e Reid Linnemann
		foreach (config_get_path('dnsmasq/domainoverrides', []) as $override) {
4378
			if ($override['ip'] == "!") {
4379
				$override['ip'] = "";
4380 fc27d3f4 Phil Davis
			}
4381 532a1a0e Reid Linnemann
			$args .= ' --server=/' . $override['domain'] . '/' . $override['ip'];
4382 fc27d3f4 Phil Davis
		}
4383
4384 9a36d901 Viktor G
		/* avoid 127.0.0.1 dns loop,
4385
		 * see https://redmine.pfsense.org/issues/12902 */
4386
		$args .= ' --no-resolv';
4387 840b1370 Marcos Mendoza
		if (!config_path_enabled('dnsmasq', 'no_system_dns')) {
4388
			foreach (get_dns_nameservers(false, true) as $dns) {
4389
				if (($dns != '127.0.0.1') && ($dns != '::1')) {
4390
					$args .= ' --server=' . $dns;
4391
				}
4392 9a36d901 Viktor G
			}
4393
		}
4394
4395 2c46f11f Scott Ullrich
		/* Allow DNS Rebind for forwarded domains */
4396 532a1a0e Reid Linnemann
		if (!config_path_enabled('system/webgui','nodnsrebindcheck')) {
4397
			foreach (config_get_path('dnsmasq/domainoverrides', []) as $override) {
4398
				$args .= ' --rebind-domain-ok=/' . $override['domain'] . '/ ';
4399 2c46f11f Scott Ullrich
			}
4400
		}
4401 91adc5c1 Scott Ullrich
4402 532a1a0e Reid Linnemann
		if (!config_path_enabled('system/webgui', 'nodnsrebindcheck')) {
4403 30d20e7d Scott Ullrich
			$dns_rebind = "--rebind-localhost-ok --stop-dns-rebind";
4404 61e047a5 Phil Davis
		}
4405 30d20e7d Scott Ullrich
4406 532a1a0e Reid Linnemann
		if (config_path_enabled('dnsmasq','strict_order')) {
4407 96ea7162 N0YB
			$args .= " --strict-order ";
4408 f48271e0 jim-p
		} else {
4409
			$args .= " --all-servers ";
4410 96ea7162 N0YB
		}
4411
4412 532a1a0e Reid Linnemann
		if (config_path_enabled('dnsmasq','domain_needed')) {
4413 96ea7162 N0YB
			$args .= " --domain-needed ";
4414
		}
4415
4416 532a1a0e Reid Linnemann
		foreach (preg_split('/\s+/', base64_decode(config_get_path('dnsmasq/custom_options', ""))) as $c) {
4417 29e53480 jim-p
			if (empty($c)) {
4418
				continue;
4419
			}
4420 532a1a0e Reid Linnemann
			$args .= " " . escapeshellarg("--{$c}");
4421
			$p = explode('=', $c);
4422
			if (array_key_exists($p[0], $standard_args)) {
4423
				unset($standard_args[$p[0]]);
4424 683992fc stilez
			}
4425 61e047a5 Phil Davis
		}
4426 41567e06 jim-p
		$args .= ' ' . implode(' ', array_values($standard_args));
4427 8f9bffbc Andrew Thompson
4428 68ce71f2 jim-p
		/* run dnsmasq. Use "-C /dev/null" since we use command line args only (Issue #6730) */
4429 f48271e0 jim-p
		$cmd = "/usr/local/sbin/dnsmasq -C /dev/null {$dns_rebind} {$args}";
4430 b4323f39 jim-p
		//log_error("dnsmasq command: {$cmd}");
4431
		mwexec_bg($cmd);
4432 928d4416 Ermal
		unset($args);
4433 5b237745 Scott Ullrich
4434 61e047a5 Phil Davis
		system_dhcpleases_configure();
4435 0a5a8df9 Warren Baker
4436 816fef25 Marcos Mendoza
		if (is_platform_booting()) {
4437 f1a44a3a Carlos Eduardo Ramos
			echo gettext("done.") . "\n";
4438 61e047a5 Phil Davis
		}
4439 5b237745 Scott Ullrich
	}
4440 a25183c5 Scott Ullrich
4441 816fef25 Marcos Mendoza
	if (!is_platform_booting() && $restart_dhcp) {
4442 4de8f7ba Phil Davis
		if (services_dhcpd_configure() != 0) {
4443 6a01ea44 Bill Marquette
			$return = 1;
4444 61e047a5 Phil Davis
		}
4445 5b237745 Scott Ullrich
	}
4446
4447 6a01ea44 Bill Marquette
	return $return;
4448 5b237745 Scott Ullrich
}
4449
4450 6ac625e8 Viktor G
function services_unbound_configure($restart_dhcp = true, $interface = '') {
4451 532a1a0e Reid Linnemann
	global $g;
4452 175dc861 Warren Baker
	$return = 0;
4453
4454 532a1a0e Reid Linnemann
	if (config_path_enabled('system','developerspew')) {
4455 175dc861 Warren Baker
		$mt = microtime();
4456
		echo "services_unbound_configure() being called $mt\n";
4457
	}
4458
4459 532a1a0e Reid Linnemann
	if (!empty($interface) && config_path_enabled('unbound') &&
4460 fbc8d7d0 Marcos Mendoza
	    !in_array('all', explode(',', config_get_path('unbound/active_interface', 'all'))) &&
4461 532a1a0e Reid Linnemann
	    !in_array($interface, explode(',', config_get_path('unbound/active_interface'))) &&
4462
	    !in_array($interface, explode(',', config_get_path('unbound/outgoing_interface')))) {
4463 6ac625e8 Viktor G
		return $return;
4464
	}
4465
4466 532a1a0e Reid Linnemann
	if (config_path_enabled('unbound')) {
4467 782453b4 jim-p
		require_once('/etc/inc/unbound.inc');
4468
4469
		/* Stop Unbound using TERM */
4470
		if (file_exists("{$g['varrun_path']}/unbound.pid")) {
4471
			sigkillbypid("{$g['varrun_path']}/unbound.pid", "TERM");
4472
		}
4473
4474
		/* If unbound is still running, wait up to 30 seconds for it to terminate. */
4475
		for ($i=1; $i <= 30; $i++) {
4476
			if (is_process_running('unbound')) {
4477
				sleep(1);
4478
			}
4479
		}
4480
4481 6a4635fc BBcan177
		$python_mode = false;
4482 12cbb18a jim-p
		$python_script = basename(config_get_path('unbound/python_script'));
4483 532a1a0e Reid Linnemann
		if (config_path_enabled('unbound','python') && !empty($python_script)) {
4484 6a4635fc BBcan177
			$python_mode = true;
4485
		}
4486
4487
		/* Include any additional functions as defined by python script include file */
4488 532a1a0e Reid Linnemann
		if (file_exists("{$g['unbound_chroot_path']}/{$python_script}_include.inc")) {
4489
			exec("/usr/local/bin/php -l " . escapeshellarg("{$g['unbound_chroot_path']}/{$python_script}_include.inc")
4490 6a4635fc BBcan177
				. " 2>&1", $py_output, $py_retval);
4491
			if ($py_retval == 0) {
4492 532a1a0e Reid Linnemann
				require_once("{$g['unbound_chroot_path']}/{$python_script}_include.inc");
4493 6a4635fc BBcan177
			}
4494
		}
4495
4496 a4ca3a94 BBcan177
		/* DNS Resolver python integration */
4497 d83d2280 jim-p
		if ($python_mode) {
4498
			if (!is_dir("{$g['unbound_chroot_path']}/dev")) {
4499
				safe_mkdir("{$g['unbound_chroot_path']}/dev");
4500
			}
4501 a72b320e Viktor G
			$status = "/sbin/mount | /usr/bin/grep " .  escapeshellarg("{$g['unbound_chroot_path']}/dev");
4502
			if (!trim($status)) {
4503
				exec("/sbin/mount -t devfs devfs " . escapeshellarg("{$g['unbound_chroot_path']}/dev"));
4504
			}
4505 d83d2280 jim-p
		} else {
4506
			if (is_dir("{$g['unbound_chroot_path']}/dev")) {
4507
				exec("/sbin/umount -f " . escapeshellarg("{$g['unbound_chroot_path']}/dev"));
4508
			}
4509
		}
4510 a4ca3a94 BBcan177
		$base_folder = '/usr/local';
4511
		foreach (array('/bin', '/lib') as $dir) {
4512 6a4635fc BBcan177
			$validate = exec("/sbin/mount | /usr/bin/grep " . escapeshellarg("{$g['unbound_chroot_path']}{$base_folder}{$dir} (nullfs") . " 2>&1");
4513
			if ($python_mode) {
4514 a4ca3a94 BBcan177
4515
				// Add DNS Resolver python integration
4516
				if (empty($validate)) {
4517
					if (!is_dir("{$g['unbound_chroot_path']}{$base_folder}{$dir}")) {
4518 14b1c98d BBcan177
						safe_mkdir("{$g['unbound_chroot_path']}{$base_folder}{$dir}");
4519 a4ca3a94 BBcan177
					}
4520 6a4635fc BBcan177
					$output = $retval = '';
4521
					exec("/sbin/mount_nullfs -o ro " . escapeshellarg("/usr/local{$dir}") . ' '
4522
					    . escapeshellarg("{$g['unbound_chroot_path']}{$base_folder}{$dir}") . " 2>&1", $output, $retval);
4523
4524 a4ca3a94 BBcan177
					// Disable Unbound python on mount failure
4525
					if ($retval != 0) {
4526 532a1a0e Reid Linnemann
						config_set_path('unbound/python','');
4527 a4ca3a94 BBcan177
						$log_msg = "[Unbound-pymod]: Disabling Unbound python due to failed mount";
4528
						write_config($log_msg);
4529
						log_error($log_msg);
4530
					}
4531
				}
4532
			}
4533
4534
			// Remove DNS Resolver python integration
4535
			elseif (!empty($validate)) {
4536 14b1c98d BBcan177
				exec("/sbin/umount -t nullfs " . escapeshellarg("{$g['unbound_chroot_path']}{$base_folder}{$dir}") . " 2>&1", $output, $retval);
4537 a4ca3a94 BBcan177
				if ($retval == 0) {
4538
					foreach (array( "/usr/local{$dir}", '/usr/local', '/usr') as $folder) {
4539 2568e151 Christian McDonald
						if (!empty(g_get('unbound_chroot_path')) && g_get('unbound_chroot_path') != '/' && is_dir("{$g['unbound_chroot_path']}{$folder}")) {
4540 6a4635fc BBcan177
							@rmdir(escapeshellarg("{$g['unbound_chroot_path']}{$folder}"));
4541 a4ca3a94 BBcan177
						}
4542
4543
						// Delete remaining subfolders on next loop
4544
						if ($dir == '/bin') {
4545
							break;
4546
						}
4547
					}
4548
				}
4549
				else {
4550
					log_error("[Unbound-pymod]: Failed to unmount!");
4551
				}
4552
			}
4553
		}
4554
4555 563d3c76 Marcos Mendoza
		// unbound has already been stopped at this point
4556 816fef25 Marcos Mendoza
		if (is_platform_booting()) {
4557 5bb1c495 Warren Baker
			echo gettext("Starting DNS Resolver...");
4558 61e047a5 Phil Davis
		}
4559 175dc861 Warren Baker
4560 b3c6783f Renato Botelho
		/* generate hosts file */
4561 4de8f7ba Phil Davis
		if (system_hosts_generate() != 0) {
4562 b3c6783f Renato Botelho
			$return = 1;
4563 61e047a5 Phil Davis
		}
4564 b3c6783f Renato Botelho
4565 881fb186 marjohn56
		/* Check here for dhcp6 complete - wait upto 10 seconds */
4566 532a1a0e Reid Linnemann
		if(config_get_path('interfaces/wan/ipaddrv6') == 'dhcp6') {
4567 881fb186 marjohn56
			$wanif = get_real_interface("wan", "inet6");
4568 816fef25 Marcos Mendoza
			if (is_platform_booting()) {
4569 881fb186 marjohn56
				for ($i=1; $i <= 10; $i++) {
4570 67784aa6 Steve Beaver
					if (!file_exists("/tmp/{$wanif}_dhcp6_complete")) {
4571 881fb186 marjohn56
						log_error(gettext("Unbound start waiting on dhcp6c."));
4572
						sleep(1);
4573
					} else {
4574
						unlink_if_exists("/tmp/{$wanif}_dhcp6_complete");
4575
						log_error(gettext("dhcp6 init complete. Continuing"));
4576
						break;
4577 67784aa6 Steve Beaver
					}
4578 881fb186 marjohn56
				}
4579
			}
4580
		}
4581 67784aa6 Steve Beaver
4582 175dc861 Warren Baker
		sync_unbound_service();
4583 816fef25 Marcos Mendoza
		if (is_platform_booting()) {
4584 881fb186 marjohn56
			log_error(gettext("sync unbound done."));
4585 175dc861 Warren Baker
			echo gettext("done.") . "\n";
4586 61e047a5 Phil Davis
		}
4587 0a5a8df9 Warren Baker
4588 61e047a5 Phil Davis
		system_dhcpleases_configure();
4589 782453b4 jim-p
	} else {
4590
		/* kill Unbound since it should not be enabled */
4591
		if (file_exists("{$g['varrun_path']}/unbound.pid")) {
4592
			sigkillbypid("{$g['varrun_path']}/unbound.pid", "KILL");
4593
		}
4594 175dc861 Warren Baker
	}
4595
4596 816fef25 Marcos Mendoza
	if (!is_platform_booting() && $restart_dhcp) {
4597 4de8f7ba Phil Davis
		if (services_dhcpd_configure() != 0) {
4598 175dc861 Warren Baker
			$return = 1;
4599 61e047a5 Phil Davis
		}
4600 175dc861 Warren Baker
	}
4601
4602
	return $return;
4603
}
4604
4605 dc6a9ddc Viktor G
function services_snmpd_configure($interface='') {
4606 532a1a0e Reid Linnemann
	global $g;
4607
	if (config_path_enabled('system','developerspew')) {
4608 acd910bf Scott Ullrich
		$mt = microtime();
4609 f19d3b7a Scott Ullrich
		echo "services_snmpd_configure() being called $mt\n";
4610
	}
4611 5b237745 Scott Ullrich
4612 532a1a0e Reid Linnemann
	$bindip = config_get_path('snmpd/bindip', "");
4613 dc6a9ddc Viktor G
	if (!empty($interface) &&
4614 532a1a0e Reid Linnemann
	    !empty($bind_ip) &&
4615
	    config_path_enabled('snmpd')) {
4616
		foreach (explode(",", $bindip) as $bind_to_ip) {
4617 dc6a9ddc Viktor G
			if ($bind_to_ip == $interface) {
4618
				$interface_restart = true;
4619
				break;
4620
			}
4621
		}
4622
		if (!$interface_restart) {
4623
			return 0;
4624
		}
4625
	}
4626
4627 5b237745 Scott Ullrich
	/* kill any running snmpd */
4628
	sigkillbypid("{$g['varrun_path']}/snmpd.pid", "TERM");
4629 dd18038e Ermal
	sleep(2);
4630 61e047a5 Phil Davis
	if (is_process_running("bsnmpd")) {
4631 a976fa82 Scott Ullrich
		mwexec("/usr/bin/killall bsnmpd", true);
4632 61e047a5 Phil Davis
	}
4633 5b237745 Scott Ullrich
4634 532a1a0e Reid Linnemann
	if (config_path_enabled('snmpd')) {
4635 a25183c5 Scott Ullrich
4636 816fef25 Marcos Mendoza
		if (is_platform_booting()) {
4637 f1a44a3a Carlos Eduardo Ramos
			echo gettext("Starting SNMP daemon... ");
4638 61e047a5 Phil Davis
		}
4639 5b237745 Scott Ullrich
4640 63b44eed jim-p
		/* Make sure a printcap file exists or else bsnmpd will log errors. See https://redmine.pfsense.org/issues/6838 */
4641
		if (!file_exists('/etc/printcap')) {
4642
			@file_put_contents('/etc/printcap', "# Empty file to prevent bsnmpd from logging errors.\n");
4643
		}
4644
4645 5b237745 Scott Ullrich
		/* generate snmpd.conf */
4646
		$fd = fopen("{$g['varetc_path']}/snmpd.conf", "w");
4647
		if (!$fd) {
4648 4de8f7ba Phil Davis
			printf(gettext("Error: cannot open snmpd.conf in services_snmpd_configure().%s"), "\n");
4649 5b237745 Scott Ullrich
			return 1;
4650
		}
4651 a25183c5 Scott Ullrich
4652 532a1a0e Reid Linnemann
		$snmpdcfg = config_get_path('snmpd');
4653 5b237745 Scott Ullrich
		$snmpdconf = <<<EOD
4654 532a1a0e Reid Linnemann
location := "{$snmpdcfg['syslocation']}"
4655
contact := "{$snmpdcfg['syscontact']}"
4656
read := "{$snmpdcfg['rocommunity']}"
4657 142da8f7 John Fleming
4658
EOD;
4659
4660 e8c516a0 Phil Davis
/* No docs on what write strings do there so disable for now.
4661 532a1a0e Reid Linnemann
		if (config_path_enabled('snmpd','rwenable') && preg_match('/^\S+$/', $snmpdcfg['rwcommunity'])) {
4662 61e047a5 Phil Davis
			$snmpdconf .= <<<EOD
4663 142da8f7 John Fleming
# write string
4664 532a1a0e Reid Linnemann
write := "{$snmpdcfg['rwcommunity']}"
4665 142da8f7 John Fleming
4666
EOD;
4667
		}
4668
*/
4669
4670
4671 532a1a0e Reid Linnemann
		if (config_path_enabled('snmpd','trapenable') && preg_match('/^\S+$/', $snmpdcfg['trapserver'])) {
4672 61e047a5 Phil Davis
			$snmpdconf .= <<<EOD
4673 142da8f7 John Fleming
# SNMP Trap support.
4674 532a1a0e Reid Linnemann
traphost := {$snmpdcfg['trapserver']}
4675
trapport := {$snmpdcfg['trapserverport']}
4676
trap := "{$snmpdcfg['trapstring']}"
4677 142da8f7 John Fleming
4678
4679
EOD;
4680
		}
4681
4682 532a1a0e Reid Linnemann
		$sysDescr = "{$g['product_label']} " .
4683
				  config_get_path('system/hostname') . '.' .config_get_path('system/domain') .
4684 cda2ef35 Renato Botelho do Couto
			" {$g['product_version_string']} " . php_uname("s") .
4685 dadf8ebb jim-p
			" " . php_uname("r") . " " . php_uname("m");
4686 142da8f7 John Fleming
4687
		$snmpdconf .= <<<EOD
4688 d47a8a69 Scott Ullrich
system := 1     # pfSense
4689
%snmpd
4690 dadf8ebb jim-p
sysDescr			= "{$sysDescr}"
4691 d47a8a69 Scott Ullrich
begemotSnmpdDebugDumpPdus       = 2
4692
begemotSnmpdDebugSyslogPri      = 7
4693
begemotSnmpdCommunityString.0.1 = $(read)
4694 142da8f7 John Fleming
4695
EOD;
4696
4697 e8c516a0 Phil Davis
/* No docs on what write strings do there so disable for now.
4698 61e047a5 Phil Davis
		if (isset($config['snmpd']['rwcommunity']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) {
4699
			$snmpdconf .= <<<EOD
4700 142da8f7 John Fleming
begemotSnmpdCommunityString.0.2 = $(write)
4701
4702
EOD;
4703
		}
4704
*/
4705
4706 c7f44ae0 Scott Ullrich
4707 532a1a0e Reid Linnemann
		if (config_path_enabled('snmpd','trapenable') && preg_match('/^\S+$/', config_get_path('snmpd/trapserver'))) {
4708 61e047a5 Phil Davis
			$snmpdconf .= <<<EOD
4709 142da8f7 John Fleming
begemotTrapSinkStatus.[$(traphost)].$(trapport) = 4
4710
begemotTrapSinkVersion.[$(traphost)].$(trapport) = 2
4711
begemotTrapSinkComm.[$(traphost)].$(trapport) = $(trap)
4712
4713
EOD;
4714
		}
4715
4716
4717
		$snmpdconf .= <<<EOD
4718 d47a8a69 Scott Ullrich
begemotSnmpdCommunityDisable    = 1
4719 03ba7a0f John Fleming
4720
EOD;
4721
4722 df8ebedc skrude61
		$bind_to_ips = array();
4723 cd974f08 Viktor G
		$bind_to_ip6s = array();
4724 532a1a0e Reid Linnemann
		if (config_path_enabled('snmpd','bindip')) {
4725
			$ipproto = config_get_path('snmpd/ipprotocol', "4");
4726
			if (strstr($ipproto, "4")) {
4727 cd974f08 Viktor G
				$inet4 = true;
4728
			}
4729 532a1a0e Reid Linnemann
			if (strstr($ipproto, "6")) {
4730 cd974f08 Viktor G
				$inet6 = true;
4731
			}
4732 532a1a0e Reid Linnemann
			foreach (explode(",", config_get_path('snmpd/bindip', "")) as $bind_to_ip) {
4733 df8ebedc skrude61
				if (is_ipaddr($bind_to_ip)) {
4734
					$bind_to_ips[] = $bind_to_ip;
4735
				} else {
4736
					$if = get_real_interface($bind_to_ip);
4737
					if (does_interface_exist($if)) {
4738 cd974f08 Viktor G
						if ($inet4) {
4739
							$bindip = get_interface_ip($bind_to_ip);
4740
							if (is_ipaddrv4($bindip)) {
4741
								$bind_to_ips[] = $bindip;
4742
							}
4743
						}
4744
						if ($inet6) {
4745
							$bindip6 = get_interface_ipv6($bind_to_ip);
4746
							if (is_ipaddrv6($bindip6)) {
4747
								$bind_to_ip6s[] = $bindip6;
4748
							}
4749 df8ebedc skrude61
						}
4750
					}
4751 61e047a5 Phil Davis
				}
4752 c82b2c3f jim-p
			}
4753 7cbad422 Scott Ullrich
		}
4754 cd974f08 Viktor G
		if (!count($bind_to_ips) && $inet4) {
4755 df8ebedc skrude61
			$bind_to_ips = array("0.0.0.0");
4756
		}
4757 cd974f08 Viktor G
		if (!count($bind_to_ip6s) && $inet6) {
4758
			$bind_to_ip6s = array("::");
4759
		}
4760 7cbad422 Scott Ullrich
4761 532a1a0e Reid Linnemann
		$pollport = config_get_path('snmpd/pollport');
4762
		if (is_port($pollport)) {
4763 df8ebedc skrude61
			foreach ($bind_to_ips as $bind_to_ip) {
4764 8ec77040 jim-p
				$snmpdconf .= <<<EOD
4765 532a1a0e Reid Linnemann
begemotSnmpdPortStatus.{$bind_to_ip}.{$pollport} = 1
4766 03ba7a0f John Fleming
4767 cd974f08 Viktor G
EOD;
4768
4769
			}
4770
			foreach ($bind_to_ip6s as $bind_to_ip6) {
4771
				$bind_to_ip6 = ip6_to_asn1($bind_to_ip6);
4772
				$snmpdconf .= <<<EOD
4773 532a1a0e Reid Linnemann
begemotSnmpdTransInetStatus.2.16.{$bind_to_ip6}{$pollport}.1 = 4
4774 cd974f08 Viktor G
4775 03ba7a0f John Fleming
EOD;
4776
4777 df8ebedc skrude61
			}
4778 03ba7a0f John Fleming
		}
4779
4780
		$snmpdconf .= <<<EOD
4781 d47a8a69 Scott Ullrich
begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1
4782
begemotSnmpdLocalPortType."/var/run/snmpd.sock" = 4
4783 142da8f7 John Fleming
4784 03ba7a0f John Fleming
# These are bsnmp macros not php vars.
4785 9cc8c59e Scott Ullrich
sysContact      = $(contact)
4786
sysLocation     = $(location)
4787
sysObjectId     = 1.3.6.1.4.1.12325.1.1.2.1.$(system)
4788 142da8f7 John Fleming
4789 d47a8a69 Scott Ullrich
snmpEnableAuthenTraps = 2
4790 03ba7a0f John Fleming
4791
EOD;
4792
4793 532a1a0e Reid Linnemann
		if (config_path_enabled('snmpd/modules', 'mibii')) {
4794 03ba7a0f John Fleming
			$snmpdconf .= <<<EOD
4795 d47a8a69 Scott Ullrich
begemotSnmpdModulePath."mibII"  = "/usr/lib/snmp_mibII.so"
4796 03ba7a0f John Fleming
4797
EOD;
4798 532a1a0e Reid Linnemann
		}
4799 03ba7a0f John Fleming
4800 532a1a0e Reid Linnemann
		if (config_path_enabled('snmpd/modules', 'netgraph')) {
4801
			$snmpdconf .= <<<EOD
4802 d47a8a69 Scott Ullrich
begemotSnmpdModulePath."netgraph" = "/usr/lib/snmp_netgraph.so"
4803
%netgraph
4804
begemotNgControlNodeName = "snmpd"
4805 03ba7a0f John Fleming
4806
EOD;
4807 532a1a0e Reid Linnemann
		}
4808 03ba7a0f John Fleming
4809 532a1a0e Reid Linnemann
		if (config_path_enabled('snmpd/modules', 'pf')) {
4810
			$snmpdconf .= <<<EOD
4811 d47a8a69 Scott Ullrich
begemotSnmpdModulePath."pf"     = "/usr/lib/snmp_pf.so"
4812 95fb49e8 Seth Mos
4813
EOD;
4814 532a1a0e Reid Linnemann
		}
4815 95fb49e8 Seth Mos
4816 532a1a0e Reid Linnemann
		if (config_path_enabled('snmpd/modules', 'hostres')) {
4817
			$snmpdconf .= <<<EOD
4818 95fb49e8 Seth Mos
begemotSnmpdModulePath."hostres"     = "/usr/lib/snmp_hostres.so"
4819
4820
EOD;
4821 532a1a0e Reid Linnemann
		}
4822 05036071 Renato Botelho
4823 532a1a0e Reid Linnemann
		if (config_path_enabled('snmpd/modules', 'bridge')) {
4824
			$snmpdconf .= <<<EOD
4825 95fb49e8 Seth Mos
begemotSnmpdModulePath."bridge"     = "/usr/lib/snmp_bridge.so"
4826 d47a8a69 Scott Ullrich
# config must end with blank line
4827 5b237745 Scott Ullrich
4828
EOD;
4829 532a1a0e Reid Linnemann
		}
4830
		if (config_path_enabled('snmpd/modules', 'ucd')) {
4831
			$snmpdconf .= <<<EOD
4832 671914b2 jim-p
begemotSnmpdModulePath."ucd"     = "/usr/local/lib/snmp_ucd.so"
4833
4834
EOD;
4835 532a1a0e Reid Linnemann
		}
4836
		if (config_path_enabled('snmpd/modules', 'regex')) {
4837 671914b2 jim-p
				$snmpdconf .= <<<EOD
4838
begemotSnmpdModulePath."regex"     = "/usr/local/lib/snmp_regex.so"
4839
4840
EOD;
4841 03ba7a0f John Fleming
		}
4842 5b237745 Scott Ullrich
4843
		fwrite($fd, $snmpdconf);
4844
		fclose($fd);
4845 928d4416 Ermal
		unset($snmpdconf);
4846 5b237745 Scott Ullrich
4847 853e003a Scott Ullrich
		/* run bsnmpd */
4848
		mwexec("/usr/sbin/bsnmpd -c {$g['varetc_path']}/snmpd.conf" .
4849 2073c2d5 Phil Davis
			" -p {$g['varrun_path']}/snmpd.pid");
4850 5b237745 Scott Ullrich
4851 816fef25 Marcos Mendoza
		if (is_platform_booting()) {
4852 f1a44a3a Carlos Eduardo Ramos
			echo gettext("done.") . "\n";
4853 61e047a5 Phil Davis
		}
4854 5b237745 Scott Ullrich
	}
4855
4856
	return 0;
4857
}
4858
4859 7c9da7be jim-p
function services_dnsupdate_process($int = "", $updatehost = "", $forced = false) {
4860 532a1a0e Reid Linnemann
	global $g;
4861
	if (config_path_enabled('system','developerspew')) {
4862 acd910bf Scott Ullrich
		$mt = microtime();
4863 f19d3b7a Scott Ullrich
		echo "services_dnsupdate_process() being called $mt\n";
4864 acd910bf Scott Ullrich
	}
4865 f19d3b7a Scott Ullrich
4866 a23d7248 Scott Ullrich
	/* Dynamic DNS updating active? */
4867 532a1a0e Reid Linnemann
	if (empty(config_get_path('dnsupdates/dnsupdate'))) {
4868 858e0d8d Renato Botelho
		return 0;
4869
	}
4870 61e047a5 Phil Davis
4871 858e0d8d Renato Botelho
	$notify_text = "";
4872 43a9b03d PiBa-NL
	$gwgroups = return_gateway_groups_array(true);
4873 532a1a0e Reid Linnemann
	foreach (config_get_path('dnsupdates/dnsupdate', []) as $i => $dnsupdate) {
4874 00d3003d jim-p
		if (!is_array($dnsupdate) ||
4875
		    empty($dnsupdate) ||
4876
		    !isset($dnsupdate['enable'])) {
4877 858e0d8d Renato Botelho
			continue;
4878
		}
4879
		/*
4880
		 * If it's using a gateway group, check if interface is
4881
		 * the active gateway for that group
4882
		 */
4883
		$group_int = '';
4884 46583aba Renato Botelho
		$friendly_group_int = '';
4885 3b41c8f3 Pulcov
		$gwgroup_member = false;
4886 858e0d8d Renato Botelho
		if (is_array($gwgroups[$dnsupdate['interface']])) {
4887
			if (!empty($gwgroups[$dnsupdate['interface']][0]['vip'])) {
4888
				$group_int = $gwgroups[$dnsupdate['interface']][0]['vip'];
4889 61e047a5 Phil Davis
			} else {
4890 858e0d8d Renato Botelho
				$group_int = $gwgroups[$dnsupdate['interface']][0]['int'];
4891 46583aba Renato Botelho
				$friendly_group_int =
4892 271fc45e jim-p
				    convert_real_interface_to_friendly_interface_name(
4893 46583aba Renato Botelho
					$group_int);
4894 3b41c8f3 Pulcov
				if (!empty($int)) {
4895
					$gwgroup_member =
4896
					    interface_gateway_group_member(get_real_interface($int),
4897
					    $dnsupdate['interface']);
4898
				}
4899 61e047a5 Phil Davis
			}
4900 858e0d8d Renato Botelho
		}
4901 3b41c8f3 Pulcov
		if (!empty($int) && ($int != $dnsupdate['interface']) && !$gwgroup_member &&
4902 46583aba Renato Botelho
		    ($int != $group_int) && ($int != $friendly_group_int)) {
4903 858e0d8d Renato Botelho
			continue;
4904
		}
4905
		if (!empty($updatehost) && ($updatehost != $dnsupdate['host'])) {
4906
			continue;
4907
		}
4908 61e047a5 Phil Davis
4909 858e0d8d Renato Botelho
		/* determine interface name */
4910
		$if = get_failover_interface($dnsupdate['interface']);
4911 7c9da7be jim-p
4912 900663a4 jim-p
		/* Determine address to update and default binding address */
4913 858e0d8d Renato Botelho
		if (isset($dnsupdate['usepublicip'])) {
4914
			$wanip = dyndnsCheckIP($if);
4915 070fb1a8 Viktor G
			if (is_private_ip($wanip)) {
4916
				log_error(sprintf(gettext(
4917
				    "phpDynDNS: Not updating %s A record because the public IP address cannot be determined."),
4918
				    $dnsupdate['host']));
4919
				continue;
4920
			}
4921 900663a4 jim-p
			$bindipv4 = get_interface_ip($if);
4922 858e0d8d Renato Botelho
		} else {
4923
			$wanip = get_interface_ip($if);
4924 900663a4 jim-p
			$bindipv4 = $wanip;
4925 858e0d8d Renato Botelho
		}
4926 b63b534c Christian McDonald
		if (is_stf_interface($dnsupdate['interface'])) {
4927 30466aef Viktor G
			$wanipv6 = get_interface_ipv6($dnsupdate['interface'] . '_stf');
4928
		} else {
4929
			$wanipv6 = get_interface_ipv6($if);
4930
		}
4931 900663a4 jim-p
		$bindipv6 = $wanipv6;
4932
4933
		/* Handle non-default interface bindings */
4934
		if ($dnsupdate['updatesource'] == "none") {
4935
			/* When empty, the directive will be omitted. */
4936
			$bindipv4 = "";
4937
			$bindipv6 = "";
4938
		} elseif (!empty($dnsupdate['updatesource'])) {
4939
			/* Find the alternate binding address */
4940
			$bindipv4 = get_interface_ip($dnsupdate['updatesource']);
4941 b63b534c Christian McDonald
			if (is_stf_interface($dnsupdate['interface'])) {
4942 30466aef Viktor G
				$bindipv6 = get_interface_ipv6($dnsupdate['updatesource'] . '_stf');
4943
			} else {
4944
				$bindipv6 = get_interface_ipv6($dnsupdate['updatesource']);
4945
			}
4946 900663a4 jim-p
		}
4947
4948
		/* Handle IPv4/IPv6 selection for the update source interface/VIP */
4949
		switch ($dnsupdate['updatesourcefamily']) {
4950
			case "inet":
4951
				$bindip = $bindipv4;
4952
				break;
4953
			case "inet6":
4954
				$bindip = $bindipv6;
4955
				break;
4956
			case "":
4957
			default:
4958
				/* Try IPv4 first, if that is empty, try IPv6. */
4959
				/* Only specify the address if it's present, otherwise omit. */
4960
				if (!empty($bindipv4)) {
4961
					$bindip = $bindipv4;
4962
				} elseif (!empty($bindipv6)) {
4963
					$bindip = $bindipv6;
4964
				}
4965
				break;
4966
		}
4967
4968 2568e151 Christian McDonald
		$cacheFile = g_get('conf_path') .
4969 23adb26d Renato Botelho
		    "/dyndns_{$dnsupdate['interface']}_rfc2136_" .
4970
		    escapeshellarg($dnsupdate['host']) .
4971
		    "_{$dnsupdate['server']}.cache";
4972 2568e151 Christian McDonald
		$cacheFilev6 = g_get('conf_path') .
4973 474def89 Renato Botelho
		    "/dyndns_{$dnsupdate['interface']}_rfc2136_" .
4974
		    escapeshellarg($dnsupdate['host']) .
4975
		    "_{$dnsupdate['server']}_v6.cache";
4976 858e0d8d Renato Botelho
		$currentTime = time();
4977
4978
		if (!$wanip && !$wanipv6) {
4979
			continue;
4980
		}
4981
4982
		$keyname = $dnsupdate['keyname'];
4983
		/* trailing dot */
4984
		if (substr($keyname, -1) != ".") {
4985
			$keyname .= ".";
4986
		}
4987
4988
		$hostname = $dnsupdate['host'];
4989
		/* trailing dot */
4990
		if (substr($hostname, -1) != ".") {
4991
			$hostname .= ".";
4992
		}
4993 67ee1ec5 Ermal Luçi
4994 7ca8845b Joeri Capens
		/* write key file */
4995 ab1112ee Joeri Capens
		$algorithm = empty($dnsupdate['keyalgorithm']) ? 'hmac-md5' : $dnsupdate['keyalgorithm'];
4996 7ca8845b Joeri Capens
		$upkey = <<<EOD
4997
key "{$keyname}" {
4998 ab1112ee Joeri Capens
	algorithm {$algorithm};
4999 7ca8845b Joeri Capens
	secret "{$dnsupdate['keydata']}";
5000
};
5001 a23d7248 Scott Ullrich
5002
EOD;
5003 7ca8845b Joeri Capens
		@file_put_contents("{$g['varetc_path']}/nsupdatekey{$i}", $upkey);
5004 67ee1ec5 Ermal Luçi
5005 858e0d8d Renato Botelho
		/* generate update instructions */
5006
		$upinst = "";
5007
		if (!empty($dnsupdate['server'])) {
5008
			$upinst .= "server {$dnsupdate['server']}\n";
5009
		}
5010 7c9da7be jim-p
5011 07bbe19b Viktor G
		if (!empty($dnsupdate['zone'])) {
5012
			$upinst .= "zone {$dnsupdate['zone']}\n";
5013
		}
5014
5015 ed680fda Renato Botelho
		$cachedipv4 = '';
5016
		$cacheTimev4 = 0;
5017 858e0d8d Renato Botelho
		if (file_exists($cacheFile)) {
5018 23adb26d Renato Botelho
			list($cachedipv4, $cacheTimev4) = explode("|",
5019
			    file_get_contents($cacheFile));
5020 858e0d8d Renato Botelho
		}
5021 ed680fda Renato Botelho
		$cachedipv6 = '';
5022
		$cacheTimev6 = 0;
5023 858e0d8d Renato Botelho
		if (file_exists($cacheFilev6)) {
5024 23adb26d Renato Botelho
			list($cachedipv6, $cacheTimev6) = explode("|",
5025
			    file_get_contents($cacheFilev6));
5026 858e0d8d Renato Botelho
		}
5027 7c9da7be jim-p
5028 858e0d8d Renato Botelho
		// 25 Days
5029
		$maxCacheAgeSecs = 25 * 24 * 60 * 60;
5030
		$need_update = false;
5031 819a603c jim-p
5032 858e0d8d Renato Botelho
		/* Update IPv4 if we have it. */
5033
		if (is_ipaddrv4($wanip) && $dnsupdate['recordtype'] != "AAAA") {
5034 23adb26d Renato Botelho
			if (($wanip != $cachedipv4) || $forced ||
5035
			    (($currentTime - $cacheTimev4) > $maxCacheAgeSecs)) {
5036
				$upinst .= "update delete " .
5037
				    "{$dnsupdate['host']}. A\n";
5038
				$upinst .= "update add {$dnsupdate['host']}. " .
5039
				    "{$dnsupdate['ttl']} A {$wanip}\n";
5040 900663a4 jim-p
				if (!empty($bindip)) {
5041
					$upinst .= "local {$bindip}\n";
5042
				}
5043 858e0d8d Renato Botelho
				$need_update = true;
5044
			} else {
5045 23adb26d Renato Botelho
				log_error(sprintf(gettext(
5046
				    "phpDynDNS: Not updating %s A record because the IP address has not changed."),
5047
				    $dnsupdate['host']));
5048 858e0d8d Renato Botelho
			}
5049
		} else {
5050
			@unlink($cacheFile);
5051
			unset($cacheFile);
5052
		}
5053 7c9da7be jim-p
5054 858e0d8d Renato Botelho
		/* Update IPv6 if we have it. */
5055
		if (is_ipaddrv6($wanipv6) && $dnsupdate['recordtype'] != "A") {
5056 23adb26d Renato Botelho
			if (($wanipv6 != $cachedipv6) || $forced ||
5057
			    (($currentTime - $cacheTimev6) > $maxCacheAgeSecs)) {
5058
				$upinst .= "update delete " .
5059
				    "{$dnsupdate['host']}. AAAA\n";
5060
				$upinst .= "update add {$dnsupdate['host']}. " .
5061
				    "{$dnsupdate['ttl']} AAAA {$wanipv6}\n";
5062 858e0d8d Renato Botelho
				$need_update = true;
5063
			} else {
5064 23adb26d Renato Botelho
				log_error(sprintf(gettext(
5065
				    "phpDynDNS: Not updating %s AAAA record because the IPv6 address has not changed."),
5066
				    $dnsupdate['host']));
5067 858e0d8d Renato Botelho
			}
5068
		} else {
5069
			@unlink($cacheFilev6);
5070
			unset($cacheFilev6);
5071
		}
5072 67ee1ec5 Ermal Luçi
5073 858e0d8d Renato Botelho
		$upinst .= "\n";	/* mind that trailing newline! */
5074 107e8acc Ovidiu Predescu
5075 858e0d8d Renato Botelho
		if (!$need_update) {
5076
			continue;
5077
		}
5078
5079 7ca8845b Joeri Capens
		@file_put_contents("{$g['varetc_path']}/nsupdatecmds{$i}", $upinst);
5080 858e0d8d Renato Botelho
		unset($upinst);
5081
		/* invoke nsupdate */
5082 7ca8845b Joeri Capens
		$cmd = "/usr/local/bin/nsupdate -k {$g['varetc_path']}/nsupdatekey{$i}";
5083 23adb26d Renato Botelho
5084 858e0d8d Renato Botelho
		if (isset($dnsupdate['usetcp'])) {
5085
			$cmd .= " -v";
5086 a23d7248 Scott Ullrich
		}
5087 23adb26d Renato Botelho
5088 858e0d8d Renato Botelho
		$cmd .= " {$g['varetc_path']}/nsupdatecmds{$i}";
5089 23adb26d Renato Botelho
5090 858e0d8d Renato Botelho
		if (mwexec($cmd) == 0) {
5091
			if (!empty($cacheFile)) {
5092 23adb26d Renato Botelho
				@file_put_contents($cacheFile,
5093
				    "{$wanip}|{$currentTime}");
5094
				log_error(sprintf(gettext(
5095
				    'phpDynDNS: updating cache file %1$s: %2$s'),
5096
				    $cacheFile, $wanip));
5097
				$notify_text .= sprintf(gettext(
5098
				    'DynDNS updated IP Address (A) for %1$s on %2$s (%3$s) to %4$s'),
5099
				    $dnsupdate['host'],
5100
				    convert_real_interface_to_friendly_descr($if),
5101
				    $if, $wanip) . "\n";
5102 858e0d8d Renato Botelho
			}
5103
			if (!empty($cacheFilev6)) {
5104 23adb26d Renato Botelho
				@file_put_contents($cacheFilev6,
5105
				    "{$wanipv6}|{$currentTime}");
5106
				log_error(sprintf(gettext(
5107
				    'phpDynDNS: updating cache file %1$s: %2$s'),
5108
				    $cacheFilev6, $wanipv6));
5109
				$notify_text .= sprintf(gettext(
5110
				    'DynDNS updated IPv6 Address (AAAA) for %1$s on %2$s (%3$s) to %4$s'),
5111
				    $dnsupdate['host'],
5112
				    convert_real_interface_to_friendly_descr($if),
5113
				    $if, $wanipv6) . "\n";
5114 858e0d8d Renato Botelho
			}
5115
		} else {
5116
			if (!empty($cacheFile)) {
5117 23adb26d Renato Botelho
				log_error(sprintf(gettext(
5118
				    'phpDynDNS: ERROR while updating IP Address (A) for %1$s (%2$s)'),
5119
				    $dnsupdate['host'], $wanip));
5120 858e0d8d Renato Botelho
			}
5121
			if (!empty($cacheFilev6)) {
5122 23adb26d Renato Botelho
				log_error(sprintf(gettext(
5123
				    'phpDynDNS: ERROR while updating IP Address (AAAA) for %1$s (%2$s)'),
5124
				    $dnsupdate['host'], $wanipv6));
5125 858e0d8d Renato Botelho
			}
5126 7c9da7be jim-p
		}
5127 858e0d8d Renato Botelho
		unset($cmd);
5128
	}
5129
5130
	if (!empty($notify_text)) {
5131
		notify_all_remote($notify_text);
5132 a23d7248 Scott Ullrich
	}
5133 c7f44ae0 Scott Ullrich
5134 a23d7248 Scott Ullrich
	return 0;
5135 5b237745 Scott Ullrich
}
5136
5137 1071e028 Scott Ullrich
/* configure cron service */
5138
function configure_cron() {
5139 532a1a0e Reid Linnemann
	global $g;
5140 e7d3fc15 Ermal
5141 ff715efc jim-p
	$crontab_contents = "";
5142 107e8acc Ovidiu Predescu
5143 532a1a0e Reid Linnemann
	if (!empty(config_get_path('cron/item', []))) {
5144 1071e028 Scott Ullrich
		$crontab_contents .= "#\n";
5145 38dccf78 Renato Botelho
		$crontab_contents .= "# pfSense specific crontab entries\n";
5146 61e047a5 Phil Davis
		$crontab_contents .= "# " .gettext("Created:") . " " . date("F j, Y, g:i a") . "\n";
5147 1071e028 Scott Ullrich
		$crontab_contents .= "#\n";
5148 ff715efc jim-p
		$crontab_contents .= "SHELL=/bin/sh\n";
5149
		$crontab_contents .= "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin\n";
5150 1071e028 Scott Ullrich
5151 532a1a0e Reid Linnemann
		$http_proxy = config_get_path('system/proxyurl');
5152
		$http_proxyport = config_get_path('system/proxyport');
5153
		if (!empty($http_proxy)) {
5154
			if (!empty($http_proxyport)) {
5155
				$http_proxy .= ':' . $http_proxyport;
5156 61e047a5 Phil Davis
			}
5157 45419ed4 Steve Wheeler
			$crontab_contents .= "HTTP_PROXY={$http_proxy}\n";
5158 1060378f jim-p
5159 532a1a0e Reid Linnemann
			$proxyuser = config_get_path('system/proxyuser');
5160
			$proxypass = config_get_path('system/proxypass');
5161
			if (!empty($proxyuser) && !empty($proxypass)) {
5162 0c1496a4 Brad Davis
				$crontab_contents .= "HTTP_PROXY_AUTH={$proxyuser}:{$proxypass}\n";
5163 1060378f jim-p
			}
5164 992f60d0 Renato Botelho
		}
5165
5166 532a1a0e Reid Linnemann
		foreach (config_get_path('cron/item', []) as $item) {
5167 1071e028 Scott Ullrich
			$crontab_contents .= "\n{$item['minute']}\t";
5168
			$crontab_contents .= "{$item['hour']}\t";
5169
			$crontab_contents .= "{$item['mday']}\t";
5170
			$crontab_contents .= "{$item['month']}\t";
5171
			$crontab_contents .= "{$item['wday']}\t";
5172
			$crontab_contents .= "{$item['who']}\t";
5173
			$crontab_contents .= "{$item['command']}";
5174
		}
5175 107e8acc Ovidiu Predescu
5176 1071e028 Scott Ullrich
		$crontab_contents .= "\n#\n";
5177 ff715efc jim-p
		$crontab_contents .= "# " . gettext("DO NOT EDIT THIS FILE MANUALLY!") . "\n";
5178
		$crontab_contents .= "# " . gettext("Use the cron package or create files in /etc/cron.d/.") . "\n";
5179 1071e028 Scott Ullrich
		$crontab_contents .= "#\n\n";
5180
	}
5181 107e8acc Ovidiu Predescu
5182 1071e028 Scott Ullrich
	/* please maintain the newline at the end of file */
5183
	file_put_contents("/etc/crontab", $crontab_contents);
5184 c2d97111 Ermal
	unset($crontab_contents);
5185 41d507a5 Scott Ullrich
5186 8fe38524 doktornotor
	/* make sure that cron is running and start it if it got killed somehow */
5187
	if (!is_process_running("cron")) {
5188
		exec("cd /tmp && /usr/sbin/cron -s 2>/dev/null");
5189
	} else {
5190 41d507a5 Scott Ullrich
	/* do a HUP kill to force sync changes */
5191 c0020b97 doktornotor
		sigkillbypid("{$g['varrun_path']}/cron.pid", "HUP");
5192 8fe38524 doktornotor
	}
5193 41d507a5 Scott Ullrich
5194 1071e028 Scott Ullrich
}
5195
5196 431484c8 Ryan Wagoner
function upnp_action ($action) {
5197 532a1a0e Reid Linnemann
	global $g;
5198 61e047a5 Phil Davis
	switch ($action) {
5199 431484c8 Ryan Wagoner
		case "start":
5200 c1ac2424 Ermal
			if (file_exists('/var/etc/miniupnpd.conf')) {
5201
				@unlink("{$g['varrun_path']}/miniupnpd.pid");
5202
				mwexec_bg("/usr/local/sbin/miniupnpd -f /var/etc/miniupnpd.conf -P {$g['varrun_path']}/miniupnpd.pid");
5203
			}
5204 431484c8 Ryan Wagoner
			break;
5205
		case "stop":
5206 c1ac2424 Ermal
			killbypid("{$g['varrun_path']}/miniupnpd.pid");
5207 61e047a5 Phil Davis
			while ((int)exec("/bin/pgrep -a miniupnpd | wc -l") > 0) {
5208 3459814a doktornotor
				mwexec('/usr/bin/killall miniupnpd 2>/dev/null', true);
5209 61e047a5 Phil Davis
			}
5210 431484c8 Ryan Wagoner
			mwexec('/sbin/pfctl -aminiupnpd -Fr 2>&1 >/dev/null');
5211
			mwexec('/sbin/pfctl -aminiupnpd -Fn 2>&1 >/dev/null');
5212
			break;
5213
		case "restart":
5214
			upnp_action('stop');
5215
			upnp_action('start');
5216
			break;
5217
	}
5218
}
5219
5220 6f20377b Scott Ullrich
function upnp_start() {
5221 532a1a0e Reid Linnemann
	if (empty(config_get_path('installedpackages/miniupnpd/config'))) {
5222 0c331f1e Ermal Lu?i
		return;
5223 61e047a5 Phil Davis
	}
5224 0c331f1e Ermal Lu?i
5225 374dd9fe jim-p
	if (config_get_path('installedpackages/miniupnpd/config/0/enable') == 'on') {
5226 81ca1f72 Self-Hosting-Group
		echo gettext("Starting UPnP IGD & PCP service...");
5227 dcc897e5 Ermal
		require_once('/usr/local/pkg/miniupnpd.inc');
5228
		sync_package_miniupnpd();
5229
		echo "done.\n";
5230 6f20377b Scott Ullrich
	}
5231
}
5232
5233 b2bb4970 jim-p
function install_cron_job($command, $active = false, $minute = "0", $hour = "*", $monthday = "*", $month = "*", $weekday = "*", $who = "root", $write_config = true) {
5234 85405c11 jim-p
	$is_installed = false;
5235 4de8f7ba Phil Davis
	$cron_changed = true;
5236 b2bb4970 jim-p
	$change_message = "";
5237 85405c11 jim-p
5238 63d6bb4f Marcos Mendoza
	config_init_path('cron/item');
5239 85405c11 jim-p
5240 e00916c1 jim-p
	$job = null;
5241
	foreach (config_get_path('cron/item', []) as $idx => $item) {
5242 61e047a5 Phil Davis
		if (strstr($item['command'], $command)) {
5243 85405c11 jim-p
			$is_installed = true;
5244 e00916c1 jim-p
			$job = $idx;
5245 85405c11 jim-p
			break;
5246
		}
5247
	}
5248
5249 61e047a5 Phil Davis
	if ($active) {
5250 85405c11 jim-p
		$cron_item = array();
5251
		$cron_item['minute'] = $minute;
5252
		$cron_item['hour'] = $hour;
5253
		$cron_item['mday'] = $monthday;
5254
		$cron_item['month'] = $month;
5255
		$cron_item['wday'] = $weekday;
5256
		$cron_item['who'] = $who;
5257
		$cron_item['command'] = $command;
5258 61e047a5 Phil Davis
		if (!$is_installed) {
5259 63d6bb4f Marcos Mendoza
			config_set_path('cron/item/', $cron_item);
5260 b2bb4970 jim-p
			$change_message = "Installed cron job for %s";
5261 85405c11 jim-p
		} else {
5262 e00916c1 jim-p
			if (config_get_path("cron/item/{$job}") == $cron_item) {
5263 4de8f7ba Phil Davis
				$cron_changed = false;
5264
			} else {
5265 e00916c1 jim-p
				config_set_path("cron/item/{$job}", $cron_item);
5266 b2bb4970 jim-p
				$change_message = "Updated cron job for %s";
5267 4de8f7ba Phil Davis
			}
5268 85405c11 jim-p
		}
5269
	} else {
5270 61e047a5 Phil Davis
		if ($is_installed == true) {
5271 e00916c1 jim-p
			config_del_path("cron/item/{$job}");
5272 b2bb4970 jim-p
			$change_message = "Removed cron job for %s";
5273 65b9347b PiBa-NL
		} else {
5274
			$cron_changed = false;
5275 85405c11 jim-p
		}
5276
	}
5277 994a0644 Phil Davis
5278 61e047a5 Phil Davis
	if ($cron_changed) {
5279 b2bb4970 jim-p
		/* Optionally write the configuration if this function made changes.
5280
		 * Performing a write_config() in this way can have unintended side effects. See #7146
5281
		 * Base system instances of this function do not need to write, packages may.
5282
		 */
5283
		if ($write_config) {
5284
			write_config(sprintf(gettext($change_message), $command));
5285
		}
5286 994a0644 Phil Davis
		configure_cron();
5287 61e047a5 Phil Davis
	}
5288 85405c11 jim-p
}
5289
5290 693833cb Seth Mos
?>