Project

General

Profile

Download (224 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * interfaces.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2024 Rubicon Communications, LLC (Netgate)
9
 * All rights reserved.
10
 *
11
 * originally based on m0n0wall (http://m0n0.ch/wall)
12
 * Copyright (c) 2004 Manuel Kasper <mk@neon1.net>.
13
 * All rights reserved.
14
 *
15
 * 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
 *
19
 * http://www.apache.org/licenses/LICENSE-2.0
20
 *
21
 * 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
 */
27

    
28
/* include all configuration functions */
29
require_once("config.lib.inc");
30
require_once("globals.inc");
31
require_once("util.inc");
32
require_once("gwlb.inc");
33
require_once("ipsec.inc");
34
require_once("vpn.inc");
35
require_once("Net/IPv6.php");
36

    
37
function interfaces_bring_up($interface) {
38
	if (!$interface) {
39
		log_error(gettext("interfaces_bring_up() was called but no variable defined."));
40
		log_error("Backtrace: " . debug_backtrace());
41
		return;
42
	}
43
	pfSense_interface_flags($interface, IFF_UP);
44
}
45

    
46
/*
47
 * Validate comma-separated list of IPv4 addresses
48
 */
49
function validate_ipv4_list($value) {
50
	$value = trim($value);
51

    
52
	if (empty($value)) {
53
		return false;
54
	}
55

    
56
	$list = explode(',', $value);
57

    
58
	foreach ($list as $ip) {
59
		if (!is_ipaddrv4($ip)) {
60
			return false;
61
		}
62
	}
63

    
64
	return true;
65
}
66

    
67
/*
68
 * Return the interface array
69
 */
70
function get_interface_arr($flush = false) {
71
	global $interface_arr_cache;
72

    
73
	/* If the cache doesn't exist, build it */
74
	if (!isset($interface_arr_cache) or $flush) {
75
		$interface_arr_cache = pfSense_interface_listget();
76
	}
77

    
78
	return $interface_arr_cache;
79
}
80

    
81
/*
82
 * does_interface_exist($interface): return true or false if a interface is
83
 * detected.
84
 */
85
function does_interface_exist($interface, $flush = false) {
86
	if (!$interface) {
87
		return false;
88
	}
89

    
90
	$ints = get_interface_arr($flush);
91
	if (in_array($interface, $ints)) {
92
		return true;
93
	} else {
94
		return false;
95
	}
96
}
97

    
98
/*
99
 * does_vip_exist($vip): return true or false if a vip is
100
 * configured.
101
 */
102
function does_vip_exist($vip) {
103
	if (!$vip) {
104
		return false;
105
	}
106

    
107

    
108
	switch ($vip['mode']) {
109
		case "carp":
110
		case "ipalias":
111
			/* XXX: Make proper checks? */
112
			$realif = get_real_interface($vip['interface']);
113
			if (!does_interface_exist($realif)) {
114
				return false;
115
			}
116
			break;
117
		case "proxyarp":
118
			/* XXX: Implement this */
119
		default:
120
			return false;
121
	}
122

    
123
	$ifacedata = pfSense_getall_interface_addresses($realif);
124

    
125
	if (is_subnetv6("{$vip['subnet']}/{$vip['subnet_bits']}")) {
126
		$comparevip = text_to_compressed_ip6($vip['subnet']);
127
		if (is_linklocal($vip['subnet'])) {
128
			$comparevip .= "%{$realif}";
129
		}
130
		$comparevip .= "/{$vip['subnet_bits']}";
131
	} else {
132
		$comparevip = "{$vip['subnet']}/{$vip['subnet_bits']}";
133
	}
134

    
135
	foreach ($ifacedata as $vipips) {
136
		if ($vipips == $comparevip) {
137
			return true;
138
		}
139
	}
140

    
141
	return false;
142
}
143

    
144
function interfaces_loopback_configure() {
145
	if (is_platform_booting()) {
146
		echo gettext("Configuring loopback interface...");
147
		mute_kernel_msgs();
148
	}
149
	pfSense_interface_setaddress("lo0", "127.0.0.1");
150
	interfaces_bring_up("lo0");
151
	if (is_platform_booting()) {
152
		unmute_kernel_msgs();
153
		echo gettext("done.") . "\n";
154
	}
155
	return 0;
156
}
157

    
158
function vlan_valid_tag($tag = NULL) {
159

    
160
	if ($tag == NULL || empty($tag) ||
161
	    !is_numericint($tag) || intval($tag) < 1 || intval($tag) > 4094) {
162
		return (false);
163
	}
164
	return (true);
165
}
166

    
167
function qinq_inuse($qinq = NULL, $inqtag = NULL) {
168
	if ($qinq == NULL || $inqtag == NULL ||
169
	    !is_array($qinq) || !vlan_valid_tag($inqtag)) {
170
		return (false);
171
	}
172

    
173
        $iflist = get_configured_interface_list(true);
174
        foreach ($iflist as $if) {
175
                if (config_get_path("interfaces/{$if}/if") == qinq_interface($qinq, $inqtag)) {
176
                        return (true);
177
                }
178
        }
179

    
180
        return (false);
181
}
182

    
183
function qinq_interface($qinq = NULL, $inqtag = NULL) {
184

    
185
	if ($qinq == NULL || $inqtag == NULL || !is_array($qinq) ||
186
	    !isset($qinq['if']) || !isset($qinq['tag']) ||
187
	    !vlan_valid_tag($qinq['tag']) || !vlan_valid_tag($inqtag)) {
188
		return (NULL);
189
	}
190
	return ("{$qinq['if']}.{$qinq['tag']}.{$inqtag}");
191
}
192

    
193
function interface_is_qinq($if = NULL) {
194
	if (!$if) {
195
		return (NULL);
196
	}
197

    
198
	/* Check basic format. */
199
	list($qinqif, $vlantag, $inqtag) = explode(".", $if);
200
	if (empty($qinqif) || empty($vlantag) || empty($inqtag) ||
201
	    !vlan_valid_tag($vlantag) || !vlan_valid_tag($inqtag)) {
202
		return (NULL);
203
	}
204

    
205
	foreach (config_get_path('qinqs/qinqentry', []) as $qinq) {
206
		if ("{$qinqif}.{$vlantag}" != $qinq['vlanif']) {
207
			continue;
208
		}
209
		if (empty($qinq['members'])) {
210
			continue;
211
		}
212
		foreach (explode(" ", $qinq['members']) as $tag) {
213
			if ($if == qinq_interface($qinq, $tag)) {
214
				return ($qinq);
215
			}
216
		}
217
	}
218

    
219
	return (NULL);
220
}
221

    
222
function vlan_inuse($vlan) {
223
	if ($vlan == NULL || !is_array($vlan)) {
224
		return (false);
225
	}
226

    
227
	$iflist = get_configured_interface_list(true);
228
	foreach ($iflist as $if) {
229
		if (config_get_path("interfaces/{$if}/if") == $vlan['vlanif']) {
230
			return (true);
231
		}
232
	}
233

    
234
	return (false);
235
}
236

    
237
function interface_is_vlan($if = NULL) {
238
	if ($if == NULL || empty($if) || is_array($if)) {
239
		return (NULL);
240
	}
241

    
242
	/* Check basic format. */
243
	$items = explode(".", $if, 2);
244
	$vlanif = array_shift($items);
245
	$vlantag = array_shift($items);
246
	if (empty($vlanif) || empty($vlantag) || !vlan_valid_tag($vlantag)) {
247
		return (NULL);
248
	}
249

    
250
	/* Find the VLAN interface. */
251
	foreach (config_get_path('vlans/vlan', []) as $vlan) {
252
		if ($if == $vlan['vlanif']) {
253
			return ($vlan);
254
		}
255
	}
256

    
257
	/* Check for the first level tag in QinQ interfaces. */
258
	foreach (config_get_path('qinqs/qinqentry', []) as $qinq) {
259
		if ($if == $qinq['vlanif']) {
260
			return ($qinq);
261
		}
262
	}
263

    
264
	return (NULL);
265
}
266

    
267
function vlan_interface($vlan = NULL) {
268
	if ($vlan == NULL || !is_array($vlan) || !isset($vlan['if']) ||
269
	    !isset($vlan['tag']) || !vlan_valid_tag($vlan['tag'])) {
270
		return (NULL);
271
	}
272
	return ("{$vlan['if']}.{$vlan['tag']}");
273
}
274

    
275
function interface_set_macaddr($interface, $mac_addr) {
276
	if (empty($mac_addr) || !is_macaddr($mac_addr)) {
277
		return;
278
	}
279

    
280
	$current_mac = get_interface_mac($interface);
281

    
282
	/*
283
	 * Don't try to reapply the MAC if it's already applied.
284
	 * When ifconfig link is used, it cycles the interface down/up, which
285
	 * triggers the interface config again, which attempts to apply the
286
	 * MAC again, which cycles the link again...
287
	 */
288
	if ($mac_addr != $current_mac) {
289
		mwexec("/sbin/ifconfig " . escapeshellarg($interface) .
290
		    " link " . escapeshellarg($mac_addr));
291
	}
292
}
293

    
294
function interface_is_parent($if, $parent_check) {
295

    
296
	$parent_array = get_parent_interface($if);
297
	if (count($parent_array) > 1) {
298
		$parent = $parent_array;
299
	} else {
300
		$parent = $parent_array[0];
301
	}
302
	if (is_array($parent)) {
303
		foreach ($parent as $parentif) {
304
			if (strcasecmp($parent_check, $parentif) == 0) {
305
				return (TRUE);
306
			}
307
		}
308
	} else {
309
		$realif = get_real_interface($parent);
310
		if (strcasecmp($parent_check, $realif) == 0) {
311
			return (TRUE);
312
		}
313
	}
314
	return (FALSE);
315
}
316

    
317
function interface_has_clones($if) {
318
	/* Check LAGGs. */
319
	foreach (config_get_path('laggs/lagg', []) as $lagg) {
320
		if (interface_is_parent($lagg['laggif'], $if)) {
321
			return (TRUE);
322
		}
323
	}
324
	/* Check VLANs. */
325
	foreach (config_get_path('vlans/vlan', []) as $vlan) {
326
		if (interface_is_parent($vlan['if'], $if)) {
327
			return (TRUE);
328
		}
329
	}
330
	/* Check bridges. */
331
	foreach (config_get_path('bridges/bridged', []) as $bridge) {
332
		$members = explode(',', $bridge['members']);
333
		foreach ($members as $member) {
334
			if (interface_is_parent($member, $if)) {
335
				return (TRUE);
336
			}
337
		}
338
	}
339

    
340
	return (FALSE);
341
}
342

    
343
/**
344
 * Configure the child VLAN interfaces for a parent interface.
345
 * 
346
 * @param bool $skip_parent_mtu Skip setting the parent interface's MTU
347
 */
348
function interfaces_vlan_configure($parentif = "", $skip_parent_mtu = false) {
349
	$dhcp6c_list = array();
350

    
351
	$vlans = config_get_path('vlans/vlan');
352
	if (is_array($vlans) && count($vlans)) {
353
		// If the parent MTU is being skipped at boot, this has already been shown
354
		if (is_platform_booting() && !$skip_parent_mtu) {
355
			echo gettext("Configuring VLAN interfaces...");
356
		}
357
		foreach ($vlans as $vlan) {
358
			if (empty($vlan['vlanif'])) {
359
				$vlan['vlanif'] = vlan_interface($vlan);
360
			}
361
			if (!empty($parentif) && ($parentif != $vlan['if'])) {
362
				continue;
363
			}
364
			/* configure dhcp6 enabled VLAN interfaces later
365
			 * see https://redmine.pfsense.org/issues/3965 */
366
			$if = convert_real_interface_to_friendly_interface_name($vlan['vlanif']);
367
			if (!empty($if) && config_get_path("interfaces/{$if}/ipaddrv6", "") == "dhcp6") {
368
				$dhcp6c_list[$if] = $vlan;
369
				continue;
370
			}
371

    
372
			/* XXX: Maybe we should report any errors?! */
373
			interface_vlan_configure($vlan, false, $skip_parent_mtu);
374
		}
375
		foreach ($dhcp6c_list as $if => $vlan) {
376
			interface_vlan_configure($vlan, false, $skip_parent_mtu);
377
		}
378
		/* Invalidate cache */
379
		get_interface_arr(true);
380
		// If the parent MTU is being skipped at boot, this has already been shown
381
		if (is_platform_booting() && !$skip_parent_mtu) {
382
			echo gettext("done.") . "\n";
383
		}
384
	}
385
}
386

    
387
/**
388
 * Configure a VLAN interface.
389
 * 
390
 * @param bool $skip_parent_mtu Skip setting the parent interface's MTU
391
 */
392
function interface_vlan_configure(&$vlan, $flush = true, $skip_parent_mtu = false) {
393
	if (!is_array($vlan)) {
394
		log_error(gettext("VLAN: called with wrong options. Problems with config!"));
395
		return(NULL);
396
	}
397
	$if = $vlan['if'];
398
	if (empty($if)) {
399
		log_error(gettext("interface_vlan_configure called with if undefined."));
400
		return(NULL);
401
	}
402

    
403
	$vlanif = empty($vlan['vlanif']) ? vlan_interface($vlan) : $vlan['vlanif'];
404
	if ($vlanif == NULL) {
405
		log_error(gettext("vlan_interface called with if undefined var."));
406
		return(NULL);
407
	}
408
	$tag = $vlan['tag'];
409
	$pcp  = empty($vlan['pcp']) ? 0 : $vlan['pcp'];	/* Apply "Best Effort" if not set */
410

    
411
	/* make sure the parent interface is up */
412
	interfaces_bring_up($if);
413
	/* Since we are going to add vlan(4) try to enable all that hardware supports. */
414
	pfSense_interface_capabilities($if, IFCAP_VLAN_HWTAGGING|IFCAP_VLAN_MTU|IFCAP_VLAN_HWFILTER);
415

    
416
	if (!empty($vlanif) && does_interface_exist($vlanif)) {
417
		pfSense_interface_destroy($vlanif);
418
	}
419

    
420
	$tmpvlanif = pfSense_interface_create2("vlan");
421
	if (!is_string($tmpvlanif)) {
422
		log_error(sprintf(gettext('Failed to configure interface %1$s: Could not create temporary interface %2$s'), $vlan['vlanif'], 'vlan'));
423
		return(NULL);
424
	}
425
	pfSense_interface_rename($tmpvlanif, $vlanif);
426
	pfSense_ngctl_name("{$tmpvlanif}:", $vlanif);
427

    
428
	pfSense_vlan_create($vlanif, $if, $tag, $pcp);
429

    
430
	interfaces_bring_up($vlanif);
431

    
432
	/* invalidate interface cache */
433
	if ($flush) {
434
		get_interface_arr(true);
435
	}
436

    
437
	/* configure interface if assigned */
438
	$assignedif = convert_real_interface_to_friendly_interface_name($vlanif);
439
	if ($assignedif) {
440
		if (config_path_enabled("interfaces/{$assignedif}")) {
441
			interface_configure($assignedif, true, false, $skip_parent_mtu);
442
		}
443
	}
444

    
445
	/* XXX: ermal -- for now leave it here at the moment it does not hurt. */
446
	interfaces_bring_up($if);
447

    
448
	if (interface_vlan_mtu_configured($vlanif)) {
449
		set_interface_mtu($vlanif, interface_vlan_mtu_configured($vlanif));
450
	}
451

    
452
	return $vlanif;
453
}
454

    
455
/*
456
 * reconfigure VLAN childs interfaces after MTU changes, see
457
 * https://redmine.pfsense.org/issues/11035
458
 */
459
function interfaces_vlan_configure_mtu($parentif = "") {
460
	foreach (config_get_path('vlans/vlan', []) as $vlan) {
461
		if ($parentif != $vlan['if']) {
462
			continue;
463
		}
464
		if (interface_vlan_mtu_configured($vlan['vlanif'])) {
465
		       set_interface_mtu($vlan['vlanif'],
466
			   interface_vlan_mtu_configured($vlan['vlanif']));
467
		}
468
		if (empty(config_get_path('ppps/ppp'))) {
469
			continue;
470
		}
471
		// PPP interfaces must be restarted to adjust MTU changes
472
		foreach (config_get_path('ppps/ppp', []) as $ppp) {
473
			// skip modems, e.g. /dev/cuaZ99.0
474
			if ($ppp['type'] == 'ppp') {
475
				continue;
476
			}
477
			$ports = array_filter(explode(',', $ppp['ports']));
478
			foreach ($ports as $port) {
479
				if ($port != $vlan['vlanif']) {
480
					continue;
481
				}
482
				$confif = convert_real_interface_to_friendly_interface_name($ppp['if']);
483
				if (empty($confif)) {
484
					continue;
485
				}
486
				interface_configure($confif);
487
			}
488
		}
489
	}
490
}
491

    
492
function interface_qinq_configure(&$qinq, $flush = true) {
493
	global $g;
494

    
495
	if (!is_array($qinq)) {
496
		log_error(sprintf(gettext("QinQ compat VLAN: called with wrong options. Problems with config!%s"), "\n"));
497
		return;
498
	}
499

    
500
	$qinqif = $qinq['if'];
501
	if (empty($qinqif)) {
502
		log_error(sprintf(gettext("interface_qinq_configure called with if undefined.%s"), "\n"));
503
		return;
504
	}
505

    
506
	if (!does_interface_exist($qinqif)) {
507
		log_error(sprintf(gettext("interface_qinq_configure called with invalid if.%s"), "\n"));
508
		return;
509
	}
510

    
511
	$vlanif = interface_vlan_configure($qinq);
512
	if ($vlanif == NULL || $vlanif != $qinq['vlanif']) {
513
		log_error(gettext("interface_qinq_configure cannot create VLAN interface"));
514
		return;
515
	}
516

    
517
	exec("/sbin/ifconfig {$vlanif} vlanproto 802.1ad > /dev/null 2>&1");
518

    
519
	/* make sure the parent is up */
520
	interfaces_bring_up($qinqif);
521

    
522
	/* invalidate interface cache */
523
	if ($flush) {
524
		get_interface_arr(true);
525
	}
526

    
527
	if (interface_is_vlan($qinqif) == NULL) {
528
		mwexec("/sbin/ifconfig {$qinqif} promisc\n");
529
	}
530

    
531
	$macaddr = get_interface_mac($qinqif);
532
	if (!empty($qinq['members'])) {
533
		$members = explode(" ", $qinq['members']);
534
		foreach ($members as $qtag) {
535
			$qinq2 = array();
536
			$qinq2['tag'] = $qtag;
537
			$qinq2['if'] = $vlanif;
538
			interface_qinq2_configure($qinq2, $macaddr);
539
			unset($qinq2);
540
		}
541
	}
542

    
543
	interfaces_bring_up($qinqif);
544
	if (!empty($qinq['members'])) {
545
		$members = explode(" ", $qinq['members']);
546
		foreach ($members as $qtag) {
547
			interfaces_bring_up(qinq_interface($qinq, $qtag));
548
		}
549
	}
550

    
551
	return $vlanif;
552
}
553

    
554
function interfaces_qinq_configure($ovpn=false) {
555
	$qinqentry = config_get_path('qinqs/qinqentry');
556
	if (is_array($qinqentry) && count($qinqentry)) {
557
		if (is_platform_booting() && $ovpn) {
558
			echo gettext("Configuring OpenVPN QinQ interfaces...");
559
		} elseif (is_platform_booting()) {
560
			echo gettext("Configuring QinQ interfaces...");
561
		}
562
		foreach ($qinqentry as $qinq) {
563
			if (($ovpn && strstr($qinq['if'], "ovpn")) ||
564
			    (!$ovpn && !strstr($qinq['if'], "ovpn"))) {
565
				interface_qinq_configure($qinq, false);
566
			}
567
		}
568
		/* Invalidate cache */
569
		get_interface_arr(true);
570
		if (is_platform_booting()) {
571
			echo gettext("done.") . "\n";
572
		}
573
	}
574
}
575

    
576
function interface_qinq2_configure(&$qinq, $macaddr) {
577
	if (!is_array($qinq)) {
578
		log_error(sprintf(gettext("QinQ compat VLAN: called with wrong options. Problems with config!%s"), "\n"));
579
		return;
580
	}
581

    
582
	$if = $qinq['if'];
583
	if (empty($if)) {
584
		log_error(sprintf(gettext("interface_qinq2_configure called with if undefined.%s"), "\n"));
585
		return;
586
	}
587
	$tag = $qinq['tag'];
588
	$vlanif = "{$if}.{$tag}";
589
	$ngif = str_replace(".", "_", $if);
590
	if (strlen($vlanif) > IF_NAMESIZE) {
591
		log_error(sprintf(gettext("interface_qinq2_configure interface name too big %s. (max. size: %d).%s"),
592
		    $vlanif, IF_NAMESIZE, "\n"));
593
		return;
594
	}
595

    
596
	exec("/sbin/ifconfig vlan create vlandev {$if} vlan {$tag} name {$vlanif}");
597

    
598
	return $vlanif;
599
}
600

    
601
function interfaces_create_wireless_clones() {
602
	$iflist = get_configured_interface_list();
603

    
604
	foreach ($iflist as $if) {
605
		$realif = config_get_path("interfaces/{$if}/if");
606
		if (!is_interface_wireless($realif)) {
607
			continue;
608
		}
609
		interface_wireless_clone(interface_get_wireless_clone($realif),
610
								 config_get_path("interfaces/{$if}"));
611
	}
612

    
613
	$wclone = config_get_path('wireless/clone');
614
	if (is_array($wclone) && count($wclone)) {
615
		if (is_platform_booting()) {
616
			echo gettext("Creating wireless clone interfaces...");
617
		}
618
		/* Invalidate cache */
619
		get_interface_arr(true);
620
		foreach ($wclone as $clone) {
621
			if (empty($clone['cloneif'])) {
622
				continue;
623
			}
624
			if (does_interface_exist($clone['cloneif'])) {
625
				continue;
626
			}
627
			/* XXX: Maybe we should report any errors?! */
628
			interface_wireless_clone($clone['cloneif'], $clone);
629
		}
630
		if (is_platform_booting()) {
631
			echo gettext("done.") . "\n";
632
		}
633
	}
634
}
635

    
636
function interfaces_bridge_configure($checkmember = 0, $realif = "") {
637
	$bridged = config_get_path('bridges/bridged');
638
	if (!is_array($bridged) || !count($bridged)) {
639
		return;
640
	}
641

    
642
	$i = 0;
643
	foreach ($bridged as $bridge) {
644
		if (empty($bridge['bridgeif'])) {
645
			$bridge['bridgeif'] = "bridge{$i}";
646
		}
647
		if (!empty($realif) && ($realif != $bridge['bridgeif'])) {
648
			continue;
649
		}
650
		$ifname = false;
651
		foreach (config_get_path('interfaces', []) as $intname => $intpar) {
652
			if ($intpar['if'] == $bridge['bridgeif']) {
653
				$ifname = $intname;
654
				break;
655
			}
656
		}
657

    
658
		if ($ifname && (config_get_path("interfaces/{$ifname}/ipaddrv6", "") == "track6")) {
659
			if ($checkmember == 1) {
660
				continue;
661
			} else {
662
				$checkmember = 0;
663
			}
664
		} elseif (($checkmember == 2) && !$ifname) {
665
			continue;
666
		}
667

    
668
		/* XXX: Maybe we should report any errors?! */
669
		interface_bridge_configure($bridge, $checkmember, false);
670
		$i++;
671
	}
672

    
673
	/* Invalidate cache */
674
	get_interface_arr(true);
675
}
676

    
677
function interface_bridge_configure(&$bridge, $checkmember = 0, $flush = true) {
678
	if (!is_array($bridge)) {
679
		return;
680
	}
681

    
682
	if (empty($bridge['members'])) {
683
		log_error(sprintf(gettext("No members found on %s"), $bridge['bridgeif']));
684
		return;
685
	}
686

    
687
	$members = explode(',', $bridge['members']);
688
	if (!count($members)) {
689
		return;
690
	}
691

    
692
	/* Calculate smaller mtu and enforce it */
693
	$smallermtu = 0;
694
	$foundgif = false;
695
	foreach ($members as $member) {
696
		$realif = get_real_interface($member);
697
		$mtu = get_interface_mtu($realif);
698
		if (substr($realif, 0, 3) == "gif") {
699
			$foundgif = true;
700
			if ($checkmember == 1) {
701
				return;
702
			}
703
			if ($mtu <= 1500) {
704
				continue;
705
			}
706
		}
707
		if ($smallermtu == 0 && !empty($mtu)) {
708
			$smallermtu = $mtu;
709
		} elseif (!empty($mtu) && $mtu < $smallermtu) {
710
			$smallermtu = $mtu;
711
		}
712
	}
713
	if ($foundgif == false && $checkmember == 2) {
714
		return;
715
	}
716

    
717
	/* Just in case anything is not working well */
718
	if ($smallermtu == 0) {
719
		$smallermtu = 1500;
720
	}
721

    
722
	if (!empty($bridge['bridgeif'])) {
723
		pfSense_interface_destroy($bridge['bridgeif']);
724
		$tempifname = pfSense_interface_create2($bridge['bridgeif']);
725
		if (!is_string($tempifname)) {
726
			log_error(sprintf(gettext('Failed to configure interface %1$s: Could not create temporary interface %2$s'), $bridge['bridgeif'], $bridge['bridgeif']));
727
			return;
728
		}
729
		$bridgeif = escapeshellarg($bridge['bridgeif']);
730
	} else {
731
		// if called directly, as interfaces_bridge_edit.php does, and bridgeif isn't set
732
		// normally set by interfaces_bridge_configure, but not upon creation of new bridge
733
		$bridgeif = pfSense_interface_create2("bridge");
734
		if (!is_string($bridgeif)) {
735
			log_error(sprintf(gettext('Failed to configure interface %1$s: Could not create temporary interface %2$s'), $bridge['bridgeif'], 'bridge'));
736
			return;
737
		}
738
		$bridge['bridgeif'] = $bridgeif;
739
	}
740

    
741
	$bridgemtu = interface_find_child_cfgmtu($bridge['bridgeif']);
742
	if ($bridgemtu > $smallermtu) {
743
		$smallermtu = $bridgemtu;
744
	}
745

    
746
	$checklist = get_configured_interface_list();
747

    
748
	/* Add interfaces to bridge */
749
	foreach ($members as $member) {
750
		if (empty($checklist[$member])) {
751
			continue;
752
		}
753
		$realif = get_real_interface($member);
754
		if (!$realif) {
755
			log_error(gettext("realif not defined in interfaces bridge - up"));
756
			continue;
757
		}
758
		/* make sure the parent interface is up */
759
		pfSense_interface_mtu($realif, $smallermtu);
760
		interfaces_bring_up($realif);
761
		enable_hardware_offloading($member);
762
		pfSense_bridge_add_member($bridge['bridgeif'], $realif);
763
	}
764

    
765
	if (isset($bridge['enablestp'])) {
766
		interface_bridge_configure_stp($bridge);
767
	}
768

    
769
	interface_bridge_configure_advanced($bridge);
770

    
771
	interface_bridge_configure_ip6linklocal($bridge);
772

    
773
	if ($flush) {
774
		get_interface_arr(true);
775
	}
776

    
777
	if ($bridge['bridgeif']) {
778
		interfaces_bring_up($bridge['bridgeif']);
779
	} else {
780
		log_error(gettext("bridgeif not defined -- could not bring interface up"));
781
	}
782
}
783

    
784
function interface_bridge_configure_stp($bridge) {
785
	if (isset($bridge['enablestp'])) {
786
		$bridgeif = escapeshellarg(trim($bridge['bridgeif']));
787
		/* configure spanning tree proto */
788
		mwexec("/sbin/ifconfig {$bridgeif} proto " . escapeshellarg($bridge['proto']));
789

    
790
		if (!empty($bridge['stp'])) {
791
			$stpifs = explode(',', $bridge['stp']);
792
			foreach ($stpifs as $stpif) {
793
				$realif = get_real_interface($stpif);
794
				mwexec("/sbin/ifconfig {$bridgeif} stp {$realif}");
795
			}
796
		}
797
		if (!empty($bridge['maxage'])) {
798
			mwexec("/sbin/ifconfig {$bridgeif} maxage " . escapeshellarg($bridge['maxage']));
799
		}
800
		if (!empty($bridge['fwdelay'])) {
801
			mwexec("/sbin/ifconfig {$bridgeif} fwddelay " . escapeshellarg($bridge['fwdelay']));
802
		}
803
		if (!empty($bridge['hellotime'])) {
804
			mwexec("/sbin/ifconfig {$bridgeif} hellotime " . escapeshellarg($bridge['hellotime']));
805
		}
806
		if (!empty($bridge['priority'])) {
807
			mwexec("/sbin/ifconfig {$bridgeif} priority " . escapeshellarg($bridge['priority']));
808
		}
809
		if (!empty($bridge['holdcnt'])) {
810
			mwexec("/sbin/ifconfig {$bridgeif} holdcnt " . escapeshellarg($bridge['holdcnt']));
811
		}
812
		if (!empty($bridge['ifpriority'])) {
813
			$pconfig = explode(",", $bridge['ifpriority']);
814
			$ifpriority = array();
815
			foreach ($pconfig as $cfg) {
816
				$embcfg = explode_assoc(":", $cfg);
817
				foreach ($embcfg as $key => $value) {
818
					$ifpriority[$key] = $value;
819
				}
820
			}
821
			foreach ($ifpriority as $key => $value) {
822
				$realif = get_real_interface($key);
823
				mwexec("/sbin/ifconfig {$bridgeif} ifpriority {$realif} " . escapeshellarg($value));
824
			}
825
		}
826
		if (!empty($bridge['ifpathcost'])) {
827
			$pconfig = explode(",", $bridge['ifpathcost']);
828
			$ifpathcost = array();
829
			foreach ($pconfig as $cfg) {
830
				$embcfg = explode_assoc(":", $cfg);
831
				foreach ($embcfg as $key => $value) {
832
					$ifpathcost[$key] = $value;
833
				}
834
			}
835
			foreach ($ifpathcost as $key => $value) {
836
				$realif = get_real_interface($key);
837
				mwexec("/sbin/ifconfig {$bridgeif} ifpathcost {$realif} " . escapeshellarg($value));
838
			}
839
		}
840
	}
841
}
842

    
843
function interface_bridge_configure_advanced($bridge) {
844
	$bridgeif = escapeshellarg(trim($bridge['bridgeif']));
845

    
846
	if ($bridge['maxaddr'] <> "") {
847
		mwexec("/sbin/ifconfig {$bridgeif} maxaddr " . escapeshellarg($bridge['maxaddr']));
848
	}
849
	if ($bridge['timeout'] <> "") {
850
		mwexec("/sbin/ifconfig {$bridgeif} timeout " . escapeshellarg($bridge['timeout']));
851
	}
852
	if (!empty($bridge['span'])) {
853
		$spanifs = explode(",", $bridge['span']);
854
		foreach ($spanifs as $spanif) {
855
			$realif = get_real_interface($spanif);
856
			mwexec("/sbin/ifconfig {$bridgeif} span {$realif}");
857
		}
858
	}
859
	if (!empty($bridge['edge'])) {
860
		$edgeifs = explode(',', $bridge['edge']);
861
		foreach ($edgeifs as $edgeif) {
862
			$realif = get_real_interface($edgeif);
863
			mwexec("/sbin/ifconfig {$bridgeif} edge {$realif}");
864
		}
865
	}
866
	if (!empty($bridge['autoedge'])) {
867
		$edgeifs = explode(',', $bridge['autoedge']);
868
		foreach ($edgeifs as $edgeif) {
869
			$realif = get_real_interface($edgeif);
870
			mwexec("/sbin/ifconfig {$bridgeif} -autoedge {$realif}");
871
		}
872
	}
873
	if (!empty($bridge['ptp'])) {
874
		$ptpifs = explode(',', $bridge['ptp']);
875
		foreach ($ptpifs as $ptpif) {
876
			$realif = get_real_interface($ptpif);
877
			mwexec("/sbin/ifconfig {$bridgeif} ptp {$realif}");
878
		}
879
	}
880
	if (!empty($bridge['autoptp'])) {
881
		$ptpifs = explode(',', $bridge['autoptp']);
882
		foreach ($ptpifs as $ptpif) {
883
			$realif = get_real_interface($ptpif);
884
			mwexec("/sbin/ifconfig {$bridgeif} -autoptp {$realif}");
885
		}
886
	}
887
	if (!empty($bridge['static'])) {
888
		$stickyifs = explode(',', $bridge['static']);
889
		foreach ($stickyifs as $stickyif) {
890
			$realif = get_real_interface($stickyif);
891
			mwexec("/sbin/ifconfig {$bridgeif} sticky {$realif}");
892
		}
893
	}
894
	if (!empty($bridge['private'])) {
895
		$privateifs = explode(',', $bridge['private']);
896
		foreach ($privateifs as $privateif) {
897
			$realif = get_real_interface($privateif);
898
			mwexec("/sbin/ifconfig {$bridgeif} private {$realif}");
899
		}
900
	}
901
}
902

    
903
function interface_bridge_configure_ip6linklocal($bridge) {
904
	$bridgeif = escapeshellarg(trim($bridge['bridgeif']));
905

    
906
	$members = explode(',', $bridge['members']);
907
	if (!count($members)) {
908
		return;
909
	}
910

    
911
	$auto_linklocal = isset($bridge['ip6linklocal']);
912
	$bridgeop = $auto_linklocal ? '' : '-';
913
	$memberop = $auto_linklocal ? '-' : '';
914

    
915
	mwexec("/usr/sbin/ndp -i {$bridgeif} -- {$bridgeop}auto_linklocal");
916
	foreach ($members as $member) {
917
		$realif = escapeshellarg(get_real_interface($member));
918
		mwexec("/usr/sbin/ndp -i {$realif} -- {$memberop}auto_linklocal");
919
	}
920
}
921

    
922
function interface_bridge_add_member($bridgeif, $interface, $flagsapplied = false) {
923
	if (!does_interface_exist($bridgeif) || !does_interface_exist($interface)) {
924
		return;
925
	}
926

    
927
	if ($flagsapplied == false) {
928
		$mtu = get_interface_mtu($bridgeif);
929
		$mtum = get_interface_mtu($interface);
930
		/* Reconfigure the bridge mtu if the new member's MTU is lower, or
931
		 * reconfigure a non-gif interface MTU if the bridge's MTU is lower */
932
		if ($mtu > $mtum) {
933
			pfSense_interface_mtu($bridgeif, $mtum);
934
		} else if ($mtu < $mtum && !(substr($interface, 0, 3) == "gif" && $mtu <= 1500)) {
935
			pfSense_interface_mtu($interface, $mtu);
936
		}
937

    
938
		hardware_offloading_applyflags($interface);
939
		interfaces_bring_up($interface);
940
	}
941

    
942
	pfSense_bridge_add_member($bridgeif, $interface);
943
	$bridged = config_get_path('bridges/bridged');
944
	if (is_array($bridged)) {
945
		foreach ($bridged as $bridge) {
946
			if ($bridgeif == $bridge['bridgeif']) {
947
				interface_bridge_configure_stp($bridge);
948
				interface_bridge_configure_advanced($bridge);
949
			}
950
		}
951
	}
952
}
953

    
954
function interfaces_lagg_configure($realif = "") {
955
	$i = 0;
956
	$laggs = config_get_path('laggs/lagg');
957
	if (is_array($laggs) && count($laggs)) {
958
		if (is_platform_booting()) {
959
			echo gettext("Configuring LAGG interfaces...");
960
		}
961
		foreach ($laggs as $lagg) {
962
			if (empty($lagg['laggif'])) {
963
				$lagg['laggif'] = "lagg{$i}";
964
			}
965
			if (!empty($realif) && $realif != $lagg['laggif']) {
966
				continue;
967
			}
968
			/* XXX: Maybe we should report any errors?! */
969
			interface_lagg_configure($lagg, false);
970
			$i++;
971
		}
972
		/* Invalidate cache */
973
		get_interface_arr(true);
974
		if (is_platform_booting()) {
975
			echo gettext("done.") . "\n";
976
		}
977
	}
978
}
979

    
980
function interface_lagg_configure($lagg, $flush = true) {
981
	if (!is_array($lagg)) {
982
		return -1;
983
	}
984

    
985
	$members = explode(',', $lagg['members']);
986
	if (!count($members)) {
987
		return -1;
988
	}
989

    
990
	if (is_platform_booting() || !(empty($lagg['laggif']))) {
991
		pfSense_interface_destroy($lagg['laggif']);
992
		$tempifname = pfSense_interface_create2($lagg['laggif']);
993
		if (!is_string($tempifname)) {
994
			log_error(sprintf(gettext('Failed to configure interface %1$s: Could not create temporary interface %2$s'), $lagg['laggif'], $lagg['laggif']));
995
			return -1;
996
		}
997
		$laggif = $lagg['laggif'];
998
	} else {
999
		$laggif = pfSense_interface_create2("lagg");
1000
		if (!is_string($laggif)) {
1001
			log_error(sprintf(gettext('Failed to configure interface %1$s: Could not create temporary interface %2$s'), $lagg['laggif'], 'lagg'));
1002
			return -1;
1003
		}
1004
	}
1005

    
1006
	/* Check if MTU was defined for this lagg interface */
1007
	$lagg_mtu = interface_find_child_cfgmtu($laggif);
1008
	if ($lagg_mtu == 0) {
1009
		foreach (config_get_path('interfaces', []) as $tmpinterface) {
1010
			// ignore the configured MTU from disabled interfaces
1011
			if (($tmpinterface['if'] == $lagg['laggif']) && isset($tmpinterface['enable']) &&
1012
			    !empty($tmpinterface['mtu'])) {
1013
				$lagg_mtu = $tmpinterface['mtu'];
1014
				break;
1015
			}
1016
		}
1017
	}
1018

    
1019
	/* Just in case anything is not working well */
1020
	if ($lagg_mtu == 0) {
1021
		$lagg_mtu = 1500;
1022
	}
1023

    
1024
	// put failover master interface on top of list
1025
	if (($lagg['proto'] == 'failover') && isset($lagg['failovermaster']) &&
1026
	    ($lagg['failovermaster'] != 'auto')) {
1027
		unset($members[array_search($lagg['failovermaster'], $members)]);
1028
		$members = array_merge(array($lagg['failovermaster']), $members);
1029
	}
1030

    
1031
	if (($lagg['proto'] == 'lacp') && isset($lagg['lacptimeout']) &&
1032
	    ($lagg['lacptimeout'] != 'slow')) {
1033
		$lacptimeout = 'lacp_fast_timeout';
1034
	} else {
1035
		$lacptimeout = '';
1036
	}
1037

    
1038
	if ((($lagg['proto'] == 'lacp') || ($lagg['proto'] == 'loadbalance')) &&
1039
	    isset($lagg['lagghash']) && !empty($lagg['lagghash'])) {
1040
		$lagghash = 'lagghash ' . escapeshellarg($lagg['lagghash']);
1041
	} else {
1042
		$lagghash = '';
1043
	}
1044

    
1045
	foreach ($members as $member) {
1046
		if (!does_interface_exist($member)) {
1047
			continue;
1048
		}
1049

    
1050
		/* make sure the parent interface is up */
1051
		pfSense_interface_mtu($member, $lagg_mtu);
1052
		interfaces_bring_up($member);
1053
		hardware_offloading_applyflags($member);
1054

    
1055
		// Ensure there are no nulls in these values. It upsets escapeshellarg()
1056
		$laggif = str_replace("\0", "", $laggif);
1057
		$member = str_replace("\0", "", $member);
1058
		mwexec("/sbin/ifconfig " . escapeshellarg($laggif) . " laggport " . escapeshellarg($member));
1059
	}
1060

    
1061
	mwexec("/sbin/ifconfig " . escapeshellarg($laggif) . " laggproto " . escapeshellarg($lagg['proto']) . " {$lacptimeout} {$lagghash}");
1062

    
1063
	if ($flush) {
1064
		get_interface_arr(true);
1065
	}
1066

    
1067
	interfaces_bring_up($laggif);
1068
	// after the lagg is recreated, its VLANs must be reconfigured
1069
	interfaces_vlan_configure($laggif, true);
1070

    
1071
	return $laggif;
1072
}
1073

    
1074
/* NOTE: $grekey is not used but useful for passing this function to array_walk. */
1075
function interface_gre_configure(&$gre, $grekey = "", $flush = true) {
1076
	global $g;
1077

    
1078
	if (!is_array($gre)) {
1079
		return -1;
1080
	}
1081

    
1082
	$realif = convert_friendly_interface_to_real_interface_name($gre['if']);
1083
	if (!interface_is_vlan($realif)) {
1084
		$realif = get_real_interface($gre['if']);
1085
	}
1086
	$realifip = get_interface_ip($gre['if']);
1087
	$realifip6 = get_interface_ipv6($gre['if']);
1088

    
1089
	/* do not run ifconfig without correct $realifip */
1090
	if ((!$realifip && is_ipaddrv4($gre['remote-addr'])) ||
1091
	    (!$realifip6 && is_ipaddrv6($gre['remote-addr']))) {
1092
		return -1;
1093
	}
1094

    
1095
	/* make sure the parent interface is up */
1096
	interfaces_bring_up($realif);
1097

    
1098
	if (is_platform_booting() || !(empty($gre['greif']))) {
1099
		pfSense_interface_destroy($gre['greif']);
1100
		$tempifname = pfSense_interface_create2($gre['greif']);
1101
		if (!is_string($tempifname)) {
1102
			log_error(sprintf(gettext('Failed to configure interface %1$s: Could not create temporary interface %2$s'), $gre['if'], $gre['greif']));
1103
			return -1;
1104
		}
1105
		$greif = $gre['greif'];
1106
	} else {
1107
		$greif = pfSense_interface_create2("gre");
1108
		if (!is_string($greif)) {
1109
			log_error(sprintf(gettext('Failed to configure interface %1$s: Could not create temporary interface %2$s'), $gre['if'], 'gre'));
1110
			return -1;
1111
		}
1112
	}
1113

    
1114
	$tunnel_type = '';
1115
	if ((!empty($gre['tunnel-local-addr'])) || (!empty($gre['tunnel-remote-addr']))) {
1116
		$tunnel_type = 'v4';
1117
	}
1118
	if ((!empty($gre['tunnel-local-addr6'])) || (!empty($gre['tunnel-remote-addr6']))) {
1119
		$tunnel_type .= 'v6';
1120
	}
1121

    
1122
	/* Do not change the order here for more see gre(4) NOTES section. */
1123
	if (is_ipaddrv6($gre['remote-addr'])) {
1124
		mwexec("/sbin/ifconfig " . escapeshellarg($greif) . " inet6 tunnel " . escapeshellarg($realifip6) . " " . escapeshellarg($gre['remote-addr']));
1125
	} else {
1126
		mwexec("/sbin/ifconfig " . escapeshellarg($greif) . " tunnel " . escapeshellarg($realifip) . " " . escapeshellarg($gre['remote-addr']));
1127
	}
1128
	if (in_array($tunnel_type, array('v4', 'v4v6'))) {
1129
		mwexec("/sbin/ifconfig " . escapeshellarg($greif) . " " . escapeshellarg($gre['tunnel-local-addr']) . " " . escapeshellarg($gre['tunnel-remote-addr']) . " netmask " . gen_subnet_mask($gre['tunnel-remote-net']));
1130
	}
1131
	if (in_array($tunnel_type, array('v6', 'v4v6'))) {
1132
		/* XXX: The prefixlen argument for tunnels of ipv6 is useless since it needs to be 128 as enforced by kernel */
1133
		mwexec("/sbin/ifconfig " . escapeshellarg($greif) . " inet6 " . escapeshellarg($gre['tunnel-local-addr6']) . " " . escapeshellarg($gre['tunnel-remote-addr6']) . " prefixlen 128");
1134
	}
1135

    
1136
	$parentif = get_real_interface($gre['if']);
1137
	if ($parentif) {
1138
		interfaces_bring_up($parentif);
1139
	} else {
1140
		log_error(gettext("could not bring parentif up -- variable not defined -- interface_gre_configure()"));
1141
	}
1142

    
1143
	if (isset($gre['link1'])) {
1144
		if (in_array($tunnel_type, array('v4', 'v4v6'))) {
1145
			mwexec("/sbin/route add " . escapeshellarg($gre['tunnel-remote-addr']) . "/" . escapeshellarg($gre['tunnel-remote-net']) . " " . escapeshellarg($gre['tunnel-local-addr']));
1146
		}
1147
		if (in_array($tunnel_type, array('v6', 'v4v6'))) {
1148
			mwexec("/sbin/route -6 add " . escapeshellarg($gre['tunnel-remote-addr6']) . "/" . escapeshellarg($gre['tunnel-remote-net6']) . " " . escapeshellarg($gre['tunnel-local-addr6']));
1149
		}
1150
	}
1151
	if (in_array($tunnel_type, array('v4', 'v4v6'))) {
1152
		file_put_contents("{$g['tmp_path']}/{$greif}_router", $gre['tunnel-remote-addr']);
1153
		unlink_if_exists("{$g['tmp_path']}/{$greif}_router.last");
1154
	}
1155
	if (in_array($tunnel_type, array('v6', 'v4v6'))) {
1156
		file_put_contents("{$g['tmp_path']}/{$greif}_routerv6", $gre['tunnel-remote-addr6']);
1157
		unlink_if_exists("{$g['tmp_path']}/{$greif}_routerv6.last");
1158
	}
1159

    
1160
	if ($flush) {
1161
		get_interface_arr(true);
1162
	}
1163

    
1164
	interfaces_bring_up($greif);
1165

    
1166
	return $greif;
1167
}
1168

    
1169
function interface_is_type($if, $type) {
1170
	switch ($type) {
1171
		case "gre":
1172
			$list = 'gres';
1173
			$entry = 'gre';
1174
			$entif = 'greif';
1175
			break;
1176
		case "gif":
1177
			$list = 'gifs';
1178
			$entry = 'gif';
1179
			$entif = 'gifif';
1180
			break;
1181
		case "lagg":
1182
			$list = 'laggs';
1183
			$entry = 'lagg';
1184
			$entif = 'laggif';
1185
			break;
1186
		default:
1187
			break;
1188
	}
1189

    
1190
	$entries = config_get_path("{$list}/{$entry}");
1191
	if (!is_array($entries)) {
1192
		return (NULL);
1193
	}
1194

    
1195
	foreach ($entries as $ent) {
1196
		if ($ent[$entif] == $if) {
1197
			return ($ent);
1198
		}
1199
	}
1200
	return (NULL);
1201
}
1202

    
1203
function is_greipsec($if) {
1204
	if (ipsec_enabled()) {
1205
		foreach (config_get_path('gres/gre', []) as $gre) {
1206
			foreach (config_get_path('ipsec/phase1', []) as $ph1ent) {
1207
				foreach (config_get_path('ipsec/phase2', []) as $ph2ent) {
1208
					if (($ph1ent['ikeid'] == $ph2ent['ikeid']) && ($ph2ent['mode'] == 'transport') &&
1209
					    !isset($ph1ent['disabled']) && !isset($ph2ent['disabled']) &&
1210
					    ($ph1ent['interface'] == $gre['if']) && ($gre['greif'] == $if) &&
1211
					    ($ph1ent['remote-gateway'] == $gre['remote-addr'])) {
1212
						    return true;
1213
					}
1214
				}
1215
			}
1216
		}
1217
	}
1218
	return false;
1219
}
1220

    
1221
/* NOTE: $gifkey is not used but useful for passing this function to array_walk. */
1222
function interface_gif_configure(&$gif, $gifkey = "", $flush = true) {
1223
	global $g;
1224
	if (!is_array($gif)) {
1225
		return -1;
1226
	}
1227

    
1228
	$realif = convert_friendly_interface_to_real_interface_name($gif['if']);
1229
	if (!interface_is_vlan($realif)) {
1230
		$realif = get_real_interface($gif['if']);
1231
	}
1232
	$ipaddr = get_interface_ip($gif['if']);
1233

    
1234
	if (is_ipaddrv4($gif['remote-addr'])) {
1235
		if (is_ipaddrv4($ipaddr)) {
1236
			$realifip = $ipaddr;
1237
		} else {
1238
			$realifip = get_interface_ip($gif['if']);
1239
		}
1240
		$realifgw = get_interface_gateway($gif['if']);
1241
	} elseif (is_ipaddrv6($gif['remote-addr'])) {
1242
		if (is_ipaddrv6($ipaddr)) {
1243
			$realifip = $ipaddr;
1244
		} else {
1245
			$realifip = get_interface_ipv6($gif['if']);
1246
		}
1247
		$realifgw = get_interface_gateway_v6($gif['if']);
1248
	}
1249

    
1250
	/* do not run ifconfig without correct $realifip */
1251
	if ((!is_ipaddrv4($realifip) && is_ipaddrv4($gif['remote-addr'])) ||
1252
	    (!is_ipaddrv6($realifip) && is_ipaddrv6($gif['remote-addr']))) {
1253
		return -1;
1254
	}
1255

    
1256
	/* make sure the parent interface is up */
1257
	$parentif = get_real_interface($gif['if']);
1258
	if ($parentif) {
1259
		interfaces_bring_up($parentif);
1260
	} else {
1261
		log_error(gettext("could not bring parentif up -- variable not defined -- interface_gif_configure()"));
1262
	}
1263

    
1264
	/* some kernels may need to load if_gif */
1265
	if (!is_module_loaded("if_gif")) {
1266
		mwexec("/sbin/kldload if_gif");
1267
	}
1268

    
1269
	if (is_platform_booting() || !(empty($gif['gifif']))) {
1270
		pfSense_interface_destroy($gif['gifif']);
1271
		$tempifname = pfSense_interface_create2($gif['gifif']);
1272
		if (!is_string($tempifname)) {
1273
			log_error(sprintf(gettext('Failed to configure interface %1$s: Could not create temporary interface %2$s'), $gif['if'], $gif['gifif']));
1274
			return -1;
1275
		}
1276
		$gifif = $gif['gifif'];
1277
	} else {
1278
		$gifif = pfSense_interface_create2("gif");
1279
		if (!is_string($gifif)) {
1280
			log_error(sprintf(gettext('Failed to configure interface %1$s: Could not create temporary interface %2$s'), $gif['if'], 'gif'));
1281
			return -1;
1282
		}
1283
	}
1284

    
1285
	/* Do not change the order here for more see gif(4) NOTES section. */
1286
	if (is_ipaddrv6($gif['remote-addr'])) {
1287
		mwexec("/sbin/ifconfig " . escapeshellarg($gifif) . " inet6 tunnel " . escapeshellarg($realifip) . " " . escapeshellarg($gif['remote-addr']));
1288
	} else {
1289
		mwexec("/sbin/ifconfig " . escapeshellarg($gifif) . " tunnel " . escapeshellarg($realifip) . " " . escapeshellarg($gif['remote-addr']));
1290
	}
1291
	if ((is_ipaddrv6($gif['tunnel-local-addr'])) || (is_ipaddrv6($gif['tunnel-remote-addr']))) {
1292
		/* XXX: The prefixlen argument for tunnels of ipv6 is useless since it needs to be 128 as enforced by kernel */
1293
		//mwexec("/sbin/ifconfig {$gifif} inet6 " . escapeshellarg($gif['tunnel-local-addr']) . " " . escapeshellarg($gif['tunnel-remote-addr']) . " prefixlen /" . escapeshellarg($gif['tunnel-remote-net']));
1294
		mwexec("/sbin/ifconfig " . escapeshellarg($gifif) . " inet6 " . escapeshellarg($gif['tunnel-local-addr']) . " " . escapeshellarg($gif['tunnel-remote-addr']) . " prefixlen 128");
1295
	} else {
1296
		mwexec("/sbin/ifconfig " . escapeshellarg($gifif) . " " . escapeshellarg($gif['tunnel-local-addr']) . " " . escapeshellarg($gif['tunnel-remote-addr']) . " netmask " . gen_subnet_mask($gif['tunnel-remote-net']));
1297
	}
1298
	if (isset($gif['link1'])) {
1299
		pfSense_interface_flags($gifif, IFF_LINK1);
1300
	}
1301
	if (isset($gif['link2'])) {
1302
		pfSense_interface_flags($gifif, IFF_LINK2);
1303
	}
1304
	if ($gifif) {
1305
		interfaces_bring_up($gifif);
1306
		$gifmtu = "";
1307
		$currentgifmtu = get_interface_mtu($gifif);
1308
		foreach (config_get_path('interfaces', []) as $tmpinterface) {
1309
			if ($tmpinterface['if'] == $gifif) {
1310
				if (isset($tmpinterface['mtu']) && is_numericint($tmpinterface['mtu'])) {
1311
					$gifmtu = $tmpinterface['mtu'];
1312
				}
1313
			}
1314
		}
1315
		if (is_numericint($gifmtu)) {
1316
			if ($gifmtu != $currentgifmtu) {
1317
				mwexec("/sbin/ifconfig " . escapeshellarg($gifif) . " mtu {$gifmtu}");
1318
			}
1319
		}
1320
	} else {
1321
		log_error(gettext("could not bring gifif up -- variable not defined"));
1322
	}
1323

    
1324
	if (!is_platform_booting()) {
1325
		$iflist = get_configured_interface_list();
1326
		foreach ($iflist as $ifname) {
1327
			$if = config_get_path("interfaces/{$ifname}/if", "");
1328
			if ($if == $gifif) {
1329
				if (get_interface_gateway($ifname)) {
1330
					system_routing_configure($ifname);
1331
					break;
1332
				}
1333
				if (get_interface_gateway_v6($ifname)) {
1334
					system_routing_configure($ifname);
1335
					break;
1336
				}
1337
			}
1338
		}
1339
	}
1340

    
1341

    
1342
	if (is_ipaddrv4($gif['tunnel-remote-addr'])) {
1343
		file_put_contents("{$g['tmp_path']}/{$gifif}_router",
1344
		    $gif['tunnel-remote-addr']);
1345
		unlink_if_exists("{$g['tmp_path']}/{$gifif}_router.last");
1346
	} elseif (is_ipaddrv6($gif['tunnel-remote-addr'])) {
1347
		file_put_contents("{$g['tmp_path']}/{$gifif}_routerv6",
1348
		    $gif['tunnel-remote-addr']);
1349
		unlink_if_exists("{$g['tmp_path']}/{$gifif}_routerv6.last");
1350
	}
1351

    
1352
	route_add_or_change($gif['remote-addr'], $realifgw);
1353

    
1354
	if ($flush) {
1355
		get_interface_arr(true);
1356
	}
1357

    
1358
	interfaces_bring_up($gifif);
1359

    
1360
	return $gifif;
1361
}
1362

    
1363
function interfaces_tunnel_configure($checkparent = 0, $realif = "", $type = "") {
1364
	if (!in_array($type, array('gre', 'gif'))) {
1365
		return;
1366
	}
1367

    
1368
	$tuns = config_get_path("{$type}s/{$type}");
1369
	if (!is_array($tuns) || !count($tuns)) {
1370
		return;
1371
	}
1372

    
1373
	foreach ($tuns as $i => $tunnel) {
1374
		if (empty($tunnel["{$type}if"])) {
1375
			$tunnel["{$type}if"] = $type . $i;
1376
		}
1377
		if (!empty($realif) && $realif != $tunnel["{$type}if"]) {
1378
			continue;
1379
		}
1380

    
1381
		$ipaddrv6 = config_get_path("interfaces/{$tunnel}/if/ipaddrv6");
1382
		if ($checkparent == 1) {
1383
			if (substr($tunnel['if'], 0, 4) == '_vip') {
1384
				continue;
1385
			}
1386
			if (substr($tunnel['if'], 0, 5) == '_lloc') {
1387
				continue;
1388
			}
1389

    
1390
			if ($ipaddrv6 == "track6") {
1391
				continue;
1392
			}
1393
		} elseif ($checkparent == 2) {
1394
			if ((substr($tunnel['if'], 0, 4) != '_vip' &&
1395
			    substr($tunnel['if'], 0, 5) != '_lloc') &&
1396
				$ipaddrv6 != "track6") {
1397
				continue;
1398
			}
1399
		}
1400
		if ($type == 'gif') {
1401
			interface_gif_configure($tunnel, "", false);
1402
		} elseif ($type == 'gre') {
1403
			interface_gre_configure($tunnel, "", false);
1404
		}
1405
	}
1406

    
1407
	/* Invalidate cache */
1408
	get_interface_arr(true);
1409
}
1410

    
1411
/* Build a list of IPsec interfaces */
1412
function interface_ipsec_vti_list_p1($ph1ent) {
1413
	$iface_list = array();
1414

    
1415
	if (empty($ph1ent) || !is_array($ph1ent) || !is_array(config_get_path('ipsec/phase2'))) {
1416
		return $iface_list;
1417
	}
1418

    
1419
	$vtisubnet_spec = ipsec_vti($ph1ent, true);
1420
	if ((!$vtisubnet_spec || !is_array($vtisubnet_spec))) {
1421
		return $iface_list;
1422
	}
1423

    
1424
	/* With IKEv1 or v2+Split, each P2 gets its own conn/reqid/interface */
1425
	if (!isset($ph1ent['mobile']) && ($ph1ent['iketype'] == 'ikev1' || isset($ph1ent['splitconn']))) {
1426
		foreach ($vtisubnet_spec as $vtisub) {
1427
			$iface_list[ipsec_get_ifname($ph1ent, $vtisub['reqid'])] = gettext("IPsec VTI") . ": ".htmlspecialchars($ph1ent['descr'] . " / " . $vtisub['descr']);
1428
		}
1429
	} else {
1430
		/* For IKEv2, only create one interface with additional addresses as aliases */
1431
		$iface_list[ipsec_get_ifname($ph1ent)] = gettext("IPsec VTI") . ": ".htmlspecialchars($ph1ent['descr']);
1432
	}
1433
	return $iface_list;
1434
}
1435
function interface_ipsec_vti_list_all() {
1436
	$iface_list = array();
1437
	$phase1 = config_get_path('ipsec/phase1');
1438
	$phase2 = config_get_path('ipsec/phase2');
1439
	if (is_array($phase1) && is_array($phase2)) {
1440
		foreach ($phase1 as $ph1ent) {
1441
			if ($ph1ent['disabled']) {
1442
				continue;
1443
			}
1444
			$iface_list = array_merge($iface_list, interface_ipsec_vti_list_p1($ph1ent));
1445
		}
1446
	}
1447
	return $iface_list;
1448
}
1449

    
1450
function is_interface_ipsec_vti_assigned($phase2) {
1451
	$phase1 = ipsec_get_phase1($phase2['ikeid']);
1452
	$vti_interface = null;
1453
	$vtisubnet_spec = ipsec_vti($phase1, true);
1454
	if (($vtisubnet_spec && is_array($vtisubnet_spec))) {
1455
		/* With IKEv1 or v2+Split, each P2 gets its own conn/reqid/interface */
1456
		if (!isset($phase1['mobile']) && ($phase1['iketype'] == 'ikev1' || isset($phase1['splitconn']))) {
1457
			foreach ($vtisubnet_spec as $vtisub) {
1458
				/* Is this for this P2? */
1459
				if (($vtisub['left'] == ipsec_idinfo_to_cidr($phase2['localid'], true, $phase2['mode'])) &&
1460
				    ($vtisub['right'] == ipsec_idinfo_to_cidr($phase2['remoteid'], false, $phase2['mode']))) {
1461
					$vti_interface = ipsec_get_ifname($phase1, $vtisub['reqid']);
1462
				}
1463
			}
1464
		} else {
1465
			$vti_interface = ipsec_get_ifname($phase1);
1466
		}
1467
	}
1468
	/* Check if this interface is assigned */
1469
	return (does_interface_exist($vti_interface) && (convert_real_interface_to_friendly_interface_name($vti_interface) != null));
1470
}
1471
function interface_ipsec_vti_configure($ph1ent) {
1472
	global $ipsec_reqid_base;
1473

    
1474
	if (empty($ph1ent) || !is_array($ph1ent) || empty(config_get_path('ipsec/phase2'))) {
1475
		return false;
1476
	}
1477

    
1478
	$vtisubnet_spec = ipsec_vti($ph1ent, true);
1479
	if ((!$vtisubnet_spec || !is_array($vtisubnet_spec))) {
1480
		return false;
1481
	}
1482

    
1483
	$left_spec = ipsec_get_phase1_src($ph1ent);
1484

    
1485
	/* Attempt to resolve the remote gateway if needed */
1486
	$right_spec = ipsec_get_phase1_dst($ph1ent);
1487
	if (empty($right_spec)) {
1488
		/* Far end cannot be resolved */
1489
		return false;
1490
	}
1491

    
1492
	$iface_addrs = array();
1493
	$reqids = array();
1494

    
1495
	/* With IKEv1 or v2+Split, each P2 gets its own conn/reqid/interface */
1496
	if (!isset($ph1ent['mobile']) && ($ph1ent['iketype'] == 'ikev1' || isset($ph1ent['splitconn']))) {
1497
		/* Form a single interface for each P2 entry */
1498
		foreach ($vtisubnet_spec as $vtisub) {
1499
			$ipsecif = ipsec_get_ifname($ph1ent, $vtisub['reqid']);
1500
			if (!is_array($iface_addrs[$ipsecif])) {
1501
				$iface_addrs[$ipsecif] = array();
1502
			}
1503
			$vtisub['alias'] = "";
1504
			$iface_addrs[$ipsecif][] = $vtisub;
1505
			$reqids[$ipsecif] = $vtisub['reqid'];
1506
		}
1507
	} else {
1508
		/* For IKEv2, only create one interface with additional addresses as aliases */
1509
		$ipsecif = ipsec_get_ifname($ph1ent);
1510
		if (!is_array($iface_addrs[$ipsecif])) {
1511
			$iface_addrs[$ipsecif] = array();
1512
		}
1513
		$have_v4 = false;
1514
		$have_v6 = false;
1515
		foreach ($vtisubnet_spec as $vtisub) {
1516
			// Alias stuff
1517
			$vtisub['alias'] = "";
1518
			if (is_ipaddrv6($vtisub['left'])) {
1519
				if ($have_v6) {
1520
					$vtisub['alias'] = " alias";
1521
				}
1522
				$have_v6 = true;
1523
			} else {
1524
				if ($have_v4) {
1525
					$vtisub['alias'] = " alias";
1526
				}
1527
				$have_v4 = true;
1528
			}
1529
			$iface_addrs[$ipsecif][] = $vtisub;
1530
		}
1531
		/* IKEv2 w/o Split uses the reqid of the first P2 */
1532
		$reqids[$ipsecif] = $vtisubnet_spec[0]['reqid'];
1533
	}
1534

    
1535
	foreach ($iface_addrs as $ipsecif => $addrs) {
1536
		if (!is_array($addrs)) {
1537
			continue;
1538
		}
1539
		// Create IPsec interface
1540
		if (does_interface_exist($ipsecif)) {
1541
			mwexec("/sbin/ifconfig " . escapeshellarg($ipsecif) . " destroy");
1542
		}
1543
		mwexec("/sbin/ifconfig " . escapeshellarg($ipsecif) . " create reqid " . escapeshellarg($ipsec_reqid_base + $reqids[$ipsecif]));
1544

    
1545
		/* Apply the outer tunnel addresses to the interface */
1546
		$inet = is_ipaddrv6($left_spec) ? "inet6" : "inet";
1547
		mwexec("/sbin/ifconfig " . escapeshellarg($ipsecif) . " {$inet} tunnel " . escapeshellarg($left_spec) . " " . escapeshellarg($right_spec) . " up");
1548

    
1549
		/* Loop through all of the addresses for this interface and apply them as needed */
1550
		foreach ($addrs as $addr) {
1551
			// apply interface addresses
1552
			if (is_v6($addr['left'])) {
1553
				$inet = "inet6";
1554
				$gwtype = "v6";
1555
				$right = '';
1556
			} else {
1557
				$inet = "inet";
1558
				$gwtype = "";
1559
				$right = escapeshellarg((explode('/', $addr['right'], 2))[0]);
1560
			}
1561

    
1562
			mwexec("/sbin/ifconfig " . escapeshellarg($ipsecif) . " {$inet} " . escapeshellarg($addr['left']) . " " . $right . $addr['alias']);
1563
			/* If alias is empty, this is the first address on the interface and should be used as the gateway. */
1564
			if (empty($addr['alias'])) {
1565
				file_put_contents("/tmp/{$ipsecif}_router{$gwtype}", (explode('/', $addr['right'], 2))[0]);
1566
				unlink_if_exists("/tmp/{$ipsecif}_router{$gwtype}.last");
1567
			}
1568
		}
1569
		/* Check/set the MTU if the user configured a custom value.
1570
		 * https://redmine.pfsense.org/issues/9111 */
1571
		$currentvtimtu = get_interface_mtu($ipsecif);
1572
		foreach (config_get_path('interfaces', []) as $tmpinterface) {
1573
			if ($tmpinterface['if'] == $ipsecif) {
1574
				if (isset($tmpinterface['mtu']) && is_numericint($tmpinterface['mtu'])) {
1575
					$vtimtu = $tmpinterface['mtu'];
1576
				}
1577
			}
1578
		}
1579
		if (is_numericint($vtimtu)) {
1580
			if ($vtimtu != $currentvtimtu) {
1581
				mwexec("/sbin/ifconfig {$ipsecif} mtu {$vtimtu}");
1582
			}
1583
		}
1584
		/* Add the VTI to the interface cache directly because calling 'get_interface_arr(true)' is expensive.
1585
		   See: https://redmine.pfsense.org/issues/15449 */
1586
		global $interface_arr_cache;
1587
		if (is_array($interface_arr_cache) && !in_array($ipsecif, $interface_arr_cache)) {
1588
			$interface_arr_cache[] = $ipsecif;
1589
		}
1590
		$ipsecif_friendlyname = convert_real_interface_to_friendly_interface_name($ipsecif);
1591
		if (!empty($ipsecif_friendlyname)) {
1592
			system_routing_configure($ipsecif_friendlyname);
1593
		}
1594
	}
1595
	return true;
1596
}
1597

    
1598
function interfaces_ipsec_vti_configure() {
1599
	$bootmsg = false;
1600

    
1601
	foreach (config_get_path('ipsec/phase1', []) as $ph1ent) {
1602
		if ($ph1ent['disabled']) {
1603
			continue;
1604
		}
1605
		if (interface_ipsec_vti_configure($ph1ent) &&
1606
			!$bootmsg && is_platform_booting()) {
1607
			echo gettext("Configuring IPsec VTI interfaces...");
1608
			$bootmsg = true;
1609
		}
1610
	}
1611
	if (is_platform_booting() && $bootmsg) {
1612
		echo gettext("done.") . "\n";
1613
	}
1614
}
1615

    
1616
function interfaces_configure() {
1617
	global $g;
1618

    
1619
	/* Set up our loopback interface */
1620
	interfaces_loopback_configure();
1621

    
1622
	/* create the unconfigured wireless clones */
1623
	interfaces_create_wireless_clones();
1624

    
1625
	/* set up LAGG virtual interfaces */
1626
	interfaces_lagg_configure();
1627

    
1628
	/* set up VLAN virtual interfaces */
1629
	interfaces_vlan_configure();
1630

    
1631
	interfaces_qinq_configure(false);
1632

    
1633
	$iflist = get_configured_interface_with_descr();
1634
	$delayed_list = array();
1635
	$bridge_list = array();
1636
	$track6_list = array();
1637
	$dhcp6c_list = array();
1638

    
1639
	/* This is needed to speedup interfaces on bootup. */
1640
	$reload = false;
1641
	if (!is_platform_booting()) {
1642
		$reload = true;
1643
	}
1644

    
1645
	foreach ($iflist as $if => $ifname) {
1646
		$realif = config_get_path("interfaces/{$if}/if","");
1647
		$ipaddrv6 = config_get_path("interfaces/{$if}/ipaddrv6", "");
1648
		if (strstr($realif, "bridge")) {
1649
			$bridge_list[$if] = $ifname;
1650
		} elseif (strstr($realif, "gre")) {
1651
			$delayed_list[$if] = $ifname;
1652
		} elseif (strstr($realif, "gif")) {
1653
			$delayed_list[$if] = $ifname;
1654
		} elseif (strstr($realif, "ovpn")) {
1655
			continue;
1656
		} elseif (strstr($realif, "ipsec")) {
1657
			continue;
1658
		} elseif ($ipaddrv6 == "track6") {
1659
			$track6_list[$if] = $ifname;
1660
		} else {
1661
			/* do not run dhcp6c if track interface does not exists
1662
			 * see https://redmine.pfsense.org/issues/3965
1663
			 * and https://redmine.pfsense.org/issues/11633 */
1664
			if ($ipaddrv6 == "dhcp6") {
1665
				$tr6list = link_interface_to_track6($if);
1666
				if (is_array($tr6list) && !empty($tr6list)) {
1667
					$dhcp6c_list[$if] = $ifname;
1668
					continue;
1669
				}
1670
			}
1671
			if (is_platform_booting()) {
1672
				printf(gettext("Configuring %s interface..."),
1673
				    $ifname);
1674
			}
1675

    
1676
			if (g_get('debug')) {
1677
				log_error(sprintf(gettext("Configuring %s"),
1678
				    $ifname));
1679
			}
1680
			interface_configure($if, $reload);
1681
			if (is_platform_booting()) {
1682
				echo gettext("done.") . "\n";
1683
			}
1684
		}
1685
	}
1686

    
1687
	/*
1688
	 * NOTE: The following function parameter consists of
1689
	 *	1 - Do not load gre/gif/bridge with parent/member as vip
1690
	 *	2 - Do load gre/gif/bridge with parent/member as vip
1691
	 */
1692

    
1693
	/* set up GRE virtual interfaces */
1694
	interfaces_tunnel_configure(1,'','gre');
1695

    
1696
	/* set up GIF virtual interfaces */
1697
	interfaces_tunnel_configure(1,'','gif');
1698

    
1699
	/* set up BRIDGE virtual interfaces */
1700
	interfaces_bridge_configure(1);
1701

    
1702
	foreach ($track6_list as $if => $ifname) {
1703
		if (is_platform_booting()) {
1704
			printf(gettext("Configuring %s interface..."), $ifname);
1705
		}
1706
		if (g_get('debug')) {
1707
			log_error(sprintf(gettext("Configuring %s"), $ifname));
1708
		}
1709

    
1710
		interface_configure($if, $reload);
1711

    
1712
		if (is_platform_booting()) {
1713
			echo gettext("done.") . "\n";
1714
		}
1715
	}
1716

    
1717
	/* bring up vip interfaces */
1718
	interfaces_vips_configure();
1719

    
1720
	/* set up GRE virtual interfaces */
1721
	interfaces_tunnel_configure(2,'','gre');
1722

    
1723
	/* set up GIF virtual interfaces */
1724
	interfaces_tunnel_configure(2,'','gif');
1725

    
1726
	foreach ($delayed_list as $if => $ifname) {
1727
		if (is_platform_booting()) {
1728
			printf(gettext("Configuring %s interface..."), $ifname);
1729
		}
1730
		if (g_get('debug')) {
1731
			log_error(sprintf(gettext("Configuring %s"), $ifname));
1732
		}
1733

    
1734
		interface_configure($if, $reload);
1735

    
1736
		if (is_platform_booting()) {
1737
			echo gettext("done.") . "\n";
1738
		}
1739
	}
1740

    
1741
	/* set up BRIDGE virtual interfaces */
1742
	interfaces_bridge_configure(2);
1743

    
1744
	foreach ($bridge_list as $if => $ifname) {
1745
		if (is_platform_booting()) {
1746
			printf(gettext("Configuring %s interface..."), $ifname);
1747
		}
1748
		if (g_get('debug')) {
1749
			log_error(sprintf(gettext("Configuring %s"), $ifname));
1750
		}
1751

    
1752
		// bridge interface needs reconfigure, then re-add VIPs, to ensure find_interface_ip is correct.
1753
		// redmine #3997
1754
		interface_reconfigure($if, $reload);
1755
		interfaces_vips_configure($if);
1756

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

    
1762
	foreach ($dhcp6c_list as $if => $ifname) {
1763
		if (is_platform_booting()) {
1764
			printf(gettext("Configuring %s interface..."), $ifname);
1765
		}
1766
		if (g_get('debug')) {
1767
			log_error(sprintf(gettext("Configuring %s"), $ifname));
1768
		}
1769

    
1770
		interface_configure($if, $reload);
1771

    
1772
		if (is_platform_booting()) {
1773
			echo gettext("done.") . "\n";
1774
		}
1775
	}
1776

    
1777
	/* set up IPsec VTI interfaces */
1778
	interfaces_ipsec_vti_configure();
1779

    
1780
	/* configure interface groups */
1781
	interfaces_group_setup();
1782

    
1783
	if (!is_platform_booting()) {
1784
		/* reconfigure static routes (kernel may have deleted them) */
1785
		system_routing_configure();
1786

    
1787
		/* reload IPsec tunnels */
1788
		ipsec_configure();
1789

    
1790
		/* restart dns servers (defering dhcpd reload) */
1791
		if (config_get_path('dnsmasq/enable')) {
1792
			services_dnsmasq_configure(false);
1793
		}
1794
		if (config_get_path('unbound/enable')) {
1795
			services_unbound_configure(false);
1796
		}
1797

    
1798
		/* reload dhcpd (interface enabled/disabled status may have changed) */
1799
		services_dhcpd_configure();
1800
	}
1801

    
1802
	return 0;
1803
}
1804

    
1805
function interface_reconfigure($interface = "wan", $reloadall = false) {
1806
	interface_bring_down($interface);
1807
	interface_configure($interface, $reloadall);
1808
}
1809

    
1810
function interface_vip_bring_down($vip) {
1811
	global $g;
1812

    
1813
	$vipif = get_real_interface($vip['interface']);
1814
	switch ($vip['mode']) {
1815
		case "proxyarp":
1816
			if (file_exists("{$g['varrun_path']}/choparp_{$vipif}.pid")) {
1817
				killbypid("{$g['varrun_path']}/choparp_{$vipif}.pid");
1818
			}
1819
			break;
1820
		case "ipalias":
1821
			if (does_interface_exist($vipif)) {
1822
				if (is_ipaddrv6($vip['subnet'])) {
1823
					mwexec("/sbin/ifconfig {$vipif} inet6 " . escapeshellarg($vip['subnet']) . " -alias");
1824
				} else {
1825
					pfSense_interface_deladdress($vipif, $vip['subnet']);
1826
				}
1827
			}
1828
			break;
1829
		case "carp":
1830
			/* XXX: Is enough to delete ip address? */
1831
			if (does_interface_exist($vipif)) {
1832
				if (is_ipaddrv6($vip['subnet'])) {
1833
					mwexec("/sbin/ifconfig {$vipif} inet6 " . escapeshellarg($vip['subnet']) . " delete");
1834
				} else {
1835
					pfSense_interface_deladdress($vipif, $vip['subnet']);
1836
				}
1837
			}
1838
			break;
1839
	}
1840
}
1841

    
1842
function interface_bring_down($interface = "wan", $destroy = false, $ifacecfg = false) {
1843
	global $g;
1844

    
1845
	$iface = config_get_path("interfaces/{$interface}");
1846
	if (!$iface) {
1847
		return;
1848
	}
1849

    
1850
	if (g_get('debug')) {
1851
		log_error(sprintf(gettext('Calling interface down for interface %1$s, destroy is %2$s'), $interface, (($destroy) ? 'true' : 'false')));
1852
	}
1853

    
1854
	/*
1855
	 * NOTE: The $realifv6 is needed when WANv4 is type PPP and v6 is DHCP and the option v6 from v4 is used.
1856
	 * In this case the real $realif of v4 is different from that of v6 for operation.
1857
	 * Keep this in mind while doing changes here!
1858
	 */
1859
	if ($ifacecfg === false) {
1860
		$ifcfg = $iface;
1861
		$ppps = config_get_path('ppps/ppp', []);
1862
		$realif = get_real_interface($interface);
1863
		$realifv6 = get_real_interface($interface, "inet6", true);
1864
	} elseif (!is_array($ifacecfg)) {
1865
		log_error(gettext("Wrong parameters used during interface_bring_down"));
1866
		$ifcfg = $iface;
1867
		$ppps = config_get_path('ppps/ppp', []);
1868
		$realif = get_real_interface($interface);
1869
		$realifv6 = get_real_interface($interface, "inet6", true);
1870
	} else {
1871
		$ifcfg = $ifacecfg['ifcfg'];
1872
		$ppps = $ifacecfg['ppps'];
1873
		if (isset($ifacecfg['ifcfg']['realif'])) {
1874
			$realif = $ifacecfg['ifcfg']['realif'];
1875
			/* XXX: Any better way? */
1876
			$realifv6 = $realif;
1877
		} else {
1878
			$realif = get_real_interface($interface);
1879
			$realifv6 = get_real_interface($interface, "inet6", true);
1880
		}
1881
	}
1882

    
1883
	/* check all gateways, including dynamic,
1884
	 * see https://redmine.pfsense.org/issues/12920 */
1885
	foreach (get_gateways(GW_CACHE_DISABLED) as $gw) {
1886
		if ($gw['friendlyiface'] == $interface) {
1887
			$restart_gateways_monitor = true;
1888
			break;
1889
		}
1890
	}
1891

    
1892
	switch ($ifcfg['ipaddr']) {
1893
		case "ppp":
1894
		case "pppoe":
1895
		case "pptp":
1896
		case "l2tp":
1897
			if (is_array($ppps) && count($ppps)) {
1898
				foreach ($ppps as  $ppp) {
1899
					if ($realif == $ppp['if']) {
1900
						if (isset($ppp['ondemand']) && !$destroy) {
1901
							send_event("interface reconfigure {$interface}");
1902
							break;
1903
						}
1904
						if (file_exists("{$g['varrun_path']}/{$ppp['type']}_{$interface}.pid")) {
1905
							killbypid("{$g['varrun_path']}/{$ppp['type']}_{$interface}.pid");
1906
							sleep(2);
1907
						}
1908
						unlink_if_exists("{$g['varetc_path']}/mpd_{$interface}.conf");
1909
						break;
1910
					}
1911
				}
1912
			}
1913
			break;
1914
		case "dhcp":
1915
			kill_dhclient_process($realif);
1916
			unlink_if_exists("{$g['varetc_path']}/dhclient_{$interface}.conf");
1917
			if (does_interface_exist("$realif")) {
1918
				mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " delete", true);
1919
				interface_vip_cleanup($interface, "inet4");
1920
				if ($destroy == true) {
1921
					pfSense_interface_flags($realif, -IFF_UP);
1922
				}
1923
				mwexec("/usr/sbin/arp -d -i " . escapeshellarg($realif) . " -a");
1924
			}
1925
			break;
1926
		default:
1927
			if (does_interface_exist("$realif")) {
1928
				mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " delete", true);
1929
				interface_vip_cleanup($interface, "inet4");
1930
				if ($destroy == true) {
1931
					pfSense_interface_flags($realif, -IFF_UP);
1932
				}
1933
				mwexec("/usr/sbin/arp -d -i " . escapeshellarg($realif) . " -a");
1934
			}
1935
			break;
1936
	}
1937

    
1938
	$track6 = array();
1939
	switch ($ifcfg['ipaddrv6']) {
1940
		case "slaac":
1941
		case "dhcp6":
1942
			interface_dhcpv6_configure($interface, $ifcfg, true);
1943
			if (does_interface_exist($realifv6)) {
1944
				mwexec("/sbin/ifconfig " . escapeshellarg($realifv6) . " inet6 -accept_rtadv");
1945
				$ip6 = find_interface_ipv6($realifv6);
1946
				if (is_ipaddrv6($ip6) && $ip6 != "::") {
1947
					mwexec("/sbin/ifconfig " . escapeshellarg($realifv6) . " inet6 {$ip6} delete", true);
1948
				}
1949
				interface_vip_cleanup($interface, "inet6");
1950
				if ($destroy == true) {
1951
					pfSense_interface_flags($realif, -IFF_UP);
1952
				}
1953
			}
1954
			$track6 = link_interface_to_track6($interface);
1955
			break;
1956
		case "6rd":
1957
		case "6to4":
1958
			$realif = "{$interface}_stf";
1959
			if (does_interface_exist("$realif")) {
1960
				/* destroy stf interface if tunnel is being disabled or tunnel type is being changed */
1961
				if (($ifcfg['ipaddrv6'] == '6rd' && (!isset($iface['ipaddrv6']) || $iface['ipaddrv6'] != '6rd')) ||
1962
				    ($ifcfg['ipaddrv6'] == '6to4' && (!isset($iface['ipaddrv6']) || $iface['ipaddrv6'] != '6to4'))) {
1963
					$destroy = true;
1964
				} else {
1965
					/* get_interface_ipv6() returns empty value if interface is being disabled */
1966
					$ip6 = get_interface_ipv6($interface);
1967
					if (is_ipaddrv6($ip6)) {
1968
						mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 {$ip6} delete", true);
1969
					}
1970
				}
1971
				interface_vip_cleanup($interface, "inet6");
1972
				if ($destroy == true) {
1973
					pfSense_interface_flags($realif, -IFF_UP);
1974
				}
1975
			}
1976
			$track6 = link_interface_to_track6($interface);
1977
			break;
1978
		default:
1979
			if (does_interface_exist("$realif")) {
1980
				$ip6 = get_interface_ipv6($interface);
1981
				if (is_ipaddrv6($ip6)) {
1982
					mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 {$ip6} delete", true);
1983
				}
1984
				if (!empty($ifcfg['ipaddrv6']) && is_ipaddrv6($ifcfg['ipaddrv6'])) {
1985
					mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 {$ifcfg['ipaddrv6']} delete", true);
1986
				}
1987
				interface_vip_cleanup($interface, "inet6");
1988
				if ($destroy == true) {
1989
					pfSense_interface_flags($realif, -IFF_UP);
1990
				}
1991
				//mwexec("/usr/sbin/arp -d -i " . escapeshellarg($realif) . " -a");
1992
			}
1993
			$track6 = link_interface_to_track6($interface);
1994
			break;
1995
	}
1996

    
1997
	if (!empty($track6) && is_array($track6)) {
1998
		if (!function_exists('services_dhcpd_configure')) {
1999
			require_once('services.inc');
2000
		}
2001
		/* Bring down radvd and dhcp6 on these interfaces */
2002
		services_dhcpd_configure('inet6');
2003
	}
2004

    
2005
	/* remove interface up file if it exists */
2006
	unlink_if_exists("{$g['tmp_path']}/{$realif}up");
2007
	unlink_if_exists("{$g['vardb_path']}/{$interface}ip");
2008
	unlink_if_exists("{$g['vardb_path']}/{$interface}ipv6");
2009
	if (file_exists("{$g['tmp_path']}/{$realif}_router")) {
2010
		rename("{$g['tmp_path']}/{$realif}_router", "{$g['tmp_path']}/{$realif}_router.last");
2011
	}
2012
	if (file_exists("{$g['tmp_path']}/{$realif}_routerv6")) {
2013
		rename("{$g['tmp_path']}/{$realif}_routerv6", "{$g['tmp_path']}/{$realif}_routerv6.last");
2014
	}
2015
	unlink_if_exists("{$g['varetc_path']}/nameserver_{$realif}");
2016
	unlink_if_exists("{$g['varetc_path']}/nameserver_v6{$interface}");
2017
	unlink_if_exists("{$g['varetc_path']}/searchdomain_{$realif}");
2018
	unlink_if_exists("{$g['varetc_path']}/searchdomain_v6{$interface}");
2019
	unlink_if_exists("{$g['tmp_path']}/{$interface}_upstart4");
2020
	unlink_if_exists("{$g['tmp_path']}/{$interface}_upstart6");
2021

    
2022
	/* hostapd and wpa_supplicant do not need to be running when the interface is down.
2023
	 * They will also use 100% CPU if running after the wireless clone gets deleted. */
2024
	if (is_array($ifcfg['wireless'])) {
2025
		kill_hostapd($realif);
2026
		mwexec(kill_wpasupplicant($realif));
2027
		unlink_if_exists("{$g['varetc_path']}/wpa_supplicant_{$realif}.*");
2028
		unlink_if_exists("{$g['varetc_path']}/hostapd_{$realif}.conf");
2029
	}
2030

    
2031
	if ($destroy == true) {
2032
		if (preg_match("/^[a-z0-9]+^tun|^ovpn|^ipsec|^gif|^gre|^lagg|^bridge|vlan|_stf$/i", $realif)) {
2033
			pfSense_interface_destroy($realif);
2034

    
2035
			/* Invalidate cache */
2036
			get_interface_arr(true);
2037
		}
2038
	}
2039

    
2040
	/* If interface has a gateway, we need to bounce dpinger to keep dpinger happy */
2041
	if ($restart_gateways_monitor) {
2042
		setup_gateways_monitor();
2043
	}
2044

    
2045
	return;
2046
}
2047

    
2048
function interfaces_carp_set_maintenancemode($carp_maintenancemode) {
2049
	if (config_path_enabled('','virtualip_carp_maintenancemode') && ($carp_maintenancemode == false)) {
2050
		config_del_path('virtualip_carp_maintenancemode');
2051
		write_config("Leave CARP maintenance mode");
2052
	} elseif (!config_path_enabled('','virtualip_carp_maintenancemode') && ($carp_maintenancemode == true)) {
2053
		config_set_path('virtualip_carp_maintenancemode', true);
2054
		write_config(gettext("Enter CARP maintenance mode"));
2055
	}
2056

    
2057
	foreach (config_get_path('virtualip/vip', []) as $vip) {
2058
		if ($vip['mode'] == "carp") {
2059
			interface_carp_configure($vip, true);
2060
		}
2061
	}
2062
}
2063

    
2064
function interface_wait_tentative($interface, $timeout = 10) {
2065
	if (!does_interface_exist($interface)) {
2066
		return false;
2067
	}
2068

    
2069
	$time = 0;
2070
	while ($time <= $timeout) {
2071
		$if = get_interface_addresses($interface);
2072
		if (!isset($if['tentative'])) {
2073
			return true;
2074
		}
2075
		sleep(1);
2076
		$time++;
2077
	}
2078

    
2079
	return false;
2080
}
2081

    
2082
function interface_isppp_type($interface) {
2083
	$iface = config_get_path("interfaces/{$interface}");
2084
	if (!is_array($iface)) {
2085
		return false;
2086
	}
2087

    
2088
	switch ($iface['ipaddr']) {
2089
		case 'pptp':
2090
		case 'l2tp':
2091
		case 'pppoe':
2092
		case 'ppp':
2093
			return true;
2094
			break;
2095
		default:
2096
			return false;
2097
			break;
2098
	}
2099
}
2100

    
2101
function interfaces_ptpid_used($ptpid) {
2102
	$ppps = config_get_path('ppps/ppp');
2103
	if (is_array($ppps)) {
2104
		foreach ($ppps as $settings) {
2105
			if ($ptpid == $settings['ptpid']) {
2106
				return true;
2107
			}
2108
		}
2109
	}
2110

    
2111
	return false;
2112
}
2113

    
2114
function interfaces_ptpid_next() {
2115
	$ptpid = 0;
2116
	while (interfaces_ptpid_used($ptpid)) {
2117
		$ptpid++;
2118
	}
2119

    
2120
	return $ptpid;
2121
}
2122

    
2123
function getMPDCRONSettings($pppif) {
2124
	global $g;
2125

    
2126
	$cron_cmd_file = "{$g['varetc_path']}/pppoe_restart_{$pppif}";
2127
	foreach (config_get_path('cron/item', []) as $i => $item) {
2128
		if (stripos($item['command'], $cron_cmd_file) !== false) {
2129
			return array("ID" => $i, "ITEM" => $item);
2130
		}
2131
	}
2132

    
2133
	return NULL;
2134
}
2135

    
2136
function handle_pppoe_reset($post_array) {
2137
	global $g;
2138

    
2139
	$pppif = "{$post_array['type']}{$post_array['ptpid']}";
2140
	$cron_cmd_file = "{$g['varetc_path']}/pppoe_restart_{$pppif}";
2141

    
2142
	if (!is_array(config_get_path('cron/item'))) {
2143
		if (config_set_path('cron/item', array()) === null)
2144
			log_error(gettext('Cron section in config root is invalid.'));
2145
			return;
2146
	}
2147

    
2148
	$itemhash = getMPDCRONSettings($pppif);
2149

    
2150
	// reset cron items if necessary and return
2151
	if (empty($post_array['pppoe-reset-type'])) {
2152
		if (isset($itemhash)) {
2153
			config_del_path("cron/item/{$itemhash['ID']}");
2154
		}
2155
		sigkillbypid("{$g['varrun_path']}/cron.pid", "HUP");
2156
		return;
2157
	}
2158

    
2159
	if (empty($itemhash)) {
2160
		$itemhash = array();
2161
	}
2162
	$item = array();
2163
	if (isset($post_array['pppoe-reset-type']) && $post_array['pppoe-reset-type'] == "custom") {
2164
		$item['minute'] = (strlen($post_array['pppoe_resetminute']) > 0) ? $post_array['pppoe_resetminute'] : "*";
2165
		$item['hour'] = (strlen($post_array['pppoe_resethour']) > 0) ? $post_array['pppoe_resethour'] : "*";
2166
		if (isset($post_array['pppoe_resetdate']) && $post_array['pppoe_resetdate'] <> "") {
2167
			$date = explode("/", $post_array['pppoe_resetdate']);
2168
			$item['mday'] = $date[1];
2169
			$item['month'] = $date[0];
2170
		} else {
2171
			$item['mday'] = "*";
2172
			$item['month'] = "*";
2173
		}
2174
		$item['wday'] = "*";
2175
		$item['who'] = "root";
2176
		$item['command'] = $cron_cmd_file;
2177
	} elseif (isset($post_array['pppoe-reset-type']) && $post_array['pppoe-reset-type'] == "preset") {
2178
		switch ($post_array['pppoe_pr_preset_val']) {
2179
			case "monthly":
2180
				$item['minute'] = "0";
2181
				$item['hour'] = "0";
2182
				$item['mday'] = "1";
2183
				$item['month'] = "*";
2184
				$item['wday'] = "*";
2185
				break;
2186
			case "weekly":
2187
				$item['minute'] = "0";
2188
				$item['hour'] = "0";
2189
				$item['mday'] = "*";
2190
				$item['month'] = "*";
2191
				$item['wday'] = "0";
2192
				break;
2193
			case "daily":
2194
				$item['minute'] = "0";
2195
				$item['hour'] = "0";
2196
				$item['mday'] = "*";
2197
				$item['month'] = "*";
2198
				$item['wday'] = "*";
2199
				break;
2200
			case "hourly":
2201
				$item['minute'] = "0";
2202
				$item['hour'] = "*";
2203
				$item['mday'] = "*";
2204
				$item['month'] = "*";
2205
				$item['wday'] = "*";
2206
				break;
2207
		} // end switch
2208
		$item['who'] = "root";
2209
		$item['command'] = $cron_cmd_file;
2210
	}
2211
	if (empty($item)) {
2212
		return;
2213
	}
2214
	if (isset($itemhash['ID'])) {
2215
		config_set_path("cron/item/{$itemhash['ID']}", $item);
2216
	} else {
2217
		config_set_path('cron/item/', $item);
2218
	}
2219
}
2220

    
2221
function restart_ppp_interfaces_using_interfaces($triggerinterfaces) {
2222
	$ppp_list = array();
2223
	foreach (config_get_path('ppps/ppp', []) as $ppp) {
2224
		// skip modems, e.g. /dev/cuaZ99.0
2225
		if ($ppp['type'] == 'ppp') {
2226
			continue;
2227
		}
2228
		$ports = array_filter(explode(",", $ppp['ports']));
2229
		foreach($ports as $port) {
2230
			foreach($triggerinterfaces as $vip) {
2231
				if ($port == "_vip{$vip['uniqid']}") {
2232
					$if = convert_real_interface_to_friendly_interface_name($ppp['if']);
2233
					if (empty($if)) {
2234
						continue;
2235
					}
2236
					$ppp_list[$if] = 1;
2237
				}
2238
			}
2239
		}
2240
	}
2241
	foreach(array_keys($ppp_list) as $pppif) {
2242
		interface_ppps_configure($pppif);
2243
	}
2244
}
2245

    
2246
/*
2247
 * This function can configure PPPoE, MLPPP (PPPoE), PPTP.
2248
 * It writes the mpd config file to /var/etc every time the link is opened.
2249
 */
2250
function interface_ppps_configure($interface) {
2251
	global $g;
2252

    
2253
	$iface = config_get_path("interfaces/{$interface}");
2254
	/* Return for unassigned interfaces. This is a minimum requirement. */
2255
	if (empty($iface)) {
2256
		return 0;
2257
	}
2258
	$ifcfg = $iface;
2259
	if (!isset($ifcfg['enable'])) {
2260
		return 0;
2261
	}
2262

    
2263
	// mpd5 requires a /var/spool/lock directory for PPP modem links.
2264
	if (!is_dir("/var/spool/lock")) {
2265
		mkdir("/var/spool/lock", 0777, true);
2266
	}
2267
	// mpd5 modem chat script expected in the same directory as the mpd_xxx.conf files
2268
	if (!file_exists("{$g['varetc_path']}/mpd.script")) {
2269
		@symlink("/usr/local/sbin/mpd.script", "{$g['varetc_path']}/mpd.script");
2270
	}
2271

    
2272
	$pppid = "";
2273
	$ppp = array();
2274
	$ppps = config_get_path('ppps/ppp');
2275
	if (is_array($ppps) && count($ppps)) {
2276
		foreach ($ppps as $pppid => $ppp) {
2277
			if ($ifcfg['if'] == $ppp['if']) {
2278
				break;
2279
			}
2280
		}
2281
	}
2282
	if (!$ppp || $ifcfg['if'] != $ppp['if']) {
2283
		log_error(sprintf(gettext("Can't find PPP config for %s in interface_ppps_configure()."), $ifcfg['if']));
2284
		return 0;
2285
	}
2286
	$pppif = $ifcfg['if'];
2287
	if ($ppp['type'] == "ppp") {
2288
		$type = "modem";
2289
	} else {
2290
		$type = $ppp['type'];
2291
	}
2292

    
2293
	$confports = array_filter(explode(',', $ppp['ports']));
2294
	if ($type == "modem") {
2295
		$ports = $confports;
2296
	} else {
2297
		$ports = array();
2298
		foreach ($confports as $pid => $port) {
2299
			if (strstr($port, "_vip")) {
2300
				if (get_carp_interface_status($port) != "MASTER") {
2301
					continue;
2302
				}
2303
			}
2304
			$ports[$pid] = get_real_interface($port);
2305
			if (empty($ports[$pid])) {
2306
				return 0;
2307
			}
2308
		}
2309
	}
2310
	$localips = explode(',', $ppp['localip']);
2311
	$gateways = explode(',', $ppp['gateway']);
2312
	$subnets = explode(',', $ppp['subnet']);
2313

    
2314
	/* We bring up the parent interface first because if DHCP is configured on the parent we need
2315
	 * to obtain an address first so we can write it in the mpd .conf file for PPTP and L2TP configs
2316
	 */
2317
	foreach ($ports as $pid => $port) {
2318
		switch ($ppp['type']) {
2319
			case "pppoe":
2320
				/* Bring the parent interface up */
2321
				interfaces_bring_up($port);
2322
				/* Enable setautosrc to automatically change mac address if parent interface's changes */
2323
				$ngif = str_replace(".", "_", $port);
2324
				mwexec("/usr/sbin/ngctl msg {$ngif}: setautosrc 1");
2325
				break;
2326
			case "pptp":
2327
			case "l2tp":
2328
				/* configure interface */
2329
				if (is_ipaddr($localips[$pid])) {
2330
					// Manually configure interface IP/subnet
2331
					pfSense_interface_setaddress($port, "{$localips[$pid]}/{$subnets[$pid]}");
2332
					interfaces_bring_up($port);
2333
				} elseif (empty($localips[$pid])) {
2334
					$localips[$pid] = get_interface_ip($port); // try to get the interface IP from the port
2335
				}
2336

    
2337
				if (!is_ipaddr($localips[$pid])) {
2338
					log_error(sprintf(gettext("Could not get a Local IP address for PPTP/L2TP link on %s in interfaces_ppps_configure. Using 0.0.0.0 ip!"), $port));
2339
					$localips[$pid] = "0.0.0.0";
2340
				}
2341
				if (!g_get('booting') && !is_ipaddr($gateways[$pid]) && is_hostname($gateways[$pid])) {
2342
					$gateways[$pid] = gethostbyname($gateways[$pid]);
2343
				}
2344
				if (!is_ipaddr($gateways[$pid])) {
2345
					log_error(sprintf(gettext('Could not get a PPTP/L2TP Remote IP address from %1$s for %2$s in interfaces_ppps_configure.'), $localips[$pid], $ppp['gateway']));
2346
					return 0;
2347
				}
2348
				break;
2349
			case "ppp":
2350
				if (!file_exists("{$port}")) {
2351
					log_error(sprintf(gettext("Device %s does not exist. PPP link cannot start without the modem device."), $port));
2352
					return 0;
2353
				}
2354
				break;
2355
			default:
2356
				log_error(sprintf(gettext("Unknown %s configured as ppp interface."), $type));
2357
				break;
2358
		}
2359
	}
2360

    
2361
	if (isset($ppp['pppoe-multilink-over-singlelink']) ||
2362
	    (is_array($ports) && count($ports) > 1)) {
2363
		$multilink = "enable";
2364
	} else {
2365
		$multilink = "disable";
2366
	}
2367

    
2368
	if ($type == "modem") {
2369
		if (is_ipaddr($ppp['localip'])) {
2370
			$localip = $ppp['localip'];
2371
		} else {
2372
			$localip = '0.0.0.0';
2373
		}
2374

    
2375
		if (is_ipaddr($ppp['gateway'])) {
2376
			$gateway = $ppp['gateway'];
2377
		} else {
2378
			$gateway = "10.64.64.{$pppid}";
2379
		}
2380
		$ranges = "{$localip}/0 {$gateway}/0";
2381

    
2382
		if (empty($ppp['apnum'])) {
2383
			$ppp['apnum'] = 1;
2384
		}
2385
	} else {
2386
		$ranges = "0.0.0.0/0 0.0.0.0/0";
2387
	}
2388

    
2389
	if (isset($ppp['ondemand'])) {
2390
		$ondemand = "enable";
2391
	} else {
2392
		$ondemand = "disable";
2393
	}
2394
	if (!isset($ppp['idletimeout'])) {
2395
		$ppp['idletimeout'] = 0;
2396
	}
2397

    
2398
	if (empty($ppp['username']) && $type == "modem") {
2399
		$ppp['username'] = "user";
2400
		$ppp['password'] = "none";
2401
	}
2402
	if (empty($ppp['password']) && $type == "modem") {
2403
		$passwd = "none";
2404
	} else {
2405
		$passwd = base64_decode($ppp['password']);
2406
	}
2407

    
2408
	$bandwidths = explode(',', $ppp['bandwidth']);
2409
	$defaultmtu = "1492";
2410
	if (!empty($ifcfg['mtu'])) {
2411
		$defaultmtu = intval($ifcfg['mtu']);
2412
	}
2413
	if (isset($ppp['mtu'])) {
2414
		$mtus = explode(',', $ppp['mtu']);
2415
	}
2416
	if (isset($ppp['mru'])) {
2417
		$mrus = explode(',', $ppp['mru']);
2418
	}
2419
	if (isset($ppp['mrru'])) {
2420
		$mrrus = explode(',', $ppp['mrru']);
2421
	}
2422
	if (!empty($ifcfg['ipaddrv6'])) {
2423
		$ipv6cp = "set bundle enable ipv6cp";
2424
	}
2425

    
2426
	// Construct the mpd.conf file
2427
	$mpdconf = <<<EOD
2428
startup:
2429
	# configure the console
2430
	set console close
2431
	# configure the web server
2432
	set web close
2433

    
2434
default:
2435
{$ppp['type']}client:
2436
	create bundle static {$interface}
2437
	set bundle period 6
2438
	set bundle lowat 0
2439
	set bundle hiwat 0
2440
	set bundle min-con 3
2441
	set bundle min-dis 6
2442
	set bundle enable bw-manage
2443
	{$ipv6cp}
2444
	set iface name {$pppif}
2445

    
2446
EOD;
2447

    
2448
	if (isset($ifcfg['descr'])) {
2449
		$mpdconf .= <<<EOD
2450
	set iface description "{$ifcfg['descr']}"
2451

    
2452
EOD;
2453
	}
2454
	$setdefaultgw = false;
2455
	$defgw4 = lookup_gateway_or_group_by_name(config_get_path('gateways/defaultgw4'));
2456
//	$defgw6 = lookup_gateway_or_group_by_name(config_get_path('gateways/defaultgw6'));
2457
	if ($defgw4['interface'] == $interface) {
2458
		$setdefaultgw = true;
2459
	}
2460

    
2461
/* Omit this, we maintain the default route by other means, and it causes problems with
2462
 * default gateway switching. See redmine #1837 for original issue
2463
 * re-enabling this for now to fix issue with missing default gateway with PPPoE in some
2464
 * edge case. redmine #6495 open to address.
2465
 */
2466
	if ($setdefaultgw == true) {
2467
		$mpdconf .= <<<EOD
2468
	set iface route default
2469

    
2470
EOD;
2471
	}
2472

    
2473
	$mpdconf .= <<<EOD
2474
	set iface {$ondemand} on-demand
2475
	set iface idle {$ppp['idletimeout']}
2476

    
2477
EOD;
2478

    
2479
	if (isset($ppp['ondemand'])) {
2480
		$mpdconf .= <<<EOD
2481
	set iface addrs 10.10.1.1 10.10.1.2
2482

    
2483
EOD;
2484
	}
2485

    
2486
	if (isset($ppp['mtu-override']) &&
2487
	    !isset($ppp['pppoe-multilink-over-singlelink'])) {
2488
		/* Find the smaller MTU set on ports */
2489
		$mtu = $defaultmtu;
2490
		foreach ($ports as $pid => $port) {
2491
			if (empty($mtus[$pid])) {
2492
				$mtus[$pid] = $defaultmtu;
2493
			}
2494
			if ($type == "pppoe") {
2495
				if ($mtus[$pid] > (get_interface_mtu($port) - 8)) {
2496
					$mtus[$pid] = get_interface_mtu($port) - 8;
2497
				}
2498
			}
2499
			if ($mtu > $mtus[$pid]) {
2500
				$mtu = $mtus[$pid];
2501
			}
2502
		}
2503
		$mpdconf .= <<<EOD
2504
	set iface mtu {$mtu} override
2505

    
2506
EOD;
2507
	}
2508

    
2509
	if (isset($ppp['tcpmssfix'])) {
2510
		$tcpmss = "disable";
2511
	} else {
2512
		$tcpmss = "enable";
2513
	}
2514
	$mpdconf .= <<<EOD
2515
	set iface {$tcpmss} tcpmssfix
2516

    
2517
EOD;
2518

    
2519
	$mpdconf .= <<<EOD
2520
	set iface up-script /usr/local/sbin/ppp-linkup
2521
	set iface down-script /usr/local/sbin/ppp-linkdown
2522
	set ipcp ranges {$ranges}
2523

    
2524
EOD;
2525
	if (isset($ppp['vjcomp'])) {
2526
		$mpdconf .= <<<EOD
2527
	set ipcp no vjcomp
2528

    
2529
EOD;
2530
	}
2531

    
2532
	if (config_path_enabled('system', 'dnsallowoverride')) {
2533
		$mpdconf .= <<<EOD
2534
	set ipcp enable req-pri-dns
2535
	set ipcp enable req-sec-dns
2536

    
2537
EOD;
2538
	}
2539

    
2540
	if (!isset($ppp['verbose_log'])) {
2541
		$mpdconf .= <<<EOD
2542
	#log -bund -ccp -chat -iface -ipcp -lcp -link
2543

    
2544
EOD;
2545
	}
2546

    
2547
	foreach ($ports as $pid => $port) {
2548
		if ($type != 'modem') {
2549
			$port = get_real_interface($port);
2550
		}
2551
		$mpdconf .= <<<EOD
2552

    
2553
	create link static {$interface}_link{$pid} {$type}
2554
	set link action bundle {$interface}
2555
	set link {$multilink} multilink
2556
	set link keep-alive 10 60
2557
	set link max-redial 0
2558

    
2559
EOD;
2560
		if (isset($ppp['shortseq'])) {
2561
			$mpdconf .= <<<EOD
2562
	set link no shortseq
2563

    
2564
EOD;
2565
		}
2566

    
2567
		if (isset($ppp['acfcomp'])) {
2568
			$mpdconf .= <<<EOD
2569
	set link no acfcomp
2570

    
2571
EOD;
2572
		}
2573

    
2574
		if (isset($ppp['protocomp'])) {
2575
			$mpdconf .= <<<EOD
2576
	set link no protocomp
2577

    
2578
EOD;
2579
		}
2580

    
2581
		$mpdconf .= <<<EOD
2582
	set link disable chap pap
2583
	set link accept chap pap eap
2584
	set link disable incoming
2585

    
2586
EOD;
2587

    
2588

    
2589
		if (!empty($bandwidths[$pid])) {
2590
			$mpdconf .= <<<EOD
2591
	set link bandwidth {$bandwidths[$pid]}
2592

    
2593
EOD;
2594
		}
2595

    
2596
		if (empty($mtus[$pid])) {
2597
			$mtus[$pid] = $defaultmtu;
2598
		}
2599
		if ($type == "pppoe") {
2600
			if ($mtus[$pid] > (get_interface_mtu($port) - 8)) {
2601
				$mtus[$pid] = get_interface_mtu($port) - 8;
2602
			}
2603
		}
2604
		if (!isset($ppp['pppoe-multilink-over-singlelink']) &&
2605
		    !isset($ppp['mtu-override']) &&
2606
		    !($type == "pppoe" && $mtus[$pid] > 1492)) {
2607
			// N.B. MTU for PPPoE with MTU > 1492 is set using pppoe max-payload - see below
2608
			$mpdconf .= <<<EOD
2609
	set link mtu {$mtus[$pid]}
2610

    
2611
EOD;
2612
		}
2613

    
2614
		if (!isset($ppp['pppoe-multilink-over-singlelink']) &&
2615
		    !isset($ppp['mtu-override']) &&
2616
		    !empty($mrus[$pid])) {
2617
			$mpdconf .= <<<EOD
2618
	set link mru {$mrus[$pid]}
2619

    
2620
EOD;
2621
		}
2622

    
2623
		if (!empty($mrrus[$pid])) {
2624
			$mpdconf .= <<<EOD
2625
	set link mrru {$mrrus[$pid]}
2626

    
2627
EOD;
2628
		}
2629

    
2630
		$mpdconf .= <<<EOD
2631
	set auth authname "{$ppp['username']}"
2632
	set auth password {$passwd}
2633

    
2634
EOD;
2635
		if ($type == "modem") {
2636
			$mpdconf .= <<<EOD
2637
	set modem device {$ppp['ports']}
2638
	set modem script DialPeer
2639
	set modem idle-script Ringback
2640
	set modem watch -cd
2641
	set modem var \$DialPrefix "DT"
2642
	set modem var \$Telephone "{$ppp['phone']}"
2643

    
2644
EOD;
2645
		}
2646
		if (isset($ppp['connect-timeout']) && $type == "modem") {
2647
			$mpdconf .= <<<EOD
2648
	set modem var \$ConnectTimeout "{$ppp['connect-timeout']}"
2649

    
2650
EOD;
2651
		}
2652
		if (isset($ppp['initstr']) && $type == "modem") {
2653
			$initstr = base64_decode($ppp['initstr']);
2654
			$mpdconf .= <<<EOD
2655
	set modem var \$InitString "{$initstr}"
2656

    
2657
EOD;
2658
		}
2659
		if (isset($ppp['simpin']) && $type == "modem") {
2660
			if ($ppp['pin-wait'] == "") {
2661
				$ppp['pin-wait'] = 0;
2662
			}
2663
			$mpdconf .= <<<EOD
2664
	set modem var \$SimPin "{$ppp['simpin']}"
2665
	set modem var \$PinWait "{$ppp['pin-wait']}"
2666

    
2667
EOD;
2668
		}
2669
		if (isset($ppp['apn']) && $type == "modem") {
2670
			$mpdconf .= <<<EOD
2671
	set modem var \$APN "{$ppp['apn']}"
2672
	set modem var \$APNum "{$ppp['apnum']}"
2673

    
2674
EOD;
2675
		}
2676
		if ($type == "pppoe") {
2677
			$hostuniq = '';
2678
			if (!empty($ppp['hostuniq'])) {
2679
				if (preg_match('/^0x[a-fA-F0-9]+$/', $ppp['hostuniq'])) {
2680
					$hostuniq = strtolower($ppp['hostuniq']) .'|';
2681
				} elseif (preg_match('/^[a-zA-Z0-9]+$/i', $ppp['hostuniq'])) {
2682
					$hostuniq = '0x' . bin2hex($ppp['hostuniq']) . '|';
2683
				}
2684
			}
2685
			// Send a null service name if none is set.
2686
			$provider = isset($ppp['provider']) ? $ppp['provider'] : "";
2687
			$mpdconf .= <<<EOD
2688
	set pppoe service "{$hostuniq}{$provider}"
2689

    
2690
EOD;
2691
		}
2692
		if (($type == "pppoe") && ($mtus[$pid] > 1492)) {
2693
			$mpdconf .= <<<EOD
2694
	set pppoe max-payload {$mtus[$pid]}
2695

    
2696
EOD;
2697
		}
2698
		if ($type == "pppoe") {
2699
			$mpdconf .= <<<EOD
2700
	set pppoe iface {$port}
2701

    
2702
EOD;
2703
		}
2704

    
2705
		if (($type == "l2tp") && !empty($ppp['secret'])) {
2706
			$secret = str_replace('"', '\"', base64_decode($ppp['secret']));
2707
			$mpdconf .= <<<EOD
2708
	set l2tp secret "{$secret}"
2709

    
2710
EOD;
2711
		}
2712

    
2713
		if (($type == "pptp") || ($type == "l2tp")) {
2714
			$mpdconf .= <<<EOD
2715
	set {$type} self {$localips[$pid]}
2716
	set {$type} peer {$gateways[$pid]}
2717

    
2718
EOD;
2719
		}
2720

    
2721
		$mpdconf .= "\topen\n";
2722
	} //end foreach ($port)
2723

    
2724

    
2725
	/* Generate mpd.conf. If mpd_[interface].conf exists in the conf path, then link to it instead of generating a fresh conf file. */
2726
	if (file_exists("{$g['conf_path']}/mpd_{$interface}.conf")) {
2727
		@symlink("{$g['conf_path']}/mpd_{$interface}.conf", "{$g['varetc_path']}/mpd_{$interface}.conf");
2728
	} else {
2729
		$fd = fopen("{$g['varetc_path']}/mpd_{$interface}.conf", "w");
2730
		if (!$fd) {
2731
			log_error(sprintf(gettext('Error: cannot open mpd_%1$s.conf in interface_ppps_configure().%2$s'), $interface, "\n"));
2732
			return 0;
2733
		}
2734
		// Write out mpd_ppp.conf
2735
		fwrite($fd, $mpdconf);
2736
		fclose($fd);
2737
		unset($mpdconf);
2738
	}
2739

    
2740
	// Create the uptime log if requested and if it doesn't exist already, or delete it if it is no longer requested.
2741
	if (isset($ppp['uptime'])) {
2742
		if (!file_exists("/conf/{$pppif}.log")) {
2743
			file_put_contents("/conf/{$pppif}.log", '');
2744
		}
2745
	} else {
2746
		if (file_exists("/conf/{$pppif}.log")) {
2747
			@unlink("/conf/{$pppif}.log");
2748
		}
2749
	}
2750

    
2751
	/* clean up old lock files */
2752
	foreach ($ports as $port) {
2753
		if (file_exists("{$g['var_path']}/spool/lock/LCK..{$port}")) {
2754
			unlink("{$g['var_path']}/spool/lock/LCK..{$port}");
2755
		}
2756
	}
2757

    
2758
	/* Set temporary bogon (RFC 5737) IPv4 addresses to work round mpd5 IPv6CP issue causing */
2759
	/* random IPv6 interface identifier during boot. More details at */
2760
	/* https://forum.netgate.com/post/592474 */
2761
	if (is_platform_booting()) {
2762
		$count = 0;
2763
		foreach (config_get_path('interfaces', []) as $tempifacename => $tempiface) {
2764
			if ((isset($tempiface['if'])) && (isset($tempiface['ipaddr']) || isset($tempiface['ipaddrv6'])) && !interface_isppp_type($tempifacename)) {
2765
				$tempaddr[$count]['if'] = $tempiface['if'];
2766
				$tempaddr[$count]['ipaddr'] = '192.0.2.' . strval (10 + ($count * 2)) . '/31';
2767
				mwexec('/sbin/ifconfig ' . escapeshellarg($tempaddr[$count]['if']) . ' inet ' . escapeshellarg($tempaddr[$count]['ipaddr']) . ' alias', true);
2768
				$count++;
2769
			}
2770
			// Maximum /31 is is x.y.z.254/31
2771
			if ($count > 122) {
2772
				break;
2773
			}
2774
		}
2775
		unset($count);
2776
	}
2777

    
2778
	/* fire up mpd */
2779
	mwexec("/usr/local/sbin/mpd5 -b -k -d {$g['varetc_path']} -f mpd_{$interface}.conf -p " .
2780
		escapeshellarg("{$g['varrun_path']}/{$ppp['type']}_{$interface}.pid") . " -s ppp " .
2781
		escapeshellarg("{$ppp['type']}client"));
2782

    
2783
	// Check for PPPoE periodic reset request
2784
	if ($type == "pppoe") {
2785
		if (!empty($ppp['pppoe-reset-type'])) {
2786
			interface_setup_pppoe_reset_file($ppp['if'], $interface);
2787
		} else {
2788
			interface_setup_pppoe_reset_file($ppp['if']);
2789
		}
2790
	}
2791
	/* wait for upto 30 seconds for the interface to appear (ppp(oe)) */
2792
	$i = 0;
2793
	while ($i < 10) {
2794
		if (does_interface_exist($ppp['if'], true)) {
2795
			break;
2796
		}
2797
		sleep(3);
2798
		$i++;
2799
	}
2800

    
2801
	/* Remove all temporary bogon IPv4 addresses */
2802
	if (is_array($tempaddr)) {
2803
		foreach ($tempaddr as $tempiface) {
2804
			if (isset($tempiface['if']) && isset($tempiface['ipaddr'])) {
2805
				mwexec('/sbin/ifconfig ' . escapeshellarg($tempiface['if']) . ' inet ' . escapeshellarg($tempiface['ipaddr']) . ' -alias', true);
2806
			}
2807
		}
2808
		unset ($tempaddr);
2809
	}
2810

    
2811
	/* we only support the 3gstats.php for huawei modems for now. Will add more later. */
2812
	/* We should be able to launch the right version for each modem */
2813
	/* We can also guess the mondev from the manufacturer */
2814
	exec("/usr/sbin/usbconfig | /usr/bin/egrep -ie '(huawei)'", $usbmodemoutput);
2815
	mwexec("/bin/ps auxww | /usr/bin/grep \"{$interface}\" | /usr/bin/grep \"[3]gstats\" | /usr/bin/awk '{print $2}' | /usr/bin/xargs kill");
2816
	foreach ($ports as $port) {
2817
		if (preg_match("/huawei/i", implode("\n", $usbmodemoutput))) {
2818
			$mondev = substr(basename($port), 0, -1);
2819
			$devlist = glob("/dev/{$mondev}?");
2820
			$mondev = basename(end($devlist));
2821
		}
2822
		if (preg_match("/zte/i", implode("\n", $usbmodemoutput))) {
2823
			$mondev = substr(basename($port), 0, -1) . "1";
2824
		}
2825
		if ($mondev != '') {
2826
			log_error(sprintf(gettext('Starting 3gstats.php on device \'%1$s\' for interface \'%2$s\''), $mondev, $interface));
2827
			mwexec_bg("/usr/local/bin/3gstats.php {$mondev} {$interface}");
2828
		}
2829
	}
2830

    
2831
	return 1;
2832
}
2833

    
2834
function interfaces_sync_setup() {
2835

    
2836
	if (config_get_path('system/developerspew')) {
2837
		$mt = microtime();
2838
		echo "interfaces_sync_setup() being called $mt\n";
2839
	}
2840

    
2841
	if (is_platform_booting()) {
2842
		echo gettext("Configuring CARP settings...");
2843
		mute_kernel_msgs();
2844
	}
2845

    
2846
	/* suck in configuration items */
2847
	if (!empty(config_get_path('hasync'))) {
2848
		$pfsyncenabled = config_get_path('hasync/pfsyncenabled');
2849
		$pfsyncinterface = config_get_path('hasync/pfsyncinterface');
2850
		$pfsyncpeerip = config_get_path('hasync/pfsyncpeerip');
2851
	}
2852

    
2853
	set_sysctl(array(
2854
		"net.inet.carp.preempt" => "1",
2855
		"net.inet.carp.log" => "1")
2856
	);
2857

    
2858
	if (!empty($pfsyncinterface)) {
2859
		$carp_sync_int = get_real_interface($pfsyncinterface);
2860
	}
2861

    
2862
	/* setup pfsync interface */
2863
	if (isset($carp_sync_int) and isset($pfsyncenabled)) {
2864
		if (is_ipaddr($pfsyncpeerip)) {
2865
			$syncpeer = "syncpeer {$pfsyncpeerip}";
2866
		} else {
2867
			$syncpeer = "-syncpeer";
2868
		}
2869

    
2870
		mwexec("/sbin/ifconfig pfsync0 syncdev {$carp_sync_int} " .
2871
		    "{$syncpeer} up");
2872
		mwexec("/sbin/ifconfig pfsync0 -defer");
2873

    
2874
		/*
2875
		 * XXX: Handle an issue with pfsync(4) and carp(4). In a
2876
		 * cluster carp will come up before pfsync(4) has updated and
2877
		 * so will cause issues for existing sessions.
2878
		 */
2879
		log_error(gettext("waiting for pfsync..."));
2880

    
2881
		$i = 0;
2882
		do {
2883
			sleep(1);
2884
			exec('/sbin/ifconfig pfsync0 | ' .
2885
				 '/usr/bin/grep -q "syncok: 0" 2>/dev/null', $output,
2886
				 $rc);
2887
			$i++;
2888
		} while ($rc != 0 && $i <= 30);
2889

    
2890
		log_error(sprintf(gettext("pfsync done in %s seconds."), $i));
2891
		log_error(gettext("Configuring CARP settings finalize..."));
2892
	} else {
2893
		mwexec("/sbin/ifconfig pfsync0 -syncdev -syncpeer down");
2894
	}
2895

    
2896
	/* Don't enable CARP if we haven't booted */
2897
	if (!is_platform_booting()) {
2898
		enable_carp();
2899
	}
2900

    
2901
	if (is_platform_booting()) {
2902
		unmute_kernel_msgs();
2903
		echo gettext("done.") . "\n";
2904
	}
2905
}
2906

    
2907
function interface_proxyarp_configure($interface = "") {
2908
	global $g;
2909
	if (config_get_path('system/developerspew')) {
2910
		$mt = microtime();
2911
		echo "interface_proxyarp_configure() being called $mt\n";
2912
	}
2913

    
2914
	/* kill any running choparp */
2915
	if (empty($interface)) {
2916
		killbyname("choparp");
2917
	} else {
2918
		$vipif = get_real_interface($interface);
2919
		if (file_exists("{$g['varrun_path']}/choparp_{$vipif}.pid")) {
2920
			killbypid("{$g['varrun_path']}/choparp_{$vipif}.pid");
2921
		}
2922
	}
2923

    
2924
	$paa = array();
2925
	$vips = config_get_path('virtualip/vip');
2926
	if (is_array($vips)) {
2927

    
2928
		/* group by interface */
2929
		foreach ($vips as $vipent) {
2930
			if ($vipent['mode'] === "proxyarp") {
2931
				if ($vipent['interface']) {
2932
					$proxyif = $vipent['interface'];
2933
				} else {
2934
					$proxyif = "wan";
2935
				}
2936

    
2937
				if (!empty($interface) && $interface != $proxyif) {
2938
					continue;
2939
				}
2940

    
2941
				if (!is_array($paa[$proxyif])) {
2942
					$paa[$proxyif] = array();
2943
				}
2944

    
2945
				$paa[$proxyif][] = $vipent;
2946
			}
2947
		}
2948
	}
2949

    
2950
	if (!empty($interface)) {
2951
		if (is_array($paa[$interface])) {
2952
			$paaifip = get_interface_ip($interface);
2953
			if (!is_ipaddr($paaifip)) {
2954
				return;
2955
			}
2956
			$vipif = get_real_interface($interface);
2957
			$args  = "-p {$g['varrun_path']}/choparp_{$vipif}.pid ";
2958
			$args .= $vipif . " auto";
2959
			foreach ($paa[$interface] as $paent) {
2960
				if (isset($paent['subnet'])) {
2961
					$args .= " " . escapeshellarg("{$paent['subnet']}/{$paent['subnet_bits']}");
2962
				} elseif (isset($paent['range'])) {
2963
					$args .= " " . escapeshellarg($paent['range']['from'] . "-" . $paent['range']['to']);
2964
				}
2965
			}
2966
			mwexec_bg("/usr/local/sbin/choparp " . $args);
2967
		}
2968
	} elseif (count($paa) > 0) {
2969
		foreach ($paa as $paif => $paents) {
2970
			$paaifip = get_interface_ip($paif);
2971
			if (!is_ipaddr($paaifip)) {
2972
				continue;
2973
			}
2974
			$vipif = get_real_interface($paif);
2975
			$args  = "-p {$g['varrun_path']}/choparp_{$vipif}.pid ";
2976
			$args .= $vipif . " auto";
2977
			foreach ($paents as $paent) {
2978
				if (isset($paent['subnet'])) {
2979
					$args .= " " . escapeshellarg("{$paent['subnet']}/{$paent['subnet_bits']}");
2980
				} elseif (isset($paent['range'])) {
2981
					$args .= " " . escapeshellarg($paent['range']['from'] . "-" . $paent['range']['to']);
2982
				}
2983
			}
2984
			mwexec_bg("/usr/local/sbin/choparp " . $args);
2985
		}
2986
	}
2987
}
2988

    
2989
function interface_vip_cleanup($interface, $inet = "all", $type = VIP_ALL) {
2990
	$vips = config_get_path('virtualip/vip');
2991
	if (is_array($vips)) {
2992
		foreach ($vips as $vip) {
2993

    
2994
			$iface = $vip['interface'];
2995
			if (substr($iface, 0, 4) == "_vip")
2996
				$iface = get_configured_vip_interface($vip['interface']);
2997
			if ($iface != $interface)
2998
				continue;
2999
			if ($type == VIP_CARP) {
3000
				if ($vip['mode'] != "carp")
3001
					continue;
3002
			} elseif ($type == VIP_IPALIAS) {
3003
				if ($vip['mode'] != "ipalias")
3004
					continue;
3005
			} else {
3006
				if ($vip['mode'] != "carp" && $vip['mode'] != "ipalias")
3007
					continue;
3008
			}
3009

    
3010
			if ($inet == "inet6" && is_ipaddrv6($vip['subnet']))
3011
				interface_vip_bring_down($vip);
3012
			elseif ($inet == "inet4" && is_ipaddrv4($vip['subnet']))
3013
				interface_vip_bring_down($vip);
3014
			elseif ($inet == "all")
3015
				interface_vip_bring_down($vip);
3016
		}
3017
	}
3018
}
3019

    
3020
function interfaces_vips_configure($interface = "") {
3021
	if (config_get_path('system/developerspew')) {
3022
		$mt = microtime();
3023
		echo "interfaces_vips_configure() being called $mt\n";
3024
	}
3025

    
3026
	$vips = config_get_path('virtualip/vip');
3027
	if (!is_array($vips)) {
3028
		return;
3029
	}
3030

    
3031
	$carp_configured = false;
3032
	$anyproxyarp = false;
3033
	foreach ($vips as $vip) {
3034
		if ($interface <> "" &&
3035
		    get_root_interface($vip['interface']) <> $interface) {
3036
			continue;
3037
		}
3038
		switch ($vip['mode']) {
3039
			case "proxyarp":
3040
				/*
3041
				 * nothing it is handled on
3042
				 * interface_proxyarp_configure()
3043
				 */
3044
				$anyproxyarp = true;
3045
				break;
3046
			case "ipalias":
3047
				interface_ipalias_configure($vip);
3048
				break;
3049
			case "carp":
3050
				if ($carp_configured == false) {
3051
					$carp_configured = true;
3052
				}
3053
				interface_carp_configure($vip);
3054
				break;
3055
		}
3056
	}
3057
	/* interfaces_sync_setup() is directly called in rc.bootup*/
3058
	if (!is_platform_booting() && $carp_configured == true) {
3059
		interfaces_sync_setup();
3060
	}
3061
	if ($anyproxyarp == true) {
3062
		interface_proxyarp_configure();
3063
	}
3064
}
3065

    
3066
function interface_ipalias_configure(&$vip) {
3067
	$gateway = '';
3068
	if ($vip['mode'] != 'ipalias') {
3069
		return;
3070
	}
3071

    
3072
	$realif = get_real_interface("_vip{$vip['uniqid']}");
3073
	if ($realif != "lo0") {
3074
		$if = convert_real_interface_to_friendly_interface_name($realif);
3075
		if (empty($if) || !config_path_enabled("interfaces/{$if}")) {
3076
			return;
3077
		}
3078
		if (is_pseudo_interface($realif)) {
3079
			if (is_ipaddrv4($vip['subnet'])) {
3080
				$gateway = get_interface_gateway($if);
3081
			} else {
3082
				$gateway = get_interface_gateway_v6($if);
3083
			}
3084
		}
3085
	}
3086

    
3087
	$af = 'inet';
3088
	if (is_ipaddrv6($vip['subnet'])) {
3089
		$af = 'inet6';
3090
	}
3091
	$iface = $vip['interface'];
3092
	$vhid = '';
3093
	if (substr($vip['interface'], 0, 4) == "_vip") {
3094
		$carpvip = get_configured_vip($vip['interface']);
3095
		$iface = $carpvip['interface'];
3096
		$vhid = "vhid {$carpvip['vhid']}";
3097
	}
3098
	mwexec("/sbin/ifconfig " . escapeshellarg($realif) ." {$af} ". escapeshellarg($vip['subnet']) ."/" . escapeshellarg($vip['subnet_bits']) . " alias {$gateway} {$vhid}");
3099
	unset($iface, $af, $realif, $carpvip, $vhid, $gateway);
3100
}
3101

    
3102
function interface_carp_configure(&$vip, $maintenancemode_only = false, $ipalias_reload = false) {
3103
	if (config_get_path('system/developerspew')) {
3104
		$mt = microtime();
3105
		echo "interface_carp_configure() being called $mt\n";
3106
	}
3107

    
3108
	if ($vip['mode'] != "carp") {
3109
		return;
3110
	}
3111

    
3112
	$realif = get_real_interface($vip['interface']);
3113
	if (!does_interface_exist($realif)) {
3114
		file_notice("CARP", sprintf(gettext(
3115
		    "Interface specified for the virtual IP address %s does not exist. Skipping this VIP."),
3116
		    $vip['subnet']), "Firewall: Virtual IP", "");
3117
		return;
3118
	}
3119
	if ($realif != "lo0") {
3120
		if (!config_path_enabled("interfaces/{$vip['interface']}")) {
3121
			return;
3122
		}
3123
	}
3124

    
3125
	$vip_password = $vip['password'];
3126
	$vip_password = escapeshellarg(addslashes(str_replace(" ", "",
3127
	    $vip_password)));
3128
	if ($vip['password'] != "") {
3129
		$password = " pass {$vip_password}";
3130
	}
3131

    
3132
	$advbase = "";
3133
	if (!empty($vip['advbase'])) {
3134
		$advbase = "advbase " . escapeshellarg($vip['advbase']);
3135
	}
3136

    
3137
	if (config_path_enabled('','virtualip_carp_maintenancemode')) {
3138
		$advskew = "advskew 254";
3139
	} else {
3140
		$advskew = "advskew " . escapeshellarg($vip['advskew']);
3141
	}
3142

    
3143
	mwexec("/sbin/ifconfig {$realif} vhid " . escapeshellarg($vip['vhid']) .
3144
	    " {$advskew} {$advbase} {$password}");
3145

    
3146
	if (!$maintenancemode_only) {
3147
		if (is_ipaddrv4($vip['subnet'])) {
3148
			mwexec("/sbin/ifconfig {$realif} " .
3149
			    escapeshellarg($vip['subnet']) . "/" .
3150
			    escapeshellarg($vip['subnet_bits']) .
3151
			    " alias vhid " . escapeshellarg($vip['vhid']));
3152
		} elseif (is_ipaddrv6($vip['subnet'])) {
3153
			mwexec("/sbin/ifconfig {$realif} inet6 " .
3154
			    escapeshellarg($vip['subnet']) . " prefixlen " .
3155
			    escapeshellarg($vip['subnet_bits']) .
3156
			    " alias vhid " . escapeshellarg($vip['vhid']));
3157
		}
3158
	}
3159

    
3160
	/* reconfigure stacked IP Aliases after CARP VIP changes
3161
	 * see https://redmine.pfsense.org/issues/12227
3162
	 * and https://redmine.pfsense.org/issues/12961 */
3163
	if ($ipalias_reload) {
3164
		foreach (config_get_path('virtualip/vip', []) as $viface) {
3165
			if (($viface['mode'] == 'ipalias') &&
3166
			    (get_root_interface($viface['interface']) == $vip['interface'])) {
3167
				interface_vip_bring_down($viface);
3168
				interface_ipalias_configure($viface);
3169
			}
3170
		}
3171
	}
3172

    
3173
	return $realif;
3174
}
3175

    
3176
function interface_wireless_clone($realif, $wlcfg) {
3177
	global $g;
3178
	/*   Check to see if interface has been cloned as of yet.
3179
	 *   If it has not been cloned then go ahead and clone it.
3180
	 */
3181
	$needs_clone = false;
3182
	if (is_array($wlcfg['wireless'])) {
3183
		$wlcfg_mode = $wlcfg['wireless']['mode'];
3184
	} else {
3185
		$wlcfg_mode = $wlcfg['mode'];
3186
	}
3187
	switch ($wlcfg_mode) {
3188
		case "hostap":
3189
			$mode = "wlanmode hostap";
3190
			break;
3191
		case "adhoc":
3192
			$mode = "wlanmode adhoc";
3193
			break;
3194
		default:
3195
			$mode = "";
3196
			break;
3197
	}
3198
	$baseif = interface_get_wireless_base($wlcfg['if']);
3199
	if (does_interface_exist($realif)) {
3200
		exec("/sbin/ifconfig " . escapeshellarg($realif), $output, $ret);
3201
		$ifconfig_str = implode($output);
3202
		if (($wlcfg_mode == "hostap") && (!preg_match("/hostap/si", $ifconfig_str))) {
3203
			log_error(sprintf(gettext("Interface %s changed to hostap mode"), $realif));
3204
			$needs_clone = true;
3205
		}
3206
		if (($wlcfg_mode == "adhoc") && (!preg_match("/adhoc/si", $ifconfig_str))) {
3207
			log_error(sprintf(gettext("Interface %s changed to adhoc mode"), $realif));
3208
			$needs_clone = true;
3209
		}
3210
		if (($wlcfg_mode == "bss") && (preg_match("/hostap|adhoc/si", $ifconfig_str))) {
3211
			log_error(sprintf(gettext("Interface %s changed to infrastructure mode"), $realif));
3212
			$needs_clone = true;
3213
		}
3214
	} else {
3215
		$needs_clone = true;
3216
	}
3217

    
3218
	if ($needs_clone == true) {
3219
		/* remove previous instance if it exists */
3220
		if (does_interface_exist($realif)) {
3221
			pfSense_interface_destroy($realif);
3222

    
3223
			/* Invalidate cache */
3224
			get_interface_arr(true);
3225
		}
3226

    
3227
		log_error(sprintf(gettext("Cloning new wireless interface %s"), $realif));
3228
		// Create the new wlan interface. FreeBSD returns the new interface name.
3229
		// example:  wlan2
3230
		exec("/sbin/ifconfig wlan create wlandev {$baseif} {$mode} bssid 2>&1", $out, $ret);
3231
		if ($ret <> 0) {
3232
			log_error(sprintf(gettext('Failed to clone interface %1$s with error code %2$s, output %3$s'), $baseif, $ret, $out[0]));
3233
			return false;
3234
		}
3235
		$newif = trim($out[0]);
3236
		// Rename the interface to {$parentnic}_wlan{$number}#: EX: ath0_wlan0
3237
		pfSense_interface_rename($newif, $realif);
3238
		file_put_contents("{$g['tmp_path']}/{$realif}_oldmac", get_interface_mac($realif));
3239
	}
3240
	return true;
3241
}
3242

    
3243
function interface_sync_wireless_clones(&$ifcfg, $sync_changes = false) {
3244

    
3245
	$shared_settings = array('standard', 'turbo', 'protmode', 'txpower', 'channel',
3246
				 'diversity', 'txantenna', 'rxantenna', 'distance',
3247
				 'regdomain', 'regcountry', 'reglocation');
3248

    
3249
	if (!is_interface_wireless($ifcfg['if'])) {
3250
		return;
3251
	}
3252

    
3253
	$baseif = interface_get_wireless_base($ifcfg['if']);
3254

    
3255
	// Sync shared settings for assigned clones
3256
	$iflist = get_configured_interface_list(true);
3257
	foreach ($iflist as $if) {
3258
		if (($baseif == interface_get_wireless_base(config_get_path("interfaces/{$if}/if"))) &&
3259
			($ifcfg['if'] != config_get_path("interfaces/{$if}/if"))) {
3260
			if (config_path_enabled("interfaces/{$if}/wireless", 'standard') || $sync_changes) {
3261
				foreach ($shared_settings as $setting) {
3262
					if ($sync_changes) {
3263
						if (isset($ifcfg['wireless'][$setting])) {
3264
							config_set_path("interfaces/{$if}/wireless/{$setting}",
3265
											$ifcfg['wireless'][$setting]);
3266
						} elseif (!empty(config_get_path("interfaces/{$if}/wireless/{$setting}"))) {
3267
							config_del_path("interfaces/{$if}/wireless/{$setting}");
3268
						}
3269
					} else {
3270
						if (!empty(config_get_path("interfaces/{$if}/wireless/{$setting}"))) {
3271
							$ifcfg['wireless'][$setting] =
3272
							    config_get_path("interfaces/{$if}/wireless/{$setting}");
3273
						} elseif (isset($ifcfg['wireless'][$setting])) {
3274
							unset($ifcfg['wireless'][$setting]);
3275
						}
3276
					}
3277
				}
3278
				if (!$sync_changes) {
3279
					break;
3280
				}
3281
			}
3282
		}
3283
	}
3284

    
3285
	// Read or write settings at shared area
3286
	if (!empty(config_get_path("wireless/interfaces/{$baseif}"))) {
3287
		foreach ($shared_settings as $setting) {
3288
			if ($sync_changes) {
3289
				if (isset($ifcfg['wireless'][$setting])) {
3290
					config_set_path("wireless/interfaces/{$baseif}/{$setting}",
3291
									$ifcfg['wireless'][$setting]);
3292
				} elseif (!empty(config_get_path("wireless/interfaces/{$baseif}/{$setting}"))) {
3293
					config_del_path("wireless/interfaces/{$baseif}/{$setting}");
3294
				}
3295
			} else {
3296
				if (!empty(config_get_path("wireless/interfaces/{$baseif}/{$setting}"))) {
3297
					$ifcfg['wireless'][$setting] = config_get_path("wireless/interfaces/{$baseif}/{$setting}");
3298
				} elseif (isset($ifcfg['wireless'][$setting])) {
3299
					unset($ifcfg['wireless'][$setting]);
3300
				}
3301
			}
3302
		}
3303
	}
3304

    
3305
	// Sync the mode on the clone creation page with the configured mode on the interface
3306
	if (interface_is_wireless_clone($ifcfg['if'])) {
3307
		foreach (config_get_path('wireless/clone', []) as $key => $clone) {
3308
			if ($clone['cloneif'] == $ifcfg['if']) {
3309
				if ($sync_changes) {
3310
					config_set_path("wireless/clone/{$key}/mode", $ifcfg['wireless']['mode']);
3311
				} else {
3312
					$ifcfg['wireless']['mode'] = $clone['mode'];
3313
				}
3314
				break;
3315
			}
3316
		}
3317
		unset($clone);
3318
	}
3319
}
3320

    
3321
function interface_wireless_configure($if, &$wl, &$wlcfg) {
3322
	global $g;
3323

    
3324
	/*    open up a shell script that will be used to output the commands.
3325
	 *    since wireless is changing a lot, these series of commands are fragile
3326
	 *    and will sometimes need to be verified by a operator by executing the command
3327
	 *    and returning the output of the command to the developers for inspection.  please
3328
	 *    do not change this routine from a shell script to individual exec commands.  -sullrich
3329
	 */
3330

    
3331
	// Remove script file
3332
	unlink_if_exists("{$g['tmp_path']}/{$if}_setup.sh");
3333

    
3334
	// Clone wireless nic if needed.
3335
	interface_wireless_clone($if, $wl);
3336

    
3337
	// Reject inadvertent changes to shared settings in case the interface hasn't been configured.
3338
	interface_sync_wireless_clones($wl, false);
3339

    
3340
	$fd_set = fopen("{$g['tmp_path']}/{$if}_setup.sh", "w");
3341
	fwrite($fd_set, "#!/bin/sh\n");
3342
	fwrite($fd_set, "# {$g['product_label']} wireless configuration script.\n\n");
3343

    
3344
	$wlan_setup_log = fopen("{$g['tmp_path']}/{$if}_setup.log", "w");
3345

    
3346
	/* set values for /path/program */
3347
	if (file_exists("/usr/local/sbin/hostapd")) {
3348
		$hostapd = "/usr/local/sbin/hostapd";
3349
	} else {
3350
		$hostapd = "/usr/sbin/hostapd";
3351
	}
3352
	if (file_exists("/usr/local/sbin/wpa_supplicant")) {
3353
		$wpa_supplicant = "/usr/local/sbin/wpa_supplicant";
3354
	} else {
3355
		$wpa_supplicant = "/usr/sbin/wpa_supplicant";
3356
	}
3357
	$ifconfig = "/sbin/ifconfig";
3358
	$sysctl = "/sbin/sysctl";
3359
	$sysctl_args = "-q";
3360

    
3361
	/* Set all wireless ifconfig variables (split up to get rid of needed checking) */
3362

    
3363
	$wlcmd = array();
3364
	$wl_sysctl = array();
3365
	/* Set a/b/g standard */
3366
	$standard = str_replace(" Turbo", "", $wlcfg['standard']);
3367
	/* skip mode entirely for "auto" */
3368
	if ($wlcfg['standard'] != "auto") {
3369
		$wlcmd[] = "mode " . escapeshellarg($standard);
3370
	}
3371

    
3372
	/* XXX: Disable ampdu for now on mwl when running in 11n mode
3373
	 * to prevent massive packet loss under certain conditions. */
3374
	if (preg_match("/^mwl/i", $if) && ($standard == "11ng" || $standard == "11na")) {
3375
		$wlcmd[] = "-ampdu";
3376
	}
3377

    
3378
	/* Set ssid */
3379
	if ($wlcfg['ssid']) {
3380
		$wlcmd[] = "ssid " .escapeshellarg($wlcfg['ssid']);
3381
	}
3382

    
3383
	/* Set 802.11g protection mode */
3384
	$wlcmd[] = "protmode " . escapeshellarg($wlcfg['protmode']);
3385

    
3386
	/* set wireless channel value */
3387
	if (isset($wlcfg['channel'])) {
3388
		if ($wlcfg['channel'] == "0") {
3389
			$wlcmd[] = "channel any";
3390
		} else {
3391
			if ($wlcfg['channel_width'] != "0") {
3392
				$channel_width = ":" . $wlcfg['channel_width'];
3393
			} else {
3394
				$channel_width = '';
3395
			}
3396
			$wlcmd[] = "channel " . escapeshellarg($wlcfg['channel']) . $channel_width;
3397
		}
3398
	}
3399

    
3400
	/* Set antenna diversity value */
3401
	if (isset($wlcfg['diversity'])) {
3402
		$wl_sysctl[] = "diversity=" . escapeshellarg($wlcfg['diversity']);
3403
	}
3404

    
3405
	/* Set txantenna value */
3406
	if (isset($wlcfg['txantenna'])) {
3407
		$wl_sysctl[] = "txantenna=" . escapeshellarg($wlcfg['txantenna']);
3408
	}
3409

    
3410
	/* Set rxantenna value */
3411
	if (isset($wlcfg['rxantenna'])) {
3412
		$wl_sysctl[] = "rxantenna=" . escapeshellarg($wlcfg['rxantenna']);
3413
	}
3414

    
3415
	/* set Distance value */
3416
	if ($wlcfg['distance']) {
3417
		$distance = escapeshellarg($wlcfg['distance']);
3418
	}
3419

    
3420
	/* Set wireless hostap mode */
3421
	if ($wlcfg['mode'] == "hostap") {
3422
		$wlcmd[] = "mediaopt hostap";
3423
	} else {
3424
		$wlcmd[] = "-mediaopt hostap";
3425
	}
3426

    
3427
	/* Set wireless adhoc mode */
3428
	if ($wlcfg['mode'] == "adhoc") {
3429
		$wlcmd[] = "mediaopt adhoc";
3430
	} else {
3431
		$wlcmd[] = "-mediaopt adhoc";
3432
	}
3433

    
3434
	/* Not necessary to set BSS mode as this is default if adhoc and/or hostap is NOT set */
3435

    
3436
	/* handle hide ssid option */
3437
	if (isset($wlcfg['hidessid']['enable'])) {
3438
		$wlcmd[] = "hidessid";
3439
	} else {
3440
		$wlcmd[] = "-hidessid";
3441
	}
3442

    
3443
	/* handle pureg (802.11g) only option */
3444
	if (isset($wlcfg['pureg']['enable'])) {
3445
		$wlcmd[] = "mode 11g pureg";
3446
	} else {
3447
		$wlcmd[] = "-pureg";
3448
	}
3449

    
3450
	/* handle puren (802.11n) only option */
3451
	if (isset($wlcfg['puren']['enable'])) {
3452
		$wlcmd[] = "puren";
3453
	} else {
3454
		$wlcmd[] = "-puren";
3455
	}
3456

    
3457
	/* enable apbridge option */
3458
	if (isset($wlcfg['apbridge']['enable'])) {
3459
		$wlcmd[] = "apbridge";
3460
	} else {
3461
		$wlcmd[] = "-apbridge";
3462
	}
3463

    
3464
	/* handle turbo option */
3465
	if (isset($wlcfg['turbo']['enable'])) {
3466
		$wlcmd[] = "mediaopt turbo";
3467
	} else {
3468
		$wlcmd[] = "-mediaopt turbo";
3469
	}
3470

    
3471
	/* handle txpower setting */
3472
	// or don't. this has issues at the moment.
3473
	/*
3474
	if ($wlcfg['txpower'] <> "" && is_numeric($wlcfg['txpower'])) {
3475
		$wlcmd[] = "txpower " . escapeshellarg($wlcfg['txpower']);
3476
	}*/
3477

    
3478
	/* handle wme option */
3479
	if (isset($wlcfg['wme']['enable'])) {
3480
		$wlcmd[] = "wme";
3481
	} else {
3482
		$wlcmd[] = "-wme";
3483
	}
3484

    
3485
	/* Enable wpa if it's configured. No WEP support anymore. */
3486
	if (isset($wlcfg['wpa']['enable'])) {
3487
		$wlcmd[] = "authmode wpa wepmode off ";
3488
	} else {
3489
		$wlcmd[] = "authmode open wepmode off ";
3490
	}
3491

    
3492
	kill_hostapd($if);
3493
	mwexec(kill_wpasupplicant("{$if}"));
3494

    
3495
	$wpa_supplicant_file = "{$g['varetc_path']}/wpa_supplicant_{$if}.";
3496
	$hostapd_conf = "{$g['varetc_path']}/hostapd_{$if}.conf";
3497

    
3498
	unlink_if_exists("{$wpa_supplicant_file}*");
3499
	unlink_if_exists($hostapd_conf);
3500

    
3501
	/* generate wpa_supplicant/hostap config if wpa is enabled */
3502
	$wpa = "";
3503
	switch ($wlcfg['mode']) {
3504
		case 'bss':
3505
			if (isset($wlcfg['wpa']['enable'])) {
3506
				$wpa .= <<<EOD
3507
ctrl_interface={$g['varrun_path']}/wpa_supplicant
3508
ctrl_interface_group=0
3509
ap_scan=1
3510
#fast_reauth=1
3511
network={
3512
ssid="{$wlcfg['ssid']}"
3513
scan_ssid=1
3514
priority=5
3515
key_mgmt={$wlcfg['wpa']['wpa_key_mgmt']}
3516
pairwise={$wlcfg['wpa']['wpa_pairwise']}
3517
group={$wlcfg['wpa']['wpa_pairwise']}
3518

    
3519
EOD;
3520
				if ($wlcfg['wpa']['wpa_key_mgmt'] == 'WPA-EAP') {
3521
					if (($wlcfg['wpa']['wpa_eap_client_mode'] == 'PEAP') ||
3522
					    ($wlcfg['wpa']['wpa_eap_client_mode'] == 'TTLS')) {
3523
						if ($wlcfg['wpa']['wpa_eap_inner_auth'] == 'MSCHAPV2') {
3524
							$wpa .= "phase1=\"peaplabel=0\"\n";
3525
						}
3526
						$wpa .= "phase2=\"auth={$wlcfg['wpa']['wpa_eap_inner_auth']}\"\n";
3527
						$wpa .= "identity=\"{$wlcfg['wpa']['wpa_eap_inner_id']}\"\n";
3528
						$eappass = base64_decode($wlcfg['wpa']['wpa_eap_inner_password']);
3529
						$wpa .= "password=\"{$eappass}\"\n";
3530
					}
3531
					if (strstr($wlcfg['wpa']['wpa_eap_client_mode'], 'TLS')) {
3532
						$cert = lookup_cert($wlcfg['wpa']['wpa_eap_cert']);
3533
						$cert = $cert['item'];
3534
						$wpa_supplicant_crt = $wpa_supplicant_file . "crt";
3535
						$wpa_supplicant_key = $wpa_supplicant_file . "key";
3536
						@file_put_contents($wpa_supplicant_crt, base64_decode($cert['crt']) . "\n" .
3537
						    ca_chain($cert));
3538
						@file_put_contents($wpa_supplicant_key, base64_decode($cert['prv']));
3539
						@chmod($wpa_supplicant_crt, 0600);
3540
						@chmod($wpa_supplicant_key, 0600);
3541
						$wpa .= "client_cert=\"{$wpa_supplicant_crt}\"\n";
3542
						$wpa .= "private_key=\"{$wpa_supplicant_key}\"\n";
3543
					}
3544
					$ca = lookup_ca($wlcfg['wpa']['wpa_eap_ca']);
3545
					$ca = $ca['item'];
3546
					$wpa_supplicant_ca = $wpa_supplicant_file . "ca";
3547
					@file_put_contents($wpa_supplicant_ca, base64_decode($ca['crt']) . "\n" .
3548
					    ca_chain($ca));
3549
					$wpa .= "ca_cert=\"{$wpa_supplicant_ca}\"\n";
3550
					$wpa .= "eap={$wlcfg['wpa']['wpa_eap_client_mode']}\n";
3551
				} else {
3552
					$wpa .= "psk=\"{$wlcfg['wpa']['passphrase']}\"\n";
3553
				}
3554
				$wpa .= "}\n";
3555

    
3556
				@file_put_contents($wpa_supplicant_file . "conf", $wpa);
3557
				unset($wpa);
3558
			}
3559
			break;
3560
		case 'hostap':
3561
			if (!empty($wlcfg['wpa']['passphrase'])) {
3562
				$wpa_passphrase = "wpa_passphrase={$wlcfg['wpa']['passphrase']}\n";
3563
			} else {
3564
				$wpa_passphrase = "";
3565
			}
3566
			if (isset($wlcfg['wpa']['enable'])) {
3567
				$wpa .= <<<EOD
3568
interface={$if}
3569
driver=bsd
3570
logger_syslog=-1
3571
logger_syslog_level=0
3572
logger_stdout=-1
3573
logger_stdout_level=0
3574
dump_file={$g['tmp_path']}/hostapd_{$if}.dump
3575
ctrl_interface={$g['varrun_path']}/hostapd
3576
ctrl_interface_group=wheel
3577
#accept_mac_file={$g['tmp_path']}/hostapd_{$if}.accept
3578
#deny_mac_file={$g['tmp_path']}/hostapd_{$if}.deny
3579
#macaddr_acl={$wlcfg['wpa']['macaddr_acl']}
3580
ssid={$wlcfg['ssid']}
3581
debug={$wlcfg['wpa']['debug_mode']}
3582
wpa={$wlcfg['wpa']['wpa_mode']}
3583
wpa_key_mgmt={$wlcfg['wpa']['wpa_key_mgmt']}
3584
wpa_pairwise={$wlcfg['wpa']['wpa_pairwise']}
3585
wpa_group_rekey={$wlcfg['wpa']['wpa_group_rekey']}
3586
wpa_gmk_rekey={$wlcfg['wpa']['wpa_gmk_rekey']}
3587
wpa_strict_rekey={$wlcfg['wpa']['wpa_strict_rekey']}
3588
{$wpa_passphrase}
3589

    
3590
EOD;
3591

    
3592
				if (isset($wlcfg['wpa']['rsn_preauth'])) {
3593
					$wpa .= <<<EOD
3594
# Enable the next lines for preauth when roaming. Interface = wired or wireless interface talking to the AP to roam from/to
3595
rsn_preauth=1
3596
rsn_preauth_interfaces={$if}
3597

    
3598
EOD;
3599
				}
3600
				if (is_array($wlcfg['wpa']['ieee8021x']) && isset($wlcfg['wpa']['ieee8021x']['enable'])) {
3601
					$wpa .= "ieee8021x=1\n";
3602

    
3603
					if (!empty($wlcfg['auth_server_addr']) && !empty($wlcfg['auth_server_shared_secret'])) {
3604
						$auth_server_port = "1812";
3605
						if (!empty($wlcfg['auth_server_port']) && is_numeric($wlcfg['auth_server_port'])) {
3606
							$auth_server_port = intval($wlcfg['auth_server_port']);
3607
						}
3608
						$wpa .= <<<EOD
3609

    
3610
auth_server_addr={$wlcfg['auth_server_addr']}
3611
auth_server_port={$auth_server_port}
3612
auth_server_shared_secret={$wlcfg['auth_server_shared_secret']}
3613

    
3614
EOD;
3615
						if (!empty($wlcfg['auth_server_addr2']) && !empty($wlcfg['auth_server_shared_secret2'])) {
3616
							$auth_server_port2 = "1812";
3617
							if (!empty($wlcfg['auth_server_port2']) && is_numeric($wlcfg['auth_server_port2'])) {
3618
								$auth_server_port2 = intval($wlcfg['auth_server_port2']);
3619
							}
3620

    
3621
							$wpa .= <<<EOD
3622
auth_server_addr={$wlcfg['auth_server_addr2']}
3623
auth_server_port={$auth_server_port2}
3624
auth_server_shared_secret={$wlcfg['auth_server_shared_secret2']}
3625

    
3626
EOD;
3627
						}
3628
					}
3629
				}
3630

    
3631
				@file_put_contents($hostapd_conf, $wpa);
3632
				unset($wpa);
3633
			}
3634
			break;
3635
	}
3636

    
3637
	/*
3638
	 *    all variables are set, lets start up everything
3639
	 */
3640

    
3641
	$baseif = interface_get_wireless_base($if);
3642
	preg_match("/^(.*?)([0-9]*)$/", $baseif, $baseif_split);
3643
	$wl_sysctl_prefix = 'dev.' . $baseif_split[1] . '.' . $baseif_split[2];
3644

    
3645
	/* set sysctls for the wireless interface */
3646
	if (!empty($wl_sysctl)) {
3647
		fwrite($fd_set, "# sysctls for {$baseif}\n");
3648
		foreach ($wl_sysctl as $wl_sysctl_line) {
3649
			fwrite($fd_set, "{$sysctl} {$sysctl_args} {$wl_sysctl_prefix}.{$wl_sysctl_line}\n");
3650
		}
3651
	}
3652

    
3653
	/* set ack timers according to users preference (if he/she has any) */
3654
	if ($distance) {
3655
		fwrite($fd_set, "# Enable ATH distance settings\n");
3656
		fwrite($fd_set, "/usr/local/sbin/athctrl.sh -i {$baseif} -d {$distance}\n");
3657
	}
3658

    
3659
	if (isset($wlcfg['wpa']['enable'])) {
3660
		if ($wlcfg['mode'] == "bss") {
3661
			fwrite($fd_set, "{$wpa_supplicant} -B -i {$if} -c {$g['varetc_path']}/wpa_supplicant_{$if}.conf\n");
3662
		}
3663
		if ($wlcfg['mode'] == "hostap") {
3664
			/* add line to script to restore old mac to make hostapd happy */
3665
			if (file_exists("{$g['tmp_path']}/{$if}_oldmac")) {
3666
				$if_oldmac = file_get_contents("{$g['tmp_path']}/{$if}_oldmac");
3667
				$if_curmac = get_interface_mac($if);
3668
				if ($if_curmac != $if_oldmac && is_macaddr($if_oldmac)) {
3669
					fwrite($fd_set, "{$ifconfig} " . escapeshellarg($if) .
3670
						" link " . escapeshellarg($if_oldmac) . "\n");
3671
				}
3672
			}
3673

    
3674
			fwrite($fd_set, "{$hostapd} -B -P {$g['varrun_path']}/hostapd_{$if}.pid {$g['varetc_path']}/hostapd_{$if}.conf\n");
3675

    
3676
			/* add line to script to restore spoofed mac after running hostapd */
3677
			if ($wl['spoofmac']) {
3678
				$if_curmac = get_interface_mac($if);
3679
				if ($wl['spoofmac'] != $if_curmac && is_macaddr($wl['spoofmac'])) {
3680
					fwrite($fd_set, "{$ifconfig} " . escapeshellarg($if) .
3681
						" link " . escapeshellarg($wl['spoofmac']) . "\n");
3682
				}
3683
			}
3684
		}
3685
	}
3686

    
3687
	fclose($fd_set);
3688

    
3689
	/* Making sure regulatory settings have actually changed
3690
	 * before applying, because changing them requires bringing
3691
	 * down all wireless networks on the interface. */
3692
	exec("{$ifconfig} " . escapeshellarg($if), $output);
3693
	$ifconfig_str = implode($output);
3694
	unset($output);
3695
	$reg_changing = false;
3696

    
3697
	/* special case for the debug country code */
3698
	if ($wlcfg['regcountry'] == 'DEBUG' && !preg_match("/\sregdomain\s+DEBUG\s/si", $ifconfig_str)) {
3699
		$reg_changing = true;
3700
	} elseif ($wlcfg['regdomain'] && !preg_match("/\sregdomain\s+{$wlcfg['regdomain']}\s/si", $ifconfig_str)) {
3701
		$reg_changing = true;
3702
	} elseif ($wlcfg['regcountry'] && !preg_match("/\scountry\s+{$wlcfg['regcountry']}\s/si", $ifconfig_str)) {
3703
		$reg_changing = true;
3704
	} elseif ($wlcfg['reglocation'] == 'anywhere' && preg_match("/\s(indoor|outdoor)\s/si", $ifconfig_str)) {
3705
		$reg_changing = true;
3706
	} elseif ($wlcfg['reglocation'] && $wlcfg['reglocation'] != 'anywhere' && !preg_match("/\s{$wlcfg['reglocation']}\s/si", $ifconfig_str)) {
3707
		$reg_changing = true;
3708
	}
3709

    
3710
	if ($reg_changing) {
3711
		/* set regulatory domain */
3712
		if ($wlcfg['regdomain']) {
3713
			$wlregcmd[] = "regdomain " . escapeshellarg($wlcfg['regdomain']);
3714
		}
3715

    
3716
		/* set country */
3717
		if ($wlcfg['regcountry']) {
3718
			$wlregcmd[] = "country " . escapeshellarg($wlcfg['regcountry']);
3719
		}
3720

    
3721
		/* set location */
3722
		if ($wlcfg['reglocation']) {
3723
			$wlregcmd[] = escapeshellarg($wlcfg['reglocation']);
3724
		}
3725

    
3726
		$wlregcmd_args = implode(" ", $wlregcmd);
3727

    
3728
		/* build a complete list of the wireless clones for this interface */
3729
		$clone_list = array();
3730
		if (does_interface_exist(interface_get_wireless_clone($baseif))) {
3731
			$clone_list[] = interface_get_wireless_clone($baseif);
3732
		}
3733
		foreach (config_get_path('wireless/clone', []) as $clone) {
3734
			if ($clone['if'] == $baseif) {
3735
				$clone_list[] = $clone['cloneif'];
3736
			}
3737
		}
3738

    
3739
		/* find which clones are up and bring them down */
3740
		$clones_up = array();
3741
		foreach ($clone_list as $clone_if) {
3742
			$clone_status = get_interface_addresses($clone_if);
3743
			if ($clone_status['status'] == 'up') {
3744
				$clones_up[] = $clone_if;
3745
				mwexec("{$ifconfig} " . escapeshellarg($clone_if) . " down");
3746
			}
3747
		}
3748

    
3749
		/* apply the regulatory settings */
3750
		mwexec("{$ifconfig} " . escapeshellarg($if) . " {$wlregcmd_args}");
3751
		fwrite($wlan_setup_log, "$ifconfig" . escapeshellarg($if) . "$wlregcmd_args \n");
3752

    
3753
		/* bring the clones back up that were previously up */
3754
		foreach ($clones_up as $clone_if) {
3755
			interfaces_bring_up($clone_if);
3756

    
3757
			/*
3758
			 * Rerun the setup script for the interface if it isn't this interface, the interface
3759
			 * is in infrastructure mode, and WPA is enabled.
3760
			 * This can be removed if wpa_supplicant stops dying when you bring the interface down.
3761
			 */
3762
			if ($clone_if != $if) {
3763
				$friendly_if = convert_real_interface_to_friendly_interface_name($clone_if);
3764
				if ((!empty($friendly_if)) &&
3765
				    (config_get_path("interfaces/{$friendly_if}/wireless/mode") == "bss") &&
3766
				    config_path_enabled("interfaces/{$friendly_if}/wireless/wpa")) {
3767
					mwexec("/bin/sh {$g['tmp_path']}/" . escapeshellarg($clone_if) . "_setup.sh");
3768
				}
3769
			}
3770
		}
3771
	}
3772

    
3773
	/* The mode must be specified in a separate command before ifconfig
3774
	 * will allow the mode and channel at the same time in the next.
3775
	 * Only do this for AP mode as this breaks client mode (PR 198680).
3776
	 */
3777
	if ($wlcfg['mode'] == "hostap") {
3778
		mwexec("/sbin/ifconfig " . escapeshellarg($if) . " mode " . escapeshellarg($standard));
3779
		fwrite($wlan_setup_log, "/sbin/ifconfig " . escapeshellarg($if) . " mode " . escapeshellarg($standard) . "\n");
3780
	}
3781

    
3782
	/* configure wireless */
3783
	$wlcmd_args = implode(" ", $wlcmd);
3784
	mwexec("/sbin/ifconfig " . escapeshellarg($if) . " " . $wlcmd_args);
3785
	fwrite($wlan_setup_log, "/sbin/ifconfig " . escapeshellarg($if) . " " . "$wlcmd_args \n");
3786
	/* Bring the interface up only after setting up all the other parameters. */
3787
	mwexec("/sbin/ifconfig " . escapeshellarg($if) . " up");
3788
	fwrite($wlan_setup_log, "/sbin/ifconfig " . escapeshellarg($if) . " up\n");
3789
	fclose($wlan_setup_log);
3790

    
3791
	unset($wlcmd_args, $wlcmd);
3792

    
3793

    
3794
	sleep(1);
3795
	/* execute hostapd and wpa_supplicant if required in shell */
3796
	mwexec("/bin/sh {$g['tmp_path']}/" . escapeshellarg($if) . "_setup.sh");
3797

    
3798
	return 0;
3799

    
3800
}
3801

    
3802
function kill_hostapd($interface) {
3803
	global $g;
3804

    
3805
	if (isvalidpid("{$g['varrun_path']}/hostapd_{$interface}.pid")) {
3806
		return killbypid("{$g['varrun_path']}/hostapd_{$interface}.pid");
3807
	}
3808
}
3809

    
3810
function kill_wpasupplicant($interface) {
3811
	return "/bin/pkill -f \"wpa_supplicant .*{$interface}\\.conf\"\n";
3812
}
3813

    
3814
function find_dhclient_process($interface) {
3815
	if ($interface) {
3816
		$pid = `/bin/pgrep -axf "dhclient: {$interface}"`;
3817
	} else {
3818
		$pid = 0;
3819
	}
3820

    
3821
	return intval($pid);
3822
}
3823

    
3824
function kill_dhclient_process($interface) {
3825
	if (empty($interface) || !does_interface_exist($interface)) {
3826
		return;
3827
	}
3828

    
3829
	$i = 0;
3830
	while ((($pid = find_dhclient_process($interface)) != 0) && ($i < 3)) {
3831
		/* 3rd time make it die for sure */
3832
		$sig = ($i == 2 ? SIGKILL : SIGTERM);
3833
		posix_kill($pid, $sig);
3834
		sleep(1);
3835
		$i++;
3836
	}
3837
	unset($i);
3838

    
3839
	unlink_if_exists(g_get('vardb_path') . "/{$interface}_cacheip");
3840
}
3841

    
3842
function find_dhcp6c_process() {
3843
	global $g;
3844

    
3845
	if (isvalidpid("{$g['varrun_path']}/dhcp6c.pid")) {
3846
		$pid = trim(file_get_contents("{$g['varrun_path']}/dhcp6c.pid"), " \n");
3847
	} else {
3848
		return(false);
3849
	}
3850

    
3851
	return intval($pid);
3852
}
3853

    
3854
function kill_dhcp6client_process($force, $release = false) {
3855
	global $g;
3856

    
3857
	$i = 0;
3858

    
3859
	/*
3860
	Beware of the following: Reason, the interface may be down, but
3861
	dhcp6c may still be running, it just complains it cannot send
3862
	and carries on. Commented out as will stop the call to kill.
3863

    
3864
	if (empty($interface) || !does_interface_exist($interface)) {
3865
		return;
3866
	}
3867
	*/
3868

    
3869
	/*********** Notes on signals for dhcp6c and this function *************
3870

    
3871
	If we have Taken the WAN interface down, then dhcp6c sits there sending
3872
	a release and waiting for the response that never comes.
3873
	So we need to tell it that the interface is down and to just die quickly
3874
	otherwise a new client may launch and we have duplicate processes.
3875
	In this case use SIGUSR1.
3876

    
3877
	If we want to exit normally obeying the no release flag then use SIGTERM.
3878
	If we want to exit with a release overriding the no release flag then
3879
	use SIGUSR2.
3880

    
3881
	If $Force is true it will use SIGUSR1, thus forcing dhcp6c to
3882
	exit quickly without sending release signals.
3883

    
3884
	If $Force is set to false and $release is also set to false dhcp6c will
3885
	follow the no-release flag.
3886

    
3887
	If $Force is set to false and $release is true then dhcp6c will send a
3888
	release regardless of the no-release flag.
3889
	***********************************************************************/
3890

    
3891
	if ($force == true) {
3892
		$psig=SIGUSR1;
3893
	} elseif ($release == false) {
3894
		$psig=SIGTERM;
3895
	} else {
3896
		$psig=SIGUSR2;
3897
	}
3898

    
3899
	while ((($pid = find_dhcp6c_process()) != 0) && ($i < 3)) {
3900
		/* 3rd time make it die for sure */
3901
		$sig = ($i == 2 ? SIGKILL : $psig);
3902
		posix_kill($pid, $sig);
3903
		sleep(1);
3904
		$i++;
3905
	}
3906
	/* Clear the RTSOLD script created lock & tidy up */
3907
	unlink_if_exists("/tmp/dhcp6c_lock");
3908
	unlink_if_exists("{$g['varrun_path']}/dhcp6c.pid"); // just in case!
3909
}
3910
function reset_dhcp6client_process() {
3911

    
3912
	$pid = find_dhcp6c_process();
3913

    
3914
	if($pid != 0) {
3915
		posix_kill($pid, SIGHUP);
3916
	}
3917
}
3918

    
3919
function run_dhcp6client_process($interfaces, $debugOption, $noreleaseOption) {
3920
	global $g;
3921

    
3922
	/*
3923
	 * Only run this if the lock does not exist. In theory the lock being
3924
	 * there in this mode means the user has selected dhcp6withoutRA while
3925
	 * a session is active in the other mode.
3926
	 *
3927
	 * It should not happen as the process should have been killed and the
3928
	 * lock deleted.
3929
	 */
3930

    
3931
	if (!file_exists("/tmp/dhcp6c_lock")) {
3932
		kill_dhcp6client_process(true);
3933
		/* Lock it to avoid multiple runs */
3934
		touch("/tmp/dhcp6c_lock");
3935
		mwexec("/usr/local/sbin/dhcp6c {$debugOption} " .
3936
		    "{$noreleaseOption} " .
3937
		    "-c {$g['varetc_path']}/dhcp6c.conf " .
3938
		    "-p {$g['varrun_path']}/dhcp6c.pid " .
3939
		    implode(' ', $interfaces));
3940
		log_error(sprintf(gettext(
3941
		    "Starting DHCP6 client for interfaces %s in DHCP6 without RA mode"),
3942
		    implode(',', $interfaces)));
3943
	}
3944
}
3945

    
3946
function interface_virtual_create($interface) {
3947
	$vlan = interface_is_vlan($interface);
3948
	if ($vlan != NULL) {
3949
		interface_vlan_configure($vlan);
3950
	} elseif (substr($interface, 0, 3) == "gre") {
3951
		interfaces_tunnel_configure(0, $interface, 'gre');
3952
	} elseif (substr($interface, 0, 3) == "gif") {
3953
		interfaces_tunnel_configure(0, $interface, 'gif');
3954
	} elseif (substr($interface, 0, 5) == "ovpns") {
3955
		foreach (config_get_path('openvpn/openvpn-server', []) as $server) {
3956
			if ($interface == "ovpns{$server['vpnid']}") {
3957
				if (!function_exists('openvpn_resync')) {
3958
					require_once('openvpn.inc');
3959
				}
3960
				log_error(sprintf(gettext("OpenVPN: Resync server %s"), $server['description']));
3961
				openvpn_resync('server', $server);
3962
			}
3963
		}
3964
		unset($server);
3965
	} elseif (substr($interface, 0, 5) == "ovpnc") {
3966
		foreach (config_get_path('openvpn/openvpn-client', []) as $client) {
3967
			if ($interface == "ovpnc{$client['vpnid']}") {
3968
				if (!function_exists('openvpn_resync')) {
3969
					require_once('openvpn.inc');
3970
				}
3971
				log_error(sprintf(gettext("OpenVPN: Resync client %s"), $client['description']));
3972
				openvpn_resync('client', $client);
3973
			}
3974
		}
3975
		unset($client);
3976
	} elseif (substr($interface, 0, 5) == "ipsec") {
3977
		foreach (config_get_path('ipsec/phase1', []) as $ph1ent) {
3978
			if ($ph1ent['disabled']) {
3979
				continue;
3980
			}
3981
			interface_ipsec_vti_configure($ph1ent);
3982
		}
3983
	} elseif (substr($interface, 0, 4) == "lagg") {
3984
		interfaces_lagg_configure($interface);
3985
	} elseif (substr($interface, 0, 6) == "bridge") {
3986
		interfaces_bridge_configure(0, $interface);
3987
	}
3988
}
3989

    
3990
function interface_vlan_mtu_configured($iface) {
3991

    
3992
	$mtu = 0;
3993
	foreach (config_get_path('vlans/vlan', []) as $vlan) {
3994

    
3995
		if ($vlan['vlanif'] != $iface)
3996
			continue;
3997

    
3998
		$assignedport = convert_real_interface_to_friendly_interface_name($vlan['vlanif']);
3999
		$parentinf = convert_real_interface_to_friendly_interface_name($vlan['if']);
4000
		if (!empty($assignedport) && !empty(config_get_path("interfaces/{$assignedport}/mtu"))) {
4001
			/* VLAN MTU */
4002
			$mtu = config_get_path("interfaces/{$assignedport}/mtu");
4003
		} elseif (!empty($parentinf) && !empty(config_get_path("interfaces/{$parentinf}/mtu"))) {
4004
			/* Parent MTU */
4005
			$mtu = config_get_path("interfaces/{$parentinf}/mtu");
4006
		}
4007
	}
4008

    
4009
	return $mtu;
4010
}
4011

    
4012
function interface_mtu_wanted_for_pppoe($realif) {
4013

    
4014
	$mtu = 0;
4015
	foreach (config_get_path('ppps/ppp', []) as $ppp) {
4016
		if ($ppp['type'] != "pppoe") {
4017
			continue;
4018
		}
4019

    
4020
		$mtus = array();
4021
		if (!empty($ppp['mtu'])) {
4022
			$mtus = explode(',', $ppp['mtu']);
4023
		}
4024
		$ports = array_filter(explode(',', $ppp['ports']));
4025

    
4026
		foreach ($ports as $pid => $port) {
4027
			$parentifa = get_parent_interface($port);
4028
			$parentif = $parentifa[0];
4029
			if ($parentif != $realif)
4030
				continue;
4031

    
4032
			// there is an MTU configured on the port in question
4033
			if (!empty($mtus[$pid])) {
4034
				$mtu = intval($mtus[$pid]) + 8;
4035
			// or use the MTU configured on the interface ...
4036
			} else {
4037
				foreach (config_get_path('interfaces', []) as $interface) {
4038
					if ($interface['if'] == $ppp['if'] &&
4039
					    !empty($interface['mtu'])) {
4040
						$mtu = intval($interface['mtu']) + 8;
4041
						break;
4042
					}
4043
				}
4044
			}
4045
		}
4046
	}
4047

    
4048
	return $mtu;
4049
}
4050

    
4051
/**
4052
 * Applies the configured settings for an interface.
4053
 * 
4054
 * @param bool $skip_parent_mtu Skip setting the parent interface's MTU
4055
 */
4056
function interface_configure($interface = "wan", $reloadall = false, $linkupevent = false, $skip_parent_mtu = false) {
4057
	global $g;
4058
	global $interface_sn_arr_cache, $interface_ip_arr_cache;
4059
	global $interface_snv6_arr_cache, $interface_ipv6_arr_cache;
4060

    
4061
	$wancfg = config_get_path("interfaces/{$interface}");
4062

    
4063
	if (!isset($wancfg['enable'])) {
4064
		return;
4065
	}
4066

    
4067
	$realif = get_real_interface($interface);
4068
	$realhwif_array = get_parent_interface($interface);
4069
	// Need code to handle MLPPP if we ever use $realhwif for MLPPP handling
4070
	$realhwif = $realhwif_array[0];
4071

    
4072
	$mac_if_cfg = $wancfg;
4073
	if (interface_is_vlan($realif)) {
4074
		$mac_if = convert_real_interface_to_friendly_interface_name(
4075
		    $realhwif);
4076
		if (!empty($mac_if) && is_array(config_get_path("interfaces/{$mac_if}"))) {
4077
			$mac_if_cfg = config_get_path("interfaces/{$mac_if}");
4078
		} else {
4079
			$mac_if = $interface;
4080
		}
4081
	}
4082

    
4083
	if (!is_platform_booting() && (substr($realif, 0, 4) != "ovpn") && (substr($realif, 0, 5) != "ipsec")) {
4084
		/* remove all IPv4 and IPv6 addresses */
4085
		$tmpifaces = pfSense_getall_interface_addresses($realif);
4086
		if (is_array($tmpifaces)) {
4087
			foreach ($tmpifaces as $tmpiface) {
4088
				if (is_ipaddrv6($tmpiface) || is_subnetv6($tmpiface)) {
4089
					if (!is_linklocal($tmpiface)) {
4090
						mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 {$tmpiface} delete");
4091
					}
4092
				} elseif (strstr($tmpiface, "fe80::1:1")) {
4093
					mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 fe80::1:1 -alias");
4094
				} else {
4095
					if (is_subnetv4($tmpiface)) {
4096
						$tmpip = explode('/', $tmpiface);
4097
						$tmpip = $tmpip[0];
4098
					} else {
4099
						$tmpip = $tmpiface;
4100
					}
4101
					pfSense_interface_deladdress($realif, $tmpip);
4102
				}
4103
			}
4104
		}
4105

    
4106
		/* only bring down the interface when both v4 and v6 are set to NONE */
4107
		if (empty($wancfg['ipaddr']) && empty($wancfg['ipaddrv6'])) {
4108
			interface_bring_down($interface);
4109
		}
4110
	}
4111

    
4112
	$interface_to_check = $realif;
4113
	if (interface_isppp_type($interface)) {
4114
		$interface_to_check = $realhwif;
4115
	}
4116

    
4117
	/* Need to check that the interface exists or not in the case where its coming back from disabled state see #3270 */
4118
	if (!is_platform_booting() && (in_array(substr($realif, 0, 3), array("gre", "gif")) ||
4119
	    !does_interface_exist($interface_to_check))) {
4120
		interface_virtual_create($interface_to_check);
4121
	}
4122

    
4123
	/* Disable Accepting router advertisements unless specifically requested */
4124
	if (g_get('debug')) {
4125
		log_error(sprintf(gettext("Deny router advertisements for interface %s"), $interface));
4126
	}
4127
	if (isset($wancfg['dhcp6usev4iface']) || $wancfg['ipaddr']==='ppp')
4128
	{
4129
		mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 -accept_rtadv", true);
4130
	}
4131
	/* wireless configuration? */
4132
	if (is_array($wancfg['wireless']) && !$linkupevent) {
4133
		interface_wireless_configure($realif, $wancfg, $wancfg['wireless']);
4134
	}
4135

    
4136
	$current_mac = get_interface_mac($realhwif);
4137
	$vendor_mac = get_interface_vendor_mac($realhwif);
4138

    
4139
	if ($current_mac != "ff:ff:ff:ff:ff:ff") {
4140
		$mac_addr = $mac_if_cfg['spoofmac'] ?: $vendor_mac;
4141

    
4142
		interface_set_macaddr($realhwif, $mac_addr);
4143

    
4144
		/* Regenerate link-local address on MAC change.
4145
		 *
4146
		 * Some network devices respond to a DHCPv6 Solicit message only when
4147
		 * the IPv6 source address is consistent with what they expect.
4148
		 *
4149
		 * See https://redmine.pfsense.org/issues/12794 */
4150
		if ($mac_addr != $current_mac) {
4151
			mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 ifdisabled");
4152
			mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 " . get_interface_linklocal($interface) . " delete");
4153
			mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 -ifdisabled");
4154
		}
4155
	} else {
4156
		/*
4157
		 * this is not a valid mac address.  generate a
4158
		 * temporary mac address so the machine can get online.
4159
		 */
4160
		echo gettext("Generating new MAC address.");
4161
		$random_mac = generate_random_mac_address();
4162
		interface_set_macaddr($realhwif, $random_mac);
4163
		config_set_path("interfaces/{$mac_if}/spoofmac", $random_mac);
4164
		write_config(sprintf(gettext('The invalid MAC address ' .
4165
		    '(ff:ff:ff:ff:ff:ff) on interface %1$s has been ' .
4166
		    'automatically replaced with %2$s'), $mac_if, $random_mac));
4167
		file_notice("MAC Address altered", sprintf(gettext('The ' .
4168
		    'invalid MAC address (ff:ff:ff:ff:ff:ff) on interface ' .
4169
		    '%1$s has been automatically replaced with %2$s'), $mac_if,
4170
		    $random_mac), "Interfaces");
4171
	}
4172

    
4173
	/* media */
4174
	if ($wancfg['media'] || $wancfg['mediaopt']) {
4175
		$cmd = "/sbin/ifconfig " . escapeshellarg($realhwif);
4176
		if ($wancfg['media']) {
4177
			$cmd .= " media " . escapeshellarg($wancfg['media']);
4178
		}
4179
		if ($wancfg['mediaopt']) {
4180
			$cmd .= " mediaopt " . escapeshellarg($wancfg['mediaopt']);
4181
		}
4182
		mwexec($cmd);
4183
	}
4184

    
4185
	/* Apply hw offloading policies as configured */
4186
	enable_hardware_offloading($interface);
4187

    
4188
	/* invalidate interface/ip/sn cache */
4189
	get_interface_arr(true);
4190
	unset($interface_ip_arr_cache[$realif]);
4191
	unset($interface_sn_arr_cache[$realif]);
4192
	unset($interface_ipv6_arr_cache[$realif]);
4193
	unset($interface_snv6_arr_cache[$realif]);
4194

    
4195
	$tunnelif = substr($realif, 0, 3);
4196

    
4197
	$mtuif = $realif;
4198
	$mtuhwif = $realhwif;
4199

    
4200
	/* adjust MTU of parent interface of PPPoE interface if this does not violate explicit configuration */
4201
	if (interface_isppp_type($interface)) {
4202
		$mtuif = $realhwif;
4203
		$mtuhwif_array = get_parent_interface($mtuif);
4204
		$mtuhwif = $mtuhwif_array[0];
4205
	}
4206

    
4207
	$wantedmtu = 0;
4208
	foreach (config_get_path('interfaces', []) as $tmpinterface) {
4209
		if ($tmpinterface['if'] == $mtuif && !empty($tmpinterface['mtu'])) {
4210
			$wantedmtu = $tmpinterface['mtu'];
4211
			break;
4212
		}
4213
	}
4214

    
4215
	/* MTU is not specified for interface, try the pppoe settings. */
4216
	if ($wantedmtu == 0) {
4217
		$wantedmtu = interface_mtu_wanted_for_pppoe($mtuif);
4218
	}
4219
	if (($wantedmtu == 0) && (interface_is_vlan($mtuif) != NULL) && interface_isppp_type($interface)) {
4220
		$wantedmtu = interface_mtu_wanted_for_pppoe($mtuhwif);
4221
	}
4222
	if (($wantedmtu == 0) && interface_is_type($mtuif, 'gre')) {
4223
		/* set MTU to 1400 for GRE over IPsec */
4224
		if (is_greipsec($mtuif)) {
4225
			$wantedmtu = 1400;
4226
		} else {
4227
			$wantedmtu = 1476;
4228
		}
4229
	}
4230
	if (($wantedmtu == 0) && interface_is_type($mtuif, 'gif')) {
4231
		$wantedmtu = 1280;
4232
	}
4233

    
4234
	/* Set the MTU to 1500 if no explicit MTU configured. */
4235
	if ($wantedmtu == 0) {
4236
		$wantedmtu = 1500; /* Default */
4237
	}
4238

    
4239
	if (interface_is_vlan($mtuif) != NULL) {
4240
		$assignedparent = convert_real_interface_to_friendly_interface_name($mtuhwif);
4241
		if (!empty($assignedparent) && !empty(config_get_path("interfaces/{$assignedparent}/mtu"))) {
4242
			$parentmtu = config_get_path("interfaces/{$assignedparent}/mtu");
4243
			if ($wancfg['mtu'] > $parentmtu) {
4244
				log_error(sprintf(gettext('There is a conflict on MTU between parent %1$s and VLAN(%2$s)'), $mtuhwif, $mtuif));
4245
			}
4246
		}
4247

    
4248
		$configuredmtu = interface_vlan_mtu_configured($mtuif);
4249

    
4250
		if ($configuredmtu != 0 && $configuredmtu > $parentmtu)
4251
			$configuredmtu = $parentmtu;
4252
		if ($configuredmtu != 0)
4253
			$mtu = $configuredmtu;
4254
		else
4255
			$mtu = $wantedmtu;
4256

    
4257
		/* Set the parent MTU. Avoid looping when a parent lagg interface
4258
		   is being configured; see #14083 */
4259
		if ((get_interface_mtu($mtuhwif) < $mtu) && !$skip_parent_mtu)
4260
			set_interface_mtu($mtuhwif, $mtu);
4261
		/* Set the VLAN MTU. */
4262
		if (get_interface_mtu($mtuif) != $mtu)
4263
			set_interface_mtu($mtuif, $mtu);
4264
	} elseif (substr($mtuif, 0, 4) == 'lagg') {
4265
		/* LAGG interface must be destroyed and re-created to change MTU */
4266
		if ($wantedmtu != get_interface_mtu($mtuif)) {
4267
			foreach (config_get_path('laggs/lagg', []) as $lagg) {
4268
				if ($lagg['laggif'] == $mtuif) {
4269
					interface_lagg_configure($lagg);
4270
					break;
4271
				}
4272
			}
4273
		}
4274
	} else {
4275
		if ($wantedmtu != get_interface_mtu($mtuif)) {
4276
			pfSense_interface_mtu($mtuif, $wantedmtu);
4277
			set_ipv6routes_mtu($mtuif, $wantedmtu);
4278
		}
4279
	}
4280
	/* XXX: What about gre/gif/.. ? */
4281

    
4282
	if (does_interface_exist($wancfg['if'])) {
4283
		interfaces_bring_up($wancfg['if']);
4284
	}
4285

    
4286
	switch ($wancfg['ipaddr']) {
4287
		case 'dhcp':
4288
			interface_dhcp_configure($interface);
4289
			break;
4290
		case 'pppoe':
4291
		case 'l2tp':
4292
		case 'pptp':
4293
		case 'ppp':
4294
			interface_ppps_configure($interface);
4295
			break;
4296
		default:
4297
			/* XXX: Kludge for now related to #3280 */
4298
			if (!in_array($tunnelif, array("gif", "gre", "ovp", "ips", "l2t"))) {
4299
				if (is_ipaddrv4($wancfg['ipaddr']) && $wancfg['subnet'] <> "") {
4300
					pfSense_interface_setaddress($realif, "{$wancfg['ipaddr']}/{$wancfg['subnet']}");
4301
				}
4302
			}
4303
			break;
4304
	}
4305

    
4306
	switch ($wancfg['ipaddrv6']) {
4307
		case 'slaac':
4308
		case 'dhcp6':
4309
			// N.B. PPP connections using PPP as the IPv6 parent interface are excluded because the ppp-ipv6 script
4310
			// calls interface_dhcpv6_configure() for these connections after IPv6CP is up, whilst rc.newwanip
4311
			// handles all non-PPP connections with 'dhcp6usev4iface' set
4312
			log_error(gettext("calling interface_dhcpv6_configure."));
4313
			if ((($wancfg['ipaddrv6'] == 'dhcp6') && !isset($wancfg['dhcp6usev4iface'])) ||
4314
			    (($wancfg['ipaddrv6'] == 'slaac') && !isset($wancfg['slaacusev4iface'])) ||
4315
			    !interface_isppp_type($interface)) {
4316
				interface_dhcpv6_configure($interface, $wancfg);
4317
			}
4318
			break;
4319
		case '6rd':
4320
			interface_6rd_configure($interface, $wancfg);
4321
			break;
4322
		case '6to4':
4323
			interface_6to4_configure($interface, $wancfg);
4324
			break;
4325
		case 'track6':
4326
			interface_track6_configure($interface, $wancfg, $linkupevent);
4327
			break;
4328
		default:
4329
			/* XXX: Kludge for now related to #3280 */
4330
			if (!in_array($tunnelif, array("gif", "gre", "ovp", "ips", "l2t"))) {
4331
				if (is_ipaddrv6($wancfg['ipaddrv6']) && $wancfg['subnetv6'] <> "") {
4332
					//pfSense_interface_setaddress($realif, "{$wancfg['ipaddrv6']}/{$wancfg['subnetv6']}");
4333
					// FIXME: Add IPv6 Support to the pfSense module
4334
					mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 {$wancfg['ipaddrv6']} prefixlen " . escapeshellarg($wancfg['subnetv6']));
4335
				}
4336
			}
4337
			break;
4338
	}
4339

    
4340
	if (($linkupevent == false) || (substr($realif, 0, 4) == "ovpn") || (substr($realif, 0, 5) == "ipsec")) {
4341
		$bridgetmp = link_interface_to_bridge($interface);
4342
		if (!empty($bridgetmp)) {
4343
			interface_bridge_add_member($bridgetmp, $realif);
4344
		}
4345
	}
4346

    
4347
	if (!is_platform_booting()) {
4348
		link_interface_to_vips($interface, "update");
4349

    
4350
		if ($tunnelif != 'gre') {
4351
			$gre = link_interface_to_tunnelif($interface, 'gre');
4352
			array_walk($gre, 'interface_gre_configure');
4353
		}
4354

    
4355
		if ($tunnelif != 'gif') {
4356
			$gif = link_interface_to_tunnelif($interface, 'gif');
4357
			array_walk($gif, 'interface_gif_configure');
4358
		}
4359

    
4360
		$grouptmp = link_interface_to_group($interface);
4361
		if (!empty($grouptmp)) {
4362
			array_walk($grouptmp, 'interface_group_add_member');
4363
		}
4364

    
4365
		if ($interface == "lan") {
4366
			/* make new hosts file */
4367
			system_hosts_generate();
4368
		}
4369

    
4370
		if ($reloadall == true) {
4371

    
4372
			/* reconfigure static routes (kernel may have deleted them) */
4373
			system_routing_configure($interface);
4374

    
4375
			/* reload ipsec tunnels */
4376
			send_event("service reload ipsecdns");
4377

    
4378
			if (config_path_enabled('dnsmasq')) {
4379
				services_dnsmasq_configure();
4380
			}
4381

    
4382
			if (config_path_enabled('unbound')) {
4383
				services_unbound_configure(true, $interface);
4384
			}
4385

    
4386
			/* update dyndns */
4387
			send_event("service reload dyndns {$interface}");
4388
		}
4389
	}
4390

    
4391
	if (!is_platform_booting() && (substr($realif, 0, 5) == 'l2tps')) {
4392
		vpn_l2tp_configure();
4393
	};
4394

    
4395
	if (!empty($wancfg['descr'])) {
4396
		mwexec("/sbin/ifconfig " . escapeshellarg($wancfg['if']) . " description " . escapeshellarg($wancfg['descr']));
4397
	};
4398

    
4399
	interfaces_staticarp_configure($interface);
4400
	return 0;
4401
}
4402

    
4403
function interface_track6_configure($interface, $wancfg, $linkupevent = false) {
4404
	global $g;
4405

    
4406
	if (!is_array($wancfg)) {
4407
		return;
4408
	}
4409

    
4410
	if (!isset($wancfg['enable'])) {
4411
		return;
4412
	}
4413

    
4414
	/* If the interface is not configured via another, exit */
4415
	if (empty($wancfg['track6-interface'])) {
4416
		return;
4417
	}
4418

    
4419
	/* always configure a link-local of fe80::1:1 on the track6 interfaces */
4420
	$realif = get_real_interface($interface);
4421
	$linklocal = find_interface_ipv6_ll($realif, true);
4422
	if (!empty($linklocal) && $linklocal != "fe80::1:1%{$realif}") {
4423
		mwexec("/sbin/ifconfig {$realif} inet6 fe80::1:1%{$realif} alias");
4424
	}
4425

    
4426
	$trackcfg = config_get_path("interfaces/{$wancfg['track6-interface']}");
4427
	if (!isset($trackcfg['enable'])) {
4428
		log_error(sprintf(gettext('Interface %1$s tracking non-existent interface %2$s'), $interface, $wancfg['track6-interface']));
4429
		return;
4430
	}
4431

    
4432
	$type = $trackcfg['ipaddrv6'];
4433
	switch ($type) {
4434
		case "6to4":
4435
			if (g_get('debug')) {
4436
				log_error(sprintf(gettext('Interface %1$s configured via %2$s type %3$s'), $interface, $wancfg['track6-interface'], $type));
4437
			}
4438
			interface_track6_6to4_configure($interface, $wancfg);
4439
			break;
4440
		case "6rd":
4441
			if (g_get('debug')) {
4442
				log_error(sprintf(gettext('Interface %1$s configured via %2$s type %3$s'), $interface, $wancfg['track6-interface'], $type));
4443
			}
4444
			interface_track6_6rd_configure($interface, $wancfg);
4445
			break;
4446
		case "dhcp6":
4447
			if ($linkupevent == true) {
4448
				/*
4449
				 * NOTE: Usually come here from rc.linkup calling so just call directly instead of generating event
4450
				 * 	Instead of disrupting all other v4 configuration just restart DHCPv6 client for now
4451
				 *
4452
				 * XXX: Probably DHCPv6 client should handle this automagically itself?
4453
				 */
4454
				$pidv6 = find_dhcp6c_process();
4455
				if ($pidv6) {
4456
					posix_kill($pidv6, SIGHUP);
4457
				}
4458
			}
4459
			break;
4460
	}
4461

    
4462
	if ($linkupevent == false && !is_platform_booting()) {
4463
		if (!function_exists('services_dhcpd_configure')) {
4464
			require_once("services.inc");
4465
		}
4466

    
4467
		/* restart dns servers (defering dhcpd reload) */
4468
		if (config_path_enabled('unbound')) {
4469
			services_unbound_configure(false, $interface);
4470
		}
4471
		if (config_path_enabled('dnsmasq')) {
4472
			services_dnsmasq_configure(false);
4473
		}
4474

    
4475
		/* reconfigure dhcpdv6 (leaving dhcpdv4 alone) */
4476
		services_dhcpd_configure("inet6");
4477
	}
4478

    
4479
	return 0;
4480
}
4481

    
4482
function interface_track6_6rd_configure($interface, $lancfg) {
4483
	global $interface_ipv6_arr_cache;
4484
	global $interface_snv6_arr_cache;
4485

    
4486
	if (!is_array($lancfg)) {
4487
		return;
4488
	}
4489

    
4490
	/* If the interface is not configured via another, exit */
4491
	if (empty($lancfg['track6-interface'])) {
4492
		return;
4493
	}
4494

    
4495
	$wancfg = config_get_path("interfaces/{$lancfg['track6-interface']}");
4496
	if (empty($wancfg)) {
4497
		log_error(sprintf(gettext('Interface %1$s tracking non-existent interface %2$s'), $interface, $lancfg['track6-interface']));
4498
		return;
4499
	}
4500

    
4501
	$ip4address = get_interface_ip($lancfg['track6-interface']);
4502
	if (!is_ipaddrv4($ip4address)) { /* XXX: This should not be needed by 6rd || (is_private_ip($ip4address))) { */
4503
		log_error(sprintf(gettext('The interface IPv4 \'%1$s\' address on interface \'%2$s\' is not valid, not configuring 6RD tunnel'), $ip4address, $lancfg['track6-interface']));
4504
		return;
4505
	}
4506
	$hexwanv4 = return_hex_ipv4($ip4address);
4507

    
4508
	/* create the long prefix notation for math, save the prefix length */
4509
	$rd6prefix = explode("/", $wancfg['prefix-6rd']);
4510
	$rd6prefixlen = $rd6prefix[1];
4511
	$rd6prefix = Net_IPv6::uncompress($rd6prefix[0]);
4512

    
4513
	/* binary presentation of the prefix for all 128 bits. */
4514
	$rd6lanbin = convert_ipv6_to_128bit($rd6prefix);
4515

    
4516
	/* just save the left prefix length bits */
4517
	$rd6lanbin = substr($rd6lanbin, 0, $rd6prefixlen);
4518
	/* add the v4 address, offset n bits from the left */
4519
	$rd6lanbin .= substr(sprintf("%032b", hexdec($hexwanv4)), (0 + $wancfg['prefix-6rd-v4plen']), 32);
4520

    
4521
	/* add the custom prefix id, max 32bits long? (64 bits - (prefixlen + (32 - v4plen)) */
4522
	/* 64 - (37 + (32 - 17)) = 8 == /52 */
4523
	$restbits = 64 - ($rd6prefixlen + (32 - $wancfg['prefix-6rd-v4plen']));
4524
	// echo "64 - (prefixlen {$rd6prefixlen} + v4len (32 - {$wancfg['prefix-6rd-v4plen']})) = {$restbits} \n";
4525
	$rd6lanbin .= substr(sprintf("%032b", str_pad($lancfg['track6-prefix-id'], 32, "0", STR_PAD_LEFT)), (32 - $restbits), 32);
4526
	/* fill the rest out with zeros */
4527
	$rd6lanbin = str_pad($rd6lanbin, 128, "0", STR_PAD_RIGHT);
4528

    
4529
	/* convert the 128 bits for the lan address back into a valid IPv6 address */
4530
	$rd6lan = convert_128bit_to_ipv6($rd6lanbin) ."1";
4531

    
4532
	$lanif = get_real_interface($interface);
4533
	$oip = find_interface_ipv6($lanif);
4534
	if (is_ipaddrv6($oip)) {
4535
		mwexec("/sbin/ifconfig {$lanif} inet6 {$oip} delete");
4536
	}
4537
	unset($interface_ipv6_arr_cache[$lanif]);
4538
	unset($interface_snv6_arr_cache[$lanif]);
4539
	log_error(sprintf(gettext('rd6 %1$s with ipv6 address %2$s based on %3$s ipv4 %4$s'), $interface, $rd6lan, $lancfg['track6-interface'], $ip4address));
4540
	mwexec("/sbin/ifconfig {$lanif} inet6 {$rd6lan} prefixlen 64");
4541

    
4542
	return 0;
4543
}
4544

    
4545
function interface_track6_6to4_configure($interface, $lancfg) {
4546
	global $interface_ipv6_arr_cache;
4547
	global $interface_snv6_arr_cache;
4548

    
4549
	if (!is_array($lancfg)) {
4550
		return;
4551
	}
4552

    
4553
	/* If the interface is not configured via another, exit */
4554
	if (empty($lancfg['track6-interface'])) {
4555
		return;
4556
	}
4557

    
4558
	$wancfg = config_get_path("interfaces/{$lancfg['track6-interface']}");
4559
	if (empty($wancfg)) {
4560
		log_error(sprintf(gettext('Interface %1$s tracking non-existent interface %2$s'), $interface, $lancfg['track6-interface']));
4561
		return;
4562
	}
4563

    
4564
	$ip4address = get_interface_ip($lancfg['track6-interface']);
4565
	if (!is_ipaddrv4($ip4address) || is_private_ip($ip4address)) {
4566
		log_error(sprintf(gettext('The interface IPv4 \'%1$s\' address on interface \'%2$s\' is not public, not configuring 6RD tunnel'), $ip4address, $lancfg['track6-interface']));
4567
		return;
4568
	}
4569
	$hexwanv4 = return_hex_ipv4($ip4address);
4570

    
4571
	/* create the long prefix notation for math, save the prefix length */
4572
	$sixto4prefix = "2002::";
4573
	$sixto4prefixlen = 16;
4574
	$sixto4prefix = Net_IPv6::uncompress($sixto4prefix);
4575

    
4576
	/* binary presentation of the prefix for all 128 bits. */
4577
	$sixto4lanbin = convert_ipv6_to_128bit($sixto4prefix);
4578

    
4579
	/* just save the left prefix length bits */
4580
	$sixto4lanbin = substr($sixto4lanbin, 0, $sixto4prefixlen);
4581
	/* add the v4 address */
4582
	$sixto4lanbin .= sprintf("%032b", hexdec($hexwanv4));
4583
	/* add the custom prefix id */
4584
	$sixto4lanbin .= sprintf("%016b", $lancfg['track6-prefix-id']);
4585
	/* fill the rest out with zeros */
4586
	$sixto4lanbin = str_pad($sixto4lanbin, 128, "0", STR_PAD_RIGHT);
4587

    
4588
	/* convert the 128 bits for the lan address back into a valid IPv6 address */
4589
	$sixto4lan = convert_128bit_to_ipv6($sixto4lanbin) ."1";
4590

    
4591
	$lanif = get_real_interface($interface);
4592
	$oip = find_interface_ipv6($lanif);
4593
	if (is_ipaddrv6($oip)) {
4594
		mwexec("/sbin/ifconfig {$lanif} inet6 {$oip} delete");
4595
	}
4596
	unset($interface_ipv6_arr_cache[$lanif]);
4597
	unset($interface_snv6_arr_cache[$lanif]);
4598
	log_error(sprintf(gettext('sixto4 %1$s with ipv6 address %2$s based on %3$s ipv4 %4$s'), $interface, $sixto4lan, $lancfg['track6-interface'], $ip4address));
4599
	mwexec("/sbin/ifconfig {$lanif} inet6 {$sixto4lan} prefixlen 64");
4600

    
4601
	return 0;
4602
}
4603

    
4604
function interface_6rd_configure($interface, $wancfg) {
4605
	global $g;
4606

    
4607
	/* because this is a tunnel interface we can only function
4608
	 *	with a public IPv4 address on the interface */
4609

    
4610
	if (!is_array($wancfg)) {
4611
		return;
4612
	}
4613

    
4614
	if (!is_module_loaded('if_stf.ko')) {
4615
		mwexec('/sbin/kldload if_stf.ko');
4616
	}
4617

    
4618
	$wanif = get_real_interface($interface);
4619
	$ip4address = find_interface_ip($wanif);
4620
	if (!is_ipaddrv4($ip4address)) {
4621
		log_error(sprintf(gettext('The interface IPv4 \'%1$s\' address on interface \'%2$s\' is not public, not configuring 6RD tunnel'), $ip4address, $wanif));
4622
		return false;
4623
	}
4624
	$hexwanv4 = return_hex_ipv4($ip4address);
4625

    
4626
	if (!is_numeric($wancfg['prefix-6rd-v4plen'])) {
4627
		$wancfg['prefix-6rd-v4plen'] = 0;
4628
	}
4629

    
4630
	/* create the long prefix notation for math, save the prefix length */
4631
	$rd6prefix = explode("/", $wancfg['prefix-6rd']);
4632
	$rd6prefixlen = $rd6prefix[1];
4633
	$brgw = explode('.', $wancfg['gateway-6rd']);
4634
	$rd6brgw = substr(ip6_to_bin($rd6prefix[0]), 0, $rd6prefixlen);
4635
	$rd6brgw .= str_pad(decbin($brgw[0]), 8, '0', STR_PAD_LEFT) . str_pad(decbin($brgw[1]), 8, '0', STR_PAD_LEFT) . str_pad(decbin($brgw[2]), 8, '0', STR_PAD_LEFT) . str_pad(decbin($brgw[3]), 8, '0', STR_PAD_LEFT);
4636
	if (strlen($rd6brgw) < 128) {
4637
		$rd6brgw = str_pad($rd6brgw, 128, '0', STR_PAD_RIGHT);
4638
	}
4639
	$rd6brgw = bin_to_compressed_ip6($rd6brgw);
4640
	unset($brgw);
4641
	$rd6prefix = Net_IPv6::uncompress($rd6prefix[0]);
4642

    
4643
	/* binary presentation of the prefix for all 128 bits. */
4644
	$rd6prefixbin = convert_ipv6_to_128bit($rd6prefix);
4645

    
4646
	/* just save the left prefix length bits */
4647
	$rd6prefixbin = substr($rd6prefixbin, 0, $rd6prefixlen);
4648
	/* if the prefix length is not 32 bits we need to shave bits off from the left of the v4 address. */
4649
	$rd6prefixbin .= substr(sprintf("%032b", hexdec($hexwanv4)), $wancfg['prefix-6rd-v4plen'], 32);
4650
	/* fill out the rest with 0's */
4651
	$rd6prefixbin = str_pad($rd6prefixbin, 128, "0", STR_PAD_RIGHT);
4652

    
4653
	/* convert the 128 bits for the broker address back into a valid IPv6 address */
4654
	$rd6prefix = convert_128bit_to_ipv6($rd6prefixbin);
4655

    
4656

    
4657
	/* XXX: need to extend to support variable prefix size for v4 */
4658
	$stfiface = "{$interface}_stf";
4659
	if (does_interface_exist($stfiface)) {
4660
		pfSense_interface_destroy($stfiface);
4661
	}
4662
	$tmpstfiface = pfSense_interface_create2("stf");
4663
	if (!is_string($tmpstfiface)) {
4664
		log_error(sprintf(gettext('Failed to configure interface %1$s: Could not create temporary interface %2$s'), $wanif, 'stf'));
4665
		return false;
4666
	}
4667
	pfSense_interface_rename($tmpstfiface, $stfiface);
4668
	pfSense_interface_flags($stfiface, IFF_LINK2);
4669
	mwexec("/sbin/ifconfig {$stfiface} inet6 {$rd6prefix}/{$rd6prefixlen}");
4670
	mwexec("/sbin/ifconfig {$stfiface} stfv4br " . escapeshellarg($wancfg['gateway-6rd']));
4671
	if ($wancfg['prefix-6rd-v4plen'] >= 0 && $wancfg['prefix-6rd-v4plen'] <= 32) {
4672
		mwexec("/sbin/ifconfig {$stfiface} stfv4net {$ip4address}/" . escapeshellarg($wancfg['prefix-6rd-v4plen']));
4673
	}
4674
	$parentmtu = get_interface_mtu(get_real_interface($interface));
4675
	if (isset($wancfg['mtu']) && ($wancfg['mtu'] >= 1300)) {
4676
		set_interface_mtu($stfiface, $wancfg['mtu'] - 20);
4677
	} elseif ($parentmtu > 1300) {
4678
		set_interface_mtu($stfiface, $parentmtu - 20);
4679
	}
4680
	if (g_get('debug')) {
4681
		log_error("Created 6rd interface {$stfiface} {$rd6prefix}/{$rd6prefixlen}");
4682
	}
4683

    
4684
	/* write out a default router file */
4685
	file_put_contents("{$g['tmp_path']}/{$wanif}_routerv6", "{$rd6brgw}\n");
4686
	unlink_if_exists("{$g['tmp_path']}/{$wanif}_routerv6.last");
4687
	file_put_contents("{$g['tmp_path']}/{$wanif}_defaultgwv6", "{$rd6brgw}\n");
4688

    
4689
	$ip4gateway = get_interface_gateway($interface);
4690
	if (is_ipaddrv4($ip4gateway)) {
4691
		route_add_or_change($wancfg['gateway-6rd'], $ip4gateway);
4692
	}
4693

    
4694
	/* configure dependent interfaces */
4695
	if (!is_platform_booting()) {
4696
		link_interface_to_track6($interface, "update");
4697
	}
4698

    
4699
	return 0;
4700
}
4701

    
4702
function interface_6to4_configure($interface, $wancfg) {
4703
	global $g;
4704

    
4705
	/* because this is a tunnel interface we can only function
4706
	 *	with a public IPv4 address on the interface */
4707

    
4708
	if (!is_array($wancfg)) {
4709
		return;
4710
	}
4711

    
4712
	$wanif = get_real_interface($interface);
4713
	$ip4address = find_interface_ip($wanif);
4714
	if ((!is_ipaddrv4($ip4address)) || (is_private_ip($ip4address))) {
4715
		log_error(sprintf(gettext('The interface IPv4 \'%1$s\' address on interface \'%2$s\' is not public, not configuring 6RD tunnel'), $ip4address, $wanif));
4716
		return false;
4717
	}
4718

    
4719
	/* create the long prefix notation for math, save the prefix length */
4720
	$stfprefixlen = 16;
4721
	$stfprefix = Net_IPv6::uncompress("2002::");
4722
	$stfarr = explode(":", $stfprefix);
4723
	$v4prefixlen = "0";
4724

    
4725
	/* we need the hex form of the interface IPv4 address */
4726
	$ip4arr = explode(".", $ip4address);
4727
	$hexwanv4 = "";
4728
	foreach ($ip4arr as $octet) {
4729
		$hexwanv4 .= sprintf("%02x", $octet);
4730
	}
4731

    
4732
	/* we need the hex form of the broker IPv4 address */
4733
	$ip4arr = explode(".", "192.88.99.1");
4734
	$hexbrv4 = "";
4735
	foreach ($ip4arr as $octet) {
4736
		$hexbrv4 .= sprintf("%02x", $octet);
4737
	}
4738

    
4739
	/* binary presentation of the prefix for all 128 bits. */
4740
	$stfprefixbin = "";
4741
	foreach ($stfarr as $element) {
4742
		$stfprefixbin .= sprintf("%016b", hexdec($element));
4743
	}
4744
	/* just save the left prefix length bits */
4745
	$stfprefixstartbin = substr($stfprefixbin, 0, $stfprefixlen);
4746

    
4747
	/* if the prefix length is not 32 bits we need to shave bits off from the left of the v4 address. */
4748
	$stfbrokerbin = substr(sprintf("%032b", hexdec($hexbrv4)), $v4prefixlen, 32);
4749
	$stfbrokerbin = str_pad($stfprefixstartbin . $stfbrokerbin, 128, "0", STR_PAD_RIGHT);
4750

    
4751
	/* for the local subnet too. */
4752
	$stflanbin = substr(sprintf("%032b", hexdec($hexwanv4)), $v4prefixlen, 32);
4753
	$stflanbin = str_pad($stfprefixstartbin . $stflanbin, 128, "0", STR_PAD_RIGHT);
4754

    
4755
	/* convert the 128 bits for the broker address back into a valid IPv6 address */
4756
	$stfbrarr = array();
4757
	$stfbrbinarr = array();
4758
	$stfbrbinarr = str_split($stfbrokerbin, 16);
4759
	foreach ($stfbrbinarr as $bin) {
4760
		$stfbrarr[] = dechex(bindec($bin));
4761
	}
4762
	$stfbrgw = text_to_compressed_ip6(implode(":", $stfbrarr));
4763

    
4764
	/* convert the 128 bits for the broker address back into a valid IPv6 address */
4765
	$stflanarr = array();
4766
	$stflanbinarr = array();
4767
	$stflanbinarr = str_split($stflanbin, 16);
4768
	foreach ($stflanbinarr as $bin) {
4769
		$stflanarr[] = dechex(bindec($bin));
4770
	}
4771
	$stflanpr = text_to_compressed_ip6(implode(":", $stflanarr));
4772
	$stflanarr[7] = 1;
4773

    
4774
	/* setup the stf interface */
4775
	if (!is_module_loaded("if_stf")) {
4776
		mwexec("/sbin/kldload if_stf.ko");
4777
	}
4778
	$stfiface = "{$interface}_stf";
4779
	if (does_interface_exist($stfiface)) {
4780
		pfSense_interface_destroy($stfiface);
4781
	}
4782
	$tmpstfiface = pfSense_interface_create2("stf");
4783
	if (!is_string($tmpstfiface)) {
4784
		log_error(sprintf(gettext('Failed to configure interface %1$s: Could not create temporary interface %2$s'), $wanif, 'stf'));
4785
		return;
4786
	}
4787
	pfSense_interface_rename($tmpstfiface, $stfiface);
4788
	pfSense_interface_flags($stfiface, IFF_LINK2);
4789
	mwexec("/sbin/ifconfig {$stfiface} inet6 {$stflanpr} prefixlen 16");
4790

    
4791
	$parentmtu = get_interface_mtu(get_real_interface($interface));
4792
	if (isset($wancfg['mtu']) && ($wancfg['mtu'] >= 1300)) {
4793
		set_interface_mtu($stfiface, $wancfg['mtu'] - 20);
4794
	} elseif ($parentmtu > 1300) {
4795
		set_interface_mtu($stfiface, $parentmtu - 20);
4796
	}
4797
	if (g_get('debug')) {
4798
		log_error("Set IPv6 address inet6 {$stflanpr} prefixlen 16 for {$stfiface}, route {$stfbrgw}");
4799
	}
4800

    
4801
	/* write out a default router file */
4802
	file_put_contents("{$g['tmp_path']}/{$wanif}_routerv6", "{$stfbrgw}");
4803
	unlink_if_exists("{$g['tmp_path']}/{$wanif}_routerv6.last");
4804
	file_put_contents("{$g['tmp_path']}/{$wanif}_defaultgwv6", "{$stfbrgw}");
4805

    
4806
	$ip4gateway = get_interface_gateway($interface);
4807
	if (is_ipaddrv4($ip4gateway)) {
4808
		route_add_or_change("192.88.99.1", $ip4gateway);
4809
	}
4810

    
4811
	if (!is_platform_booting()) {
4812
		link_interface_to_track6($interface, "update");
4813
	}
4814

    
4815
	return 0;
4816
}
4817

    
4818
function interface_dhcpv6_configure($ifconf, $ifcfg, $destroy = false) {
4819
	global $g;
4820

    
4821
	$dhcp6cconf = "";
4822
	$id = "0";
4823
	$dhcp6cinterfaces = array();
4824
	$dhcp6cifs_descr = array();
4825
	$dhcp6crealifs = array();
4826
	$debugOption = "-d";
4827
	$noreleaseOption = "";
4828

    
4829
	if (!empty(config_get_path('system/global-v6duid'))) {
4830
		// Write the DUID file
4831
		if(!write_dhcp6_duid(config_get_path('system/global-v6duid'))) {
4832
		    log_error(gettext("Failed to write user DUID file!"));
4833
		}
4834
	}
4835

    
4836
	foreach (config_get_path('interfaces', []) as $interface => $wancfg) {
4837
		$wanif = get_real_interface($interface, "inet6");
4838

    
4839
		if (($ifconf == $interface) && $destroy) {
4840
			unlink_if_exists("{$g['varetc_path']}/dhcp6c_{$interface}_script.sh");
4841
			unlink_if_exists("{$g['varetc_path']}/dhcp6c_{$interface}_dhcp6withoutra_script.sh");
4842
			unlink_if_exists("{$g['varetc_path']}/rtsold_{$wanif}_script.sh");
4843
			unlink_if_exists(g_get('vardb_path') . "/{$wanif}_cacheipv6");
4844
			continue;
4845
		}
4846

    
4847
		if (!isset($wancfg['enable']) || (($ifconf == $interface) && $destroy) ||
4848
		    (($wancfg['ipaddrv6'] != 'dhcp6') && ($wancfg['ipaddrv6'] != 'slaac'))) {
4849
			continue;
4850
		}
4851

    
4852
		$dhcp6cinterfaces[$interface] = $wancfg;
4853

    
4854
		if (config_path_enabled('system','dhcp6debug')) {
4855
			$debugOption = "-D";
4856
		}
4857
		if (config_path_enabled('system','dhcp6norelease')) {
4858
			$noreleaseOption = "-n";
4859
		}
4860

    
4861
		/* accept router advertisements for this interface                 */
4862
		/* Moved to early in the function as sometimes interface not ready */
4863
		/* RTSOLD fails as interface does not accept .....                 */
4864

    
4865
		log_error("Accept router advertisements on interface {$wanif} ");
4866
		mwexec("/sbin/ifconfig {$wanif} inet6 accept_rtadv");
4867

    
4868
		if ($wancfg['adv_dhcp6_config_file_override']) {
4869
			// DHCP6 Config File Override
4870
			$dhcp6cconf .= DHCP6_Config_File_Override($wancfg, $wanif);
4871
		} elseif ($wancfg['adv_dhcp6_config_advanced']) {
4872
			// DHCP6 Config File Advanced
4873
			$dhcp6cconf .= DHCP6_Config_File_Advanced($interface, $wancfg, $wanif);
4874
		} else {
4875
			// DHCP6 Config File Basic
4876
			$dhcp6cconf .= "interface {$wanif} {\n";
4877

    
4878
			/* for SLAAC interfaces we do fire off a dhcp6 client for just our name servers */
4879
			if ($wancfg['ipaddrv6'] == "slaac") {
4880
				$dhcp6cconf .= "\tinformation-only;\n";
4881
				$dhcp6cconf .= "\trequest domain-name-servers;\n";
4882
				$dhcp6cconf .= "\trequest domain-name;\n";
4883
				$dhcp6cconf .= "\tscript \"{$g['varetc_path']}/dhcp6c_{$interface}_script.sh\"; # we'd like some nameservers please\n";
4884
				$dhcp6cconf .= "};\n";
4885
			} else {
4886
				$trackiflist = array();
4887
				$iflist = link_interface_to_track6($interface);
4888
				foreach ($iflist as $ifname => $ifcfg) {
4889
					if (is_numeric($ifcfg['track6-prefix-id'])) {
4890
						$trackiflist[$ifname] = $ifcfg;
4891
					}
4892
				}
4893

    
4894
				/* skip address request if this is set */
4895
				if (!isset($wancfg['dhcp6prefixonly'])) {
4896
					$dhcp6cconf .= "\tsend ia-na {$id};\t# request stateful address\n";
4897
				}
4898
				if (is_numeric($wancfg['dhcp6-ia-pd-len'])) {
4899
					$dhcp6cconf .= "\tsend ia-pd {$id};\t# request prefix delegation\n";
4900
				}
4901

    
4902
				$dhcp6cconf .= "\trequest domain-name-servers;\n";
4903
				$dhcp6cconf .= "\trequest domain-name;\n";
4904

    
4905
				/*
4906
				 * dhcp6c will run different scripts depending on
4907
				 * whether dhcpwithoutra is set or unset.
4908
				 */
4909
				if (isset($wancfg['dhcp6withoutra'])) {
4910
					$dhcp6cconf .= "\tscript \"{$g['varetc_path']}/dhcp6c_{$interface}_dhcp6withoutra_script.sh\"; # we'd like nameservers and RTSOLD to do all the work\n";
4911
				} else {
4912
					$dhcp6cconf .= "\tscript \"{$g['varetc_path']}/dhcp6c_{$interface}_script.sh\"; # we'd like some nameservers please\n";
4913
				}
4914
				$dhcp6cconf .= "};\n";
4915

    
4916
				if (!isset($wancfg['dhcp6prefixonly'])) {
4917
					$dhcp6cconf .= "id-assoc na {$id} { };\n";
4918
				}
4919

    
4920
				if (is_numeric($wancfg['dhcp6-ia-pd-len'])) {
4921
					/* Setup the prefix delegation */
4922
					$dhcp6cconf .= "id-assoc pd {$id} {\n";
4923
					$preflen = 64 - $wancfg['dhcp6-ia-pd-len'];
4924
					if (isset($wancfg['dhcp6-ia-pd-send-hint'])) {
4925
						$dhcp6cconf .= "\tprefix ::/{$preflen} infinity;\n";
4926
					}
4927
					foreach ($trackiflist as $friendly => $ifcfg) {
4928
						if (g_get('debug')) {
4929
							log_error("setting up $interface - {$ifcfg['track6-prefix-id']}");
4930
						}
4931
						$realif = get_real_interface($friendly);
4932
						$dhcp6cconf .= "\tprefix-interface {$realif} {\n";
4933
						$dhcp6cconf .= "\t\tsla-id {$ifcfg['track6-prefix-id']};\n";
4934
						$dhcp6cconf .= "\t\tsla-len {$wancfg['dhcp6-ia-pd-len']};\n";
4935
						$dhcp6cconf .= "\t};\n";
4936
					}
4937
					unset($preflen, $iflist, $ifcfg, $ifname);
4938
					$dhcp6cconf .= "};\n\n";
4939
				}
4940
				unset($trackiflist);
4941
			}
4942
			$id++;
4943
		}
4944

    
4945
		/*************** Script Debug Logging ***************************
4946
		Both dhcp6 scripts now have a logging message built in.
4947
		These logging messages ONLY appear if dhcp6c debug logging is set.
4948
		The logging messages appear in the dhcp section of the logs,
4949
		not in system.
4950

    
4951
		These scripts now also take advantage of the REASON= env vars
4952
		supplied by dhcp6c.
4953
		****************************************************************/
4954

    
4955
		/* Script create for dhcp6withoutRA mode */
4956
		/* dhcp6c will launch rtsold. rtsold will then run the wan ipv6 configure */
4957
		$dhcp6cscriptwithoutra = "#!/bin/sh\n";
4958
		$dhcp6cscriptwithoutra .= "# This shell script launches rtsold.\n";
4959
		$dhcp6cscriptwithoutra .= "dmips=\${new_domain_name_servers}\n";
4960
		$dhcp6cscriptwithoutra .= "dmnames=\${new_domain_name}\n";
4961
		$dhcp6cscriptwithoutra .= "dreason=\${REASON}\n";
4962
		// Need to pass params to  the final script
4963
		$dhcp6cscriptwithoutra .= "echo \$dmips > /tmp/{$wanif}_domain_name_servers\n";
4964
		$dhcp6cscriptwithoutra .= "echo \$dmnames > /tmp/{$wanif}_new_domain_name\n";
4965
		$dhcp6cscriptwithoutra .= "echo \$dreason > /tmp/{$wanif}_reason\n";
4966
		$dhcp6cscriptwithoutra .= "case \$REASON in\n";
4967
		$dhcp6cscriptwithoutra .= "REQUEST)\n";
4968
		$dhcp6cscriptwithoutra .= "/usr/sbin/rtsold -1 -p {$g['varrun_path']}/rtsold_{$wanif}.pid -A {$g['varetc_path']}/rtsold_{$wanif}_script.sh {$wanif}\n";
4969
		if ($debugOption == '-D') {
4970
			$dhcp6cscriptwithoutra .= "/usr/bin/logger -t dhcp6c \"dhcp6c REQUEST on {$wanif} - running rtsold\"\n";
4971
		}
4972
		$dhcp6cscriptwithoutra .= ";;\n";
4973
		$dhcp6cscriptwithoutra .= "REBIND)\n";
4974
		if ($debugOption == '-D') {
4975
			$dhcp6cscriptwithoutra .= "/usr/bin/logger -t dhcp6c \"dhcp6c rebind on {$wanif}\"\n";
4976
		}
4977
		$dhcp6cscriptwithoutra .= ";;\n";
4978
		if (isset($wancfg['dhcp6norelease'])) {
4979
			$dhcp6cscriptwithoutra .= "EXIT)\n";
4980
		} else {
4981
			$dhcp6cscriptwithoutra .= "RELEASE)\n";
4982
		}
4983
		if ($debugOption == '-D') {
4984
			$dhcp6cscriptwithoutra .= "/usr/bin/logger -t dhcp6c \"dhcp6c EXIT or RELEASE on {$wanif} running rc.newwanipv6\"\n";
4985
		}
4986
		$dhcp6cscriptwithoutra .= "/usr/local/sbin/fcgicli -f /etc/rc.newwanipv6 -d \"interface={$wanif}&dmnames=\${dmnames}&dmips=\${dmips}\"\n";
4987
		$dhcp6cscriptwithoutra .= ";;\n";
4988
		$dhcp6cscriptwithoutra .= "RENEW|INFO)\n";
4989
		if ($debugOption == '-D') {
4990
			$dhcp6cscriptwithoutra .= "/usr/bin/logger -t dhcp6c \"dhcp6c renew, no change - bypassing update on {$wanif}\"\n";
4991
		}
4992
		$dhcp6cscriptwithoutra .= "esac\n";
4993
		if (!@file_put_contents(
4994
		    "{$g['varetc_path']}/dhcp6c_{$interface}_dhcp6withoutra_script.sh",
4995
		    $dhcp6cscriptwithoutra)) {
4996
			printf("Error: cannot open " .
4997
			    "dhcp6c_{$interface}_dhcp6cwithoutra_script.sh in " .
4998
			    "interface_dhcpv6_configure() for writing.\n");
4999
			unset($dhcp6cscriptwithoutra);
5000
			return 1;
5001
		}
5002

    
5003
		@chmod("{$g['varetc_path']}/dhcp6c_{$interface}_dhcp6withoutra_script.sh", 0755);
5004

    
5005
		/*
5006
		 * Dual mode wan_dhcp6c script with variations depending on node
5007
		 * dhcp6 will run the wan ipv6 configure
5008
		 */
5009
		$dhcp6cscript  = "#!/bin/sh\n";
5010
		$dhcp6cscript .= "# This shell script launches /etc/rc.newwanipv6 with a interface argument.\n";
5011
		if (!isset($wancfg['dhcp6withoutra'])) {
5012
			$dhcp6cscript .= "dmips=\${new_domain_name_servers}\n";
5013
			$dhcp6cscript .= "dmnames=\${new_domain_name}\n";
5014
			$dhcp6cscript .= "case \$REASON in\n";
5015
			$dhcp6cscript .= "REBIND)\n";
5016
			if ($debugOption == '-D') {
5017
				$dhcp6cscript .= "/usr/bin/logger -t dhcp6c \"dhcp6c rebind on {$wanif}\"\n";
5018
			}
5019
			$dhcp6cscript .= ";;\n";
5020
			$dhcp6cscript .= "REQUEST|";
5021
			if (isset($wancfg['dhcp6norelease'])) {
5022
				$dhcp6cscript .= "EXIT)\n";
5023
			} else {
5024
				$dhcp6cscript .= "RELEASE)\n";
5025
			}
5026
			if ($debugOption == '-D') {
5027
				$dhcp6cscript .= "/usr/bin/logger -t dhcp6c \"dhcp6c RELEASE, REQUEST or EXIT on {$wanif} running rc.newwanipv6\"\n";
5028
			}
5029
			$dhcp6cscript .= "/usr/local/sbin/fcgicli -f /etc/rc.newwanipv6 -d \"interface={$wanif}&dmnames=\${dmnames}&dmips=\${dmips}\"\n";
5030
			$dhcp6cscript .= ";;\n";
5031
			$dhcp6cscript .= "RENEW|INFO)\n";
5032
			if ($debugOption == '-D') {
5033
				$dhcp6cscript .= "/usr/bin/logger -t dhcp6c \"dhcp6c renew, no change - bypassing update on {$wanif}\"\n";
5034
			}
5035
			$dhcp6cscript .= "esac\n";
5036
			$rtsold_ra_ifs[] = $wanif;
5037
		} else {
5038
			// Need to get the parameters from the dhcp6cwithoutRA run
5039
			$dhcp6cscript .= "dmips=\$(cat \"/tmp/{$wanif}_domain_name_servers\")\n";
5040
			$dhcp6cscript .= "dmnames=\$(cat \"/tmp/{$wanif}_new_domain_name\")\n";
5041
			$dhcp6cscript .= "/bin/sleep 1\n";
5042
			$dhcp6cscript .= "/usr/local/sbin/fcgicli -f /etc/rc.newwanipv6 -d \"interface={$wanif}&dmnames=\${dmnames}&dmips=\${dmips}\"\n";
5043
		}
5044

    
5045
		/* Add wide-dhcp6c shell script here. Because we can not pass a argument to it. */
5046
		if (!@file_put_contents("{$g['varetc_path']}/dhcp6c_{$interface}_script.sh", $dhcp6cscript)) {
5047
			printf("Error: cannot open dhcp6c_{$interface}_script.sh in interface_dhcpv6_configure() for writing.\n");
5048
			unset($dhcp6cscript);
5049
			return 1;
5050
		}
5051
		unset($dhcp6cscript);
5052
		@chmod("{$g['varetc_path']}/dhcp6c_{$interface}_script.sh", 0755);
5053
	}
5054

    
5055
	if (!empty($dhcp6cinterfaces)) {
5056
		/* wide-dhcp6c works for now. */
5057
		if (!@file_put_contents("{$g['varetc_path']}/dhcp6c.conf", $dhcp6cconf)) {
5058
			printf("Error: cannot open dhcp6c.conf in interface_dhcpv6_configure() for writing.\n");
5059
			return 1;
5060
		}
5061
		foreach ($dhcp6cinterfaces as $interface => $wancfg) {
5062
			$dhcp6cifs_descr[] = $interface . '(' . $wancfg['if'] . ')';
5063
			$dhcp6crealifs[] = $wancfg['if'];
5064
		}
5065
		$dhcp6cdescr = implode(',', $dhcp6cifs_descr);
5066
		$dhcp6cifs = implode(' ', $dhcp6crealifs);
5067
		foreach ($dhcp6cinterfaces as $interface => $wancfg) {
5068
			$wanif = get_real_interface($interface, "inet6");
5069

    
5070
			$rtsoldscript_header = <<<EOD
5071
#!/bin/sh
5072
# This shell script launches dhcp6c and configured gateways for this interface.
5073
if [ -n "\$2" ]; then
5074
	if [ -n "$(echo \$2 | /usr/bin/grep '^fe80')" ]; then
5075
		echo \$2\%{$wanif} > {$g['tmp_path']}/{$wanif}_routerv6
5076
		/bin/rm -f {$g['tmp_path']}/{$wanif}_routerv6.last
5077
		echo \$2\%{$wanif} > {$g['tmp_path']}/{$wanif}_defaultgwv6
5078
	else
5079
		echo \$2 > {$g['tmp_path']}/{$wanif}_routerv6
5080
		/bin/rm -f {$g['tmp_path']}/{$wanif}_routerv6.last
5081
		echo \$2 > {$g['tmp_path']}/{$wanif}_defaultgwv6
5082
	fi
5083
	/usr/bin/logger -t rtsold "Received RA specifying route \$2 for interface {$interface}({$wanif})"
5084
fi
5085

    
5086
EOD;
5087

    
5088
			/* non ipoe Process */
5089
			$rtsoldscript = $rtsoldscript_header;
5090
			if (!isset($wancfg['dhcp6withoutra'])) {
5091
				/*
5092
				 * We only want this script to run once, and if it runs twice
5093
				 * then do not launch dhcp6c again, this only happens if
5094
				 * dhcpwithoutra is not set.
5095
				 *
5096
				 * Check for a lock file, trying to prevent multiple instances
5097
				 * of dhcp6c being launched
5098
				 */
5099
				$rtsoldscript .= "if [ ! -f /tmp/dhcp6c_lock ]; then\n";
5100
				/*
5101
				 * Create the lock file, trying to prevent multiple instances
5102
				 * of dhcp6c being launched
5103
				 */
5104
				$rtsoldscript .= "\t/usr/bin/touch /tmp/dhcp6c_lock\n";
5105
				$rtsoldscript .= "\tif [ -f {$g['varrun_path']}/dhcp6c.pid ]; then\n";
5106
				$rtsoldscript .= "\t\t/bin/pkill -F {$g['varrun_path']}/dhcp6c.pid\n";
5107
				$rtsoldscript .= "\t\t/bin/rm -f {$g['varrun_path']}/dhcp6c.pid\n";
5108
				$rtsoldscript .= "\t\t/bin/sleep 1\n";
5109
				$rtsoldscript .= "\tfi\n";
5110
				$rtsoldscript .= "\t/usr/local/sbin/dhcp6c {$debugOption} " .
5111
				    "{$noreleaseOption} -c {$g['varetc_path']}/dhcp6c.conf " .
5112
				    "-p {$g['varrun_path']}/dhcp6c.pid {$dhcp6cifs}\n";
5113
				$rtsoldscript .= "\t/usr/bin/logger -t rtsold \"Starting dhcp6 client for interfaces {$dhcp6cdescr}\"\n";
5114
				$rtsoldscript .= "else\n";
5115
				$rtsoldscript .= "\t/usr/bin/logger -t rtsold \"RTSOLD Lock in place - sending SIGHUP to dhcp6c\"\n";
5116
				$rtsoldscript .= "\tdhcp6c_pid=\$(cat \"{$g['varrun_path']}/dhcp6c.pid\")\n";
5117
				$rtsoldscript .= "\t/bin/kill -1 \${dhcp6c_pid}\n";
5118
				$rtsoldscript .= "fi\n";
5119
			} else {
5120
				/*
5121
				 * The script needs to run in dhcp6withoutra mode as RA may
5122
				 * not have been received, or there can be a delay with
5123
				 * certain ISPs
5124
				 */
5125
				$rtsoldscript .= "{$g['varetc_path']}/dhcp6c_{$interface}_script.sh\n";
5126
				$rtsoldscript .= "/bin/sleep 1\n";
5127
			}
5128

    
5129
			/* Add wide-dhcp6c shell script here. Because we can not pass a argument to it. */
5130
			if (!@file_put_contents("{$g['varetc_path']}/rtsold_{$wanif}_script.sh", $rtsoldscript)) {
5131
				printf("Error: cannot open rtsold_{$wanif}_script.sh in interface_dhcpv6_configure() for writing.\n");
5132
				return 1;
5133
			}
5134
			unset($rtsoldscript);
5135
			@chmod("{$g['varetc_path']}/rtsold_{$wanif}_script.sh", 0755);
5136
		}
5137

    
5138
		$realif = get_real_interface($ifconf, "inet6");
5139
		if (isvalidpid("{$g['varrun_path']}/rtsold_{$realif}.pid")) {
5140
			killbypid("{$g['varrun_path']}/rtsold_{$realif}.pid");
5141
			log_error("Killing running rtsold process");
5142
			sleep(2);
5143
		}
5144

    
5145
		if (file_exists("{$g['tmp_path']}/dhcp6c_ifs")) {
5146
			$dhcp6crealifs_run = unserialize_data(file_get_contents("{$g['tmp_path']}/dhcp6c_ifs"), []);
5147
		} else {
5148
			$dhcp6crealifs_run = array();
5149
		}
5150

    
5151
		if (($dhcp6crealifs != $dhcp6crealifs_run) || $destroy) {
5152
			kill_dhcp6client_process(false);
5153
			run_dhcp6client_process($dhcp6crealifs, $debugOption, $noreleaseOption);
5154
			file_put_contents("{$g['tmp_path']}/dhcp6c_ifs", serialize($dhcp6crealifs));
5155
			$dhcp6c_restarted = true;
5156
			if ($destroy) {
5157
				$track6 = link_interface_to_track6($ifconf);
5158
				if (is_array($track6) && !empty($track6)) {
5159
					/* remove stale track interfaces IP */
5160
					foreach (array_keys($track6) as $tr6if) {
5161
						interface_reconfigure($tr6if, true);
5162
					}
5163
				}
5164
			}
5165
		}
5166

    
5167
		if (isset($ifcfg['dhcp6withoutra']) && !$dhcp6c_restarted) {
5168
			/*
5169
			 * Start dhcp6c here if we don't want to wait for ra - calls
5170
			 * separate function
5171
			 *
5172
			 * In this mode dhcp6c launches rtsold via its script. RTSOLD
5173
			 * will then run the configure on receipt of the RA.
5174
			 *
5175
			 * Already started. interface_dhcpv6_configure() appears to get
5176
			 * called multiple times.
5177
			 *
5178
			 * Taking the interface down or releasing will kill the client.
5179
			 */
5180

    
5181
			/*
5182
			 * If the interface is being brought up, wait for the
5183
			 * interface to configure accept RA before launching.
5184
			 * Otherwise it is not ready to accept and will fail.
5185
			 */
5186
			sleep(3);
5187
			if (file_exists("/tmp/dhcp6c_lock")) {
5188
				reset_dhcp6client_process();
5189
			}
5190
		} elseif (!$destroy) {
5191
			/*
5192
			 * Fire up rtsold for IPv6 RAs, this backgrounds immediately
5193
			 * ( it does not background, it exits! ) It will launch dhcp6c
5194
			 * if dhcpwihtoutra is not set
5195
			 */
5196
			log_error("Starting rtsold process on {$ifconf}({$realif})");
5197
			sleep(2);
5198
			mwexec("/usr/sbin/rtsold -1 " .
5199
			    "-p {$g['varrun_path']}/rtsold_{$realif}.pid " .
5200
			    "-A {$g['varetc_path']}/rtsold_{$realif}_script.sh " .
5201
			    $realif);
5202
		}
5203
	} else {
5204
		kill_dhcp6client_process(true);
5205
		unlink_if_exists("{$g['varetc_path']}/dhcp6c.conf");
5206
		unlink_if_exists("{$g['tmp_path']}/dhcp6c_ifs");
5207
	}
5208

    
5209
	/*
5210
	 * NOTE: will be called from rtsold invoked script
5211
	 * link_interface_to_track6($interface, "update");
5212
	 */
5213

    
5214
	return 0;
5215
}
5216

    
5217
function DHCP6_Config_File_Advanced($interface, $wancfg, $wanif) {
5218
	global $g;
5219

    
5220
	$send_options = "";
5221
	if ($wancfg['adv_dhcp6_interface_statement_send_options'] != '') {
5222
		$options = DHCP_Config_Option_Split($wancfg['adv_dhcp6_interface_statement_send_options']);
5223
		foreach ($options as $option) {
5224
			$send_options .= "\tsend " . trim($option) . ";\n";
5225
		}
5226
	}
5227

    
5228
	$request_options = "";
5229
	if ($wancfg['adv_dhcp6_interface_statement_request_options'] != '') {
5230
		$options = DHCP_Config_Option_Split($wancfg['adv_dhcp6_interface_statement_request_options']);
5231
		foreach ($options as $option) {
5232
			$request_options .= "\trequest " . trim($option) . ";\n";
5233
		}
5234
	}
5235

    
5236
	$information_only = "";
5237
	if ($wancfg['adv_dhcp6_interface_statement_information_only_enable'] != '') {
5238
		$information_only = "\tinformation-only;\n";
5239
	}
5240

    
5241
	if (isset($wancfg['dhcp6withoutra'])) {
5242
		$script = "\tscript \"{$g['varetc_path']}/dhcp6c_{$interface}_dhcp6withoutra_script.sh\";\n";
5243
	} else {
5244
		$script = "\tscript \"{$g['varetc_path']}/dhcp6c_{$interface}_script.sh\";\n";
5245
	}
5246
	if ($wancfg['adv_dhcp6_interface_statement_script'] != '') {
5247
		$script = "\tscript \"{$wancfg['adv_dhcp6_interface_statement_script']}\";\n";
5248
	}
5249

    
5250
	$interface_statement  = "interface";
5251
	$interface_statement .= " {$wanif}";
5252
	$interface_statement .= " {\n";
5253
	$interface_statement .= "$send_options";
5254
	$interface_statement .= "$request_options";
5255
	$interface_statement .= "$information_only";
5256
	$interface_statement .= "$script";
5257
	$interface_statement .= "};\n";
5258

    
5259
	$id_assoc_statement_address = "";
5260
	if ($wancfg['adv_dhcp6_id_assoc_statement_address_enable'] != '') {
5261
		$id_assoc_statement_address .= "id-assoc";
5262
		$id_assoc_statement_address .= " na";
5263
		if (is_numeric($wancfg['adv_dhcp6_id_assoc_statement_address_id'])) {
5264
			$id_assoc_statement_address .= " {$wancfg['adv_dhcp6_id_assoc_statement_address_id']}";
5265
		}
5266
		$id_assoc_statement_address .= " { ";
5267

    
5268
		if (($wancfg['adv_dhcp6_id_assoc_statement_address'] != '') &&
5269
		    (is_numeric($wancfg['adv_dhcp6_id_assoc_statement_address_pltime']) ||
5270
		    ($wancfg['adv_dhcp6_id_assoc_statement_address_pltime'] == 'infinity'))) {
5271
			$id_assoc_statement_address .= "\n\taddress";
5272
			$id_assoc_statement_address .= " {$wancfg['adv_dhcp6_id_assoc_statement_address']}";
5273
			$id_assoc_statement_address .= " {$wancfg['adv_dhcp6_id_assoc_statement_address_pltime']}";
5274
			if ((is_numeric($wancfg['adv_dhcp6_id_assoc_statement_address_vltime'])) ||
5275
			    ($wancfg['adv_dhcp6_id_assoc_statement_address_vltime'] == 'infinity')) {
5276
				$id_assoc_statement_address .= " {$wancfg['adv_dhcp6_id_assoc_statement_address_vltime']}";
5277
			}
5278
			$id_assoc_statement_address .= ";\n";
5279
		}
5280

    
5281
		$id_assoc_statement_address .= "};\n";
5282
	}
5283

    
5284
	$id_assoc_statement_prefix = "";
5285
	if ($wancfg['adv_dhcp6_id_assoc_statement_prefix_enable'] != '') {
5286
		$id_assoc_statement_prefix .= "id-assoc";
5287
		$id_assoc_statement_prefix .= " pd";
5288
		if (is_numeric($wancfg['adv_dhcp6_id_assoc_statement_prefix_id'])) {
5289
			$id_assoc_statement_prefix .= " {$wancfg['adv_dhcp6_id_assoc_statement_prefix_id']}";
5290
		}
5291
		$id_assoc_statement_prefix .= " { ";
5292

    
5293
		if (($wancfg['adv_dhcp6_id_assoc_statement_prefix'] != '') &&
5294
		    (is_numeric($wancfg['adv_dhcp6_id_assoc_statement_prefix_pltime']) ||
5295
		    ($wancfg['adv_dhcp6_id_assoc_statement_prefix_pltime'] == 'infinity'))) {
5296
			$id_assoc_statement_prefix .= "\n\tprefix";
5297
			$id_assoc_statement_prefix .= " {$wancfg['adv_dhcp6_id_assoc_statement_prefix']}";
5298
			$id_assoc_statement_prefix .= " {$wancfg['adv_dhcp6_id_assoc_statement_prefix_pltime']}";
5299
			if ((is_numeric($wancfg['adv_dhcp6_id_assoc_statement_prefix_vltime'])) ||
5300
			    ($wancfg['adv_dhcp6_id_assoc_statement_prefix_vltime'] == 'infinity')) {
5301
				$id_assoc_statement_prefix .= " {$wancfg['adv_dhcp6_id_assoc_statement_prefix_vltime']}";
5302
			}
5303
			$id_assoc_statement_prefix .= ";";
5304
		}
5305

    
5306
		$realif = get_real_interface($wancfg['adv_dhcp6_prefix_selected_interface']);
5307
		if (is_numeric($wancfg['adv_dhcp6_prefix_interface_statement_sla_id'])) {
5308
			$id_assoc_statement_prefix .= "\n\tprefix-interface";
5309
			$id_assoc_statement_prefix .= " {$realif}";
5310
			$id_assoc_statement_prefix .= " {\n";
5311
			$id_assoc_statement_prefix .= "\t\tsla-id {$wancfg['adv_dhcp6_prefix_interface_statement_sla_id']};\n";
5312
			if (($wancfg['adv_dhcp6_prefix_interface_statement_sla_len'] >= 0) &&
5313
			    ($wancfg['adv_dhcp6_prefix_interface_statement_sla_len'] <= 128)) {
5314
				$id_assoc_statement_prefix .= "\t\tsla-len {$wancfg['adv_dhcp6_prefix_interface_statement_sla_len']};\n";
5315
			}
5316
			$id_assoc_statement_prefix .= "\t};";
5317
		}
5318

    
5319
		if (($wancfg['adv_dhcp6_id_assoc_statement_prefix'] != '') ||
5320
		    (is_numeric($wancfg['adv_dhcp6_prefix_interface_statement_sla_id']))) {
5321
			$id_assoc_statement_prefix .= "\n";
5322
		}
5323

    
5324
		$id_assoc_statement_prefix .= "};\n";
5325
	}
5326

    
5327
	$authentication_statement = "";
5328
	if (($wancfg['adv_dhcp6_authentication_statement_authname'] != '') &&
5329
	    ($wancfg['adv_dhcp6_authentication_statement_protocol'] == 'delayed')) {
5330
		$authentication_statement .= "authentication";
5331
		$authentication_statement .= " {$wancfg['adv_dhcp6_authentication_statement_authname']}";
5332
		$authentication_statement .= " {\n";
5333
		$authentication_statement .= "\tprotocol {$wancfg['adv_dhcp6_authentication_statement_protocol']};\n";
5334
		if (preg_match("/(hmac(-)?md5)||(HMAC(-)?MD5)/", $wancfg['adv_dhcp6_authentication_statement_algorithm'])) {
5335
			$authentication_statement .= "\talgorithm {$wancfg['adv_dhcp6_authentication_statement_algorithm']};\n";
5336
		}
5337
		if ($wancfg['adv_dhcp6_authentication_statement_rdm'] == 'monocounter') {
5338
			$authentication_statement .= "\trdm {$wancfg['adv_dhcp6_authentication_statement_rdm']};\n";
5339
		}
5340
		$authentication_statement .= "};\n";
5341
	}
5342

    
5343
	$key_info_statement = "";
5344
	if (($wancfg['adv_dhcp6_key_info_statement_keyname'] != '') &&
5345
	    ($wancfg['adv_dhcp6_key_info_statement_realm'] != '') &&
5346
	    (is_numeric($wancfg['adv_dhcp6_key_info_statement_keyid'])) &&
5347
	    ($wancfg['adv_dhcp6_key_info_statement_secret'] != '')) {
5348
		$key_info_statement .= "keyinfo";
5349
		$key_info_statement .= " {$wancfg['adv_dhcp6_key_info_statement_keyname']}";
5350
		$key_info_statement .= " {\n";
5351
		$key_info_statement .= "\trealm \"{$wancfg['adv_dhcp6_key_info_statement_realm']}\";\n";
5352
		$key_info_statement .= "\tkeyid {$wancfg['adv_dhcp6_key_info_statement_keyid']};\n";
5353
		$key_info_statement .= "\tsecret \"{$wancfg['adv_dhcp6_key_info_statement_secret']}\";\n";
5354
		if (preg_match("/((([0-9]{4}-)?[0-9]{2}[0-9]{2} )?[0-9]{2}:[0-9]{2})||(forever)/", $wancfg['adv_dhcp6_key_info_statement_expire'])) {
5355
			$key_info_statement .= "\texpire \"{$wancfg['adv_dhcp6_key_info_statement_expire']}\";\n";
5356
		}
5357
		$key_info_statement .= "};\n";
5358
	}
5359

    
5360
	$dhcp6cconf  = $interface_statement;
5361
	$dhcp6cconf .= $id_assoc_statement_address;
5362
	$dhcp6cconf .= $id_assoc_statement_prefix;
5363
	$dhcp6cconf .= $authentication_statement;
5364
	$dhcp6cconf .= $key_info_statement;
5365

    
5366
	$dhcp6cconf = DHCP6_Config_File_Substitutions($wancfg, $wanif, $dhcp6cconf);
5367

    
5368
	return $dhcp6cconf;
5369
}
5370

    
5371

    
5372
function DHCP6_Config_File_Override($wancfg, $wanif) {
5373

    
5374
	$dhcp6cconf = @file_get_contents($wancfg['adv_dhcp6_config_file_override_path']);
5375

    
5376
	if ($dhcp6cconf === false) {
5377
		log_error(sprintf(gettext('Error: cannot open %s in DHCP6_Config_File_Override() for reading.'), $wancfg['adv_dhcp6_config_file_override_path']));
5378
		return '';
5379
	} else {
5380
		return DHCP6_Config_File_Substitutions($wancfg, $wanif, $dhcp6cconf);;
5381
	}
5382
}
5383

    
5384

    
5385
function DHCP6_Config_File_Substitutions($wancfg, $wanif, $dhcp6cconf) {
5386

    
5387
	$dhcp6cconf = DHCP_Config_File_Substitutions($wancfg, $wanif, $dhcp6cconf);
5388

    
5389
	return $dhcp6cconf;
5390
}
5391

    
5392

    
5393
function interface_dhcp_configure($interface) {
5394
	global $g, $vlanprio_values;
5395

    
5396
	$ifcfg = config_get_path("interfaces/{$interface}");
5397
	if (empty($ifcfg)) {
5398
		$ifcfg = array();
5399
	}
5400

    
5401
	$dhclientconf_vlantag = "";
5402
	if (isset($ifcfg['dhcpvlanenable']) && isset($ifcfg['dhcpcvpt'])) {
5403
		$dhclientconf_vlantag = "vlan-pcp {$vlanprio_values[$ifcfg['dhcpcvpt']]};\n";
5404
	}
5405

    
5406
	/* generate dhclient_wan.conf */
5407
	$fd = fopen("{$g['varetc_path']}/dhclient_{$interface}.conf", "w");
5408
	if (!$fd) {
5409
		log_error(sprintf(gettext("Error: cannot open dhclient_%s.conf in interface_dhcp_configure() for writing."), $interface));
5410
		return 1;
5411
	}
5412

    
5413
	if ($ifcfg['dhcphostname']) {
5414
		$dhclientconf_hostname = "send dhcp-client-identifier \"{$ifcfg['dhcphostname']}\";\n";
5415
		$dhclientconf_hostname .= "\tsend host-name \"{$ifcfg['dhcphostname']}\";\n";
5416
	} else {
5417
		$dhclientconf_hostname = "";
5418
	}
5419

    
5420
	$realif = get_real_interface($interface);
5421
	if (empty($realif)) {
5422
		log_error(sprintf(gettext("Invalid interface \"%s\" in interface_dhcp_configure()"), $interface));
5423
		return 0;
5424
	}
5425
	$dhclientconf = "";
5426

    
5427
	$dhclientconf .= <<<EOD
5428
interface "{$realif}" {
5429
	supersede interface-mtu 0;
5430
	timeout 60;
5431
	retry 15;
5432
	select-timeout 0;
5433
	initial-interval 1;
5434
	{$dhclientconf_vlantag}
5435
	{$dhclientconf_hostname}
5436
	script "/usr/local/sbin/pfSense-dhclient-script";
5437
EOD;
5438

    
5439
	if (validate_ipv4_list($ifcfg['dhcprejectfrom'])) {
5440
		$dhclientconf .= <<<EOD
5441

    
5442
	reject {$ifcfg['dhcprejectfrom']};
5443
EOD;
5444
	}
5445
	$dhclientconf .= <<<EOD
5446

    
5447
}
5448

    
5449
EOD;
5450

    
5451
	// DHCP Config File Advanced
5452
	if ($ifcfg['adv_dhcp_config_advanced']) {
5453
		$dhclientconf = DHCP_Config_File_Advanced($ifcfg, $realif);
5454
	}
5455

    
5456
	if (is_ipaddr($ifcfg['alias-address'])) {
5457
		$subnetmask = gen_subnet_mask($ifcfg['alias-subnet']);
5458
		$dhclientconf .= <<<EOD
5459
alias {
5460
	interface "{$realif}";
5461
	fixed-address {$ifcfg['alias-address']};
5462
	option subnet-mask {$subnetmask};
5463
}
5464

    
5465
EOD;
5466
	}
5467

    
5468
	// DHCP Config File Override
5469
	if ($ifcfg['adv_dhcp_config_file_override']) {
5470
		$dhclientconf = DHCP_Config_File_Override($ifcfg, $realif);
5471
	}
5472

    
5473
	fwrite($fd, $dhclientconf);
5474
	fclose($fd);
5475

    
5476
	/* bring wan interface up before starting dhclient */
5477
	if ($realif) {
5478
		interfaces_bring_up($realif);
5479
	}
5480

    
5481
	/* Make sure dhclient is not running */
5482
	kill_dhclient_process($realif);
5483

    
5484
	/* fire up dhclient */
5485
	mwexec("/sbin/dhclient -c {$g['varetc_path']}/dhclient_{$interface}.conf -p {$g['varrun_path']}/dhclient.{$realif}.pid {$realif} > {$g['tmp_path']}/{$realif}_output 2> {$g['tmp_path']}/{$realif}_error_output");
5486

    
5487
	return 0;
5488
}
5489

    
5490
function DHCP_Config_File_Advanced($ifcfg, $realif) {
5491

    
5492
	$hostname = "";
5493
	if ($ifcfg['dhcphostname'] != '') {
5494
		$hostname = "\tsend host-name \"{$ifcfg['dhcphostname']}\";\n";
5495
	}
5496

    
5497
	/* DHCP Protocol Timings */
5498
	$protocol_timings = array ('adv_dhcp_pt_timeout' => "timeout", 'adv_dhcp_pt_retry' => "retry", 'adv_dhcp_pt_select_timeout' => "select-timeout", 'adv_dhcp_pt_reboot' => "reboot", 'adv_dhcp_pt_backoff_cutoff' => "backoff-cutoff", 'adv_dhcp_pt_initial_interval' => "initial-interval");
5499
	foreach ($protocol_timings as $Protocol_Timing => $PT_Name) {
5500
		$pt_variable = "{$Protocol_Timing}";
5501
		${$pt_variable} = "";
5502
		if ($ifcfg[$Protocol_Timing] != "") {
5503
			${$pt_variable} = "{$PT_Name} {$ifcfg[$Protocol_Timing]};\n";
5504
		}
5505
	}
5506

    
5507
	$send_options = "";
5508
	if ($ifcfg['adv_dhcp_send_options'] != '') {
5509
		$options = DHCP_Config_Option_Split($ifcfg['adv_dhcp_send_options']);
5510
		foreach ($options as $option) {
5511
			$send_options .= "\tsend " . trim($option) . ";\n";
5512
		}
5513
	}
5514

    
5515
	$request_options = "";
5516
	if ($ifcfg['adv_dhcp_request_options'] != '') {
5517
		$request_options = "\trequest {$ifcfg['adv_dhcp_request_options']};\n";
5518
	}
5519

    
5520
	$required_options = "";
5521
	if ($ifcfg['adv_dhcp_required_options'] != '') {
5522
		$required_options = "\trequire {$ifcfg['adv_dhcp_required_options']};\n";
5523
	}
5524

    
5525
	$option_modifiers = "";
5526
	if ($ifcfg['adv_dhcp_option_modifiers'] != '') {
5527
		$modifiers = DHCP_Config_Option_Split($ifcfg['adv_dhcp_option_modifiers']);
5528
		foreach ($modifiers as $modifier) {
5529
			$option_modifiers .= "\t" . trim($modifier) . ";\n";
5530
		}
5531
	}
5532

    
5533
	$dhclientconf  = "interface \"{$realif}\" {\n";
5534
	$dhclientconf .= "\n";
5535
	$dhclientconf .= "\tsupersede interface-mtu 0;\n";
5536
	$dhclientconf .= "# DHCP Protocol Timing Values\n";
5537
	$dhclientconf .= "{$adv_dhcp_pt_timeout}";
5538
	$dhclientconf .= "{$adv_dhcp_pt_retry}";
5539
	$dhclientconf .= "{$adv_dhcp_pt_select_timeout}";
5540
	$dhclientconf .= "{$adv_dhcp_pt_reboot}";
5541
	$dhclientconf .= "{$adv_dhcp_pt_backoff_cutoff}";
5542
	$dhclientconf .= "{$adv_dhcp_pt_initial_interval}";
5543
	$dhclientconf .= "\n";
5544
	$dhclientconf .= "# DHCP Protocol Options\n";
5545
	$dhclientconf .= "{$hostname}";
5546
	$dhclientconf .= "{$send_options}";
5547
	$dhclientconf .= "{$request_options}";
5548
	$dhclientconf .= "{$required_options}";
5549
	$dhclientconf .= "{$option_modifiers}";
5550
	$dhclientconf .= "\n";
5551
	if (is_ipaddrv4($ifcfg['dhcprejectfrom'])) {
5552
		$dhclientconf .= "reject {$ifcfg['dhcprejectfrom']};\n";
5553
	}
5554
	$dhclientconf .= "\tscript \"/usr/local/sbin/pfSense-dhclient-script\";\n";
5555
	$dhclientconf .= "}\n";
5556

    
5557
	$dhclientconf = DHCP_Config_File_Substitutions($ifcfg, $realif, $dhclientconf);
5558

    
5559
	return $dhclientconf;
5560
}
5561

    
5562
function DHCP_Config_Option_Split($option_string) {
5563
	preg_match_all('/[^",]*(?:"[^"]*"[^",]*)+(?:"[^",]*)?|[^,]+/m', $option_string, $matches, PREG_PATTERN_ORDER);
5564
	return $matches ? $matches[0] : [];
5565
}
5566

    
5567
function DHCP_Config_File_Override($ifcfg, $realif) {
5568

    
5569
	$dhclientconf = @file_get_contents($ifcfg['adv_dhcp_config_file_override_path']);
5570

    
5571
	if ($dhclientconf === false) {
5572
		log_error(sprintf(gettext("Error: cannot open %s in DHCP_Config_File_Override() for reading."), $ifcfg['adv_dhcp_config_file_override_path']));
5573
		return '';
5574
	} else {
5575
		return DHCP_Config_File_Substitutions($ifcfg, $realif, $dhclientconf);
5576
	}
5577
}
5578

    
5579

    
5580
function DHCP_Config_File_Substitutions($ifcfg, $realif, $dhclientconf) {
5581

    
5582
	/* Apply Interface Substitutions */
5583
	$dhclientconf = str_replace("{interface}", "{$realif}", $dhclientconf);
5584

    
5585
	/* Apply Hostname Substitutions */
5586
	$dhclientconf = str_replace("{hostname}", $ifcfg['dhcphostname'], $dhclientconf);
5587

    
5588
	/* Arrays of MAC Address Types, Cases, Delimiters */
5589
	/* ASCII or HEX, Upper or Lower Case, Various Delimiters (none, space, colon, hyphen, period) */
5590
	$various_mac_types      = array("mac_addr_ascii", "mac_addr_hex");
5591
	$various_mac_cases      = array("U", "L");
5592
	$various_mac_delimiters = array("", " ", ":", "-", ".");
5593

    
5594
	/* Apply MAC Address Substitutions */
5595
	foreach ($various_mac_types as $various_mac_type) {
5596
		foreach ($various_mac_cases as $various_mac_case) {
5597
			foreach ($various_mac_delimiters as $various_mac_delimiter) {
5598

    
5599
				$res = stripos($dhclientconf, $various_mac_type . $various_mac_case . $various_mac_delimiter);
5600
				if ($res !== false) {
5601

    
5602
					/* Get MAC Address as ASCII String With Colon (:) delimiters */
5603
					if ("$various_mac_case" == "U") {
5604
						$dhcpclientconf_mac = strtoupper(get_interface_mac($realif));
5605
					}
5606
					if ("$various_mac_case" == "L") {
5607
						$dhcpclientconf_mac = strtolower(get_interface_mac($realif));
5608
					}
5609

    
5610
					if ("$various_mac_type" == "mac_addr_hex") {
5611
						/* Convert MAC ascii string to HEX with colon (:) delimiters. */
5612
						$dhcpclientconf_mac = str_replace(":", "", $dhcpclientconf_mac);
5613
						$dhcpclientconf_mac_hex = "";
5614
						$delimiter = "";
5615
						for ($i = 0; $i < strlen($dhcpclientconf_mac); $i++) {
5616
							$dhcpclientconf_mac_hex .= $delimiter. bin2hex($dhcpclientconf_mac[$i]);
5617
							$delimiter = ":";
5618
						}
5619
						$dhcpclientconf_mac = $dhcpclientconf_mac_hex;
5620
					}
5621

    
5622
					/* MAC Address Delimiter Substitutions */
5623
					$dhcpclientconf_mac = str_replace(":", $various_mac_delimiter, $dhcpclientconf_mac);
5624

    
5625
					/* Apply MAC Address Substitutions */
5626
					$dhclientconf = str_replace("{" . $various_mac_type . $various_mac_case . $various_mac_delimiter . "}", $dhcpclientconf_mac, $dhclientconf);
5627
				}
5628
			}
5629
		}
5630
	}
5631

    
5632
	return $dhclientconf;
5633
}
5634

    
5635
function interfaces_group_setup() {
5636
	foreach (config_get_path('ifgroups/ifgroupentry', []) as $groupar) {
5637
		interface_group_setup($groupar);
5638
	}
5639

    
5640
	return;
5641
}
5642

    
5643
function interface_group_setup(&$groupname /* The parameter is an array */) {
5644
	if (!is_array($groupname) || !isset($groupname['members']) || empty(trim($groupname['members']))) {
5645
		return;
5646
	}
5647
	$members = explode(" ", $groupname['members']);
5648
	foreach ($members as $ifs) {
5649
		$realif = get_real_interface($ifs);
5650
		if ($realif && does_interface_exist($realif)) {
5651
			mwexec("/sbin/ifconfig {$realif} group {$groupname['ifname']}");
5652
		}
5653
	}
5654

    
5655
	return;
5656
}
5657

    
5658
function is_interface_group($if) {
5659
	foreach (config_get_path('ifgroups/ifgroupentry', []) as $groupentry) {
5660
		if ($groupentry['ifname'] === $if) {
5661
			return true;
5662
		}
5663
	}
5664

    
5665
	return false;
5666
}
5667

    
5668
function interface_group_add_member($interface, $groupname) {
5669
	$interface = get_real_interface($interface);
5670
	if (does_interface_exist($interface)) {
5671
		mwexec("/sbin/ifconfig {$interface} group " . escapeshellarg($groupname), true);
5672
	}
5673
}
5674

    
5675
/* COMPAT Function */
5676
function convert_friendly_interface_to_real_interface_name($interface) {
5677
	return get_real_interface($interface);
5678
}
5679

    
5680
/* COMPAT Function */
5681
function get_real_wan_interface($interface = "wan") {
5682
	return get_real_interface($interface);
5683
}
5684

    
5685
/* COMPAT Function */
5686
function get_current_wan_address($interface = "wan") {
5687
	return get_interface_ip($interface);
5688
}
5689

    
5690
/*
5691
 * convert_real_interface_to_friendly_interface_name($interface): convert fxp0 -> wan, etc.
5692
 */
5693
function convert_real_interface_to_friendly_interface_name($interface = "wan", $checkparent = false) {
5694

    
5695
	/* XXX: For speed reasons reference directly the interface array */
5696
	$ifdescrs = config_get_path('interfaces', []);
5697
	//$ifdescrs = get_configured_interface_list(true);
5698

    
5699
	foreach ($ifdescrs as $if => $ifname) {
5700
		if ($if == $interface || $ifname['if'] == $interface) {
5701
			return $if;
5702
		}
5703

    
5704
		if (get_real_interface($if) == $interface) {
5705
			return $if;
5706
		}
5707

    
5708
		if ($checkparent == false) {
5709
			continue;
5710
		}
5711

    
5712
		$int = get_parent_interface($if, true);
5713
		if (is_array($int)) {
5714
			foreach ($int as $iface) {
5715
				if ($iface == $interface) {
5716
					return $if;
5717
				}
5718
			}
5719
		}
5720
	}
5721

    
5722
	if ($interface == "enc0") {
5723
		return 'IPsec';
5724
	}
5725
}
5726

    
5727
/* attempt to resolve interface to friendly descr */
5728
function convert_friendly_interface_to_friendly_descr($interface) {
5729

    
5730
	$iface = config_get_path("interfaces/{$interface}");
5731
	switch ($interface) {
5732
		case "l2tp":
5733
			$ifdesc = "L2TP";
5734
			break;
5735
		case "pptp":
5736
			$ifdesc = "PPTP";
5737
			break;
5738
		case "pppoe":
5739
			$ifdesc = "PPPoE";
5740
			break;
5741
		case "openvpn":
5742
			$ifdesc = "OpenVPN";
5743
			break;
5744
		case "lo0":
5745
			$ifdesc = "Loopback";
5746
			break;
5747
		case "enc0":
5748
		case "ipsec":
5749
		case "IPsec":
5750
			$ifdesc = "IPsec";
5751
			break;
5752
		default:
5753
			if ($iface) {
5754
				if (empty($iface['descr'])) {
5755
					$ifdesc = strtoupper($interface);
5756
				} else {
5757
					$ifdesc = strtoupper($iface['descr']);
5758
				}
5759
				break;
5760
			} elseif (substr($interface, 0, 4) == '_vip') {
5761
				foreach (config_get_path('virtualip/vip', []) as $vip) {
5762
					if (($vip['mode'] == "carp") || ($vip['mode'] == "ipalias")) {
5763
						if ($interface == "_vip{$vip['uniqid']}") {
5764
							$descr = $vip['subnet'];
5765
							if (!empty($vip['vhid'])) {
5766
								$descr .= " (vhid {$vip['vhid']})";
5767
							}
5768
							if (!empty($vip['descr'])) {
5769
								$descr .= " - " .$vip['descr'];
5770
							}
5771
							return $descr;
5772
						}
5773
					}
5774
				}
5775
			} elseif (substr($interface, 0, 5) == '_lloc') {
5776
				return get_interface_linklocal($interface);
5777
			} else {
5778
				foreach (config_get_path('ifgroups/ifgroupentry', []) as $ifgen) {
5779
					if ($ifgen['ifname'] === $interface) {
5780
						return $ifgen['ifname'];
5781
					}
5782
				}
5783

    
5784
				/* if list */
5785
				$ifdescrs = get_configured_interface_with_descr(true);
5786
				foreach ($ifdescrs as $if => $ifname) {
5787
					if ($if == $interface || $ifname == $interface) {
5788
						return $ifname;
5789
					}
5790
				}
5791
			}
5792
			break;
5793
	}
5794

    
5795
	return $ifdesc;
5796
}
5797

    
5798
function convert_real_interface_to_friendly_descr($interface) {
5799

    
5800
	$ifdesc = convert_real_interface_to_friendly_interface_name("{$interface}");
5801

    
5802
	if (!empty($ifdesc)) {
5803
		return convert_friendly_interface_to_friendly_descr($ifdesc);
5804
	}
5805

    
5806
	return $interface;
5807
}
5808

    
5809
/*
5810
 *  get_parent_interface($interface):
5811
 *			--returns the (real or virtual) parent interface(s) array for a given interface friendly name (i.e. wan)
5812
 *				or virtual interface (i.e. vlan)
5813
 *				(We need array because MLPPP and bridge interfaces have more than one parent.)
5814
 *			-- returns $interface passed in if $interface parent is not found
5815
 *			-- returns empty array if an invalid interface is passed
5816
 *	(Only handles ppps and vlans now.)
5817
 */
5818
function get_parent_interface($interface, $avoidrecurse = false) {
5819
	$parents = array();
5820
	//Check that we got a valid interface passed
5821
	$realif = get_real_interface($interface);
5822
	if ($realif == NULL) {
5823
		return $parents;
5824
	}
5825

    
5826
	// If we got a real interface, find it's friendly assigned name
5827
	if ($interface == $realif && $avoidrecurse == false) {
5828
		$interface = convert_real_interface_to_friendly_interface_name($interface);
5829
	}
5830
	if (!empty($interface)) {
5831
		$iface = config_get_path("interfaces/{$interface}");
5832
	}
5833
	if ($iface) {
5834
		$ifcfg = $iface;
5835
		switch ($ifcfg['ipaddr']) {
5836
			case "ppp":
5837
			case "pppoe":
5838
			case "pptp":
5839
			case "l2tp":
5840
				if (empty($parents)) {
5841
					foreach (config_get_path('ppps/ppp', []) as $ppp) {
5842
						// skip modems, e.g. /dev/cuaZ99.0
5843
						if ($ppp['type'] == 'ppp') {
5844
							continue;
5845
						}
5846
						if ($ifcfg['if'] == $ppp['if']) {
5847
							$ports = array_filter(explode(',', $ppp['ports']));
5848
							foreach ($ports as $pid => $parent_if) {
5849
								$parents[$pid] = get_real_interface($parent_if);
5850
							}
5851
							break;
5852
						}
5853
					}
5854
				}
5855
				break;
5856
			case "dhcp":
5857
			case "static":
5858
			default:
5859
				// Handle _vlans
5860
				$vlan = interface_is_vlan($ifcfg['if']);
5861
				if ($vlan != NULL) {
5862
					$parents[0] = $vlan['if'];
5863
				}
5864
				break;
5865
		}
5866
	}
5867

    
5868
	if (empty($parents)) {
5869
		// Handle _vlans not assigned to an interface
5870
		$vlan = interface_is_vlan($realif);
5871
		if ($vlan != NULL) {
5872
			$parents[0] = $vlan['if'];
5873
		}
5874
	}
5875

    
5876
	if (empty($parents)) {
5877
		/* Handle LAGGs. */
5878
		$lagg = interface_is_type($realif, 'lagg');
5879
		if ($lagg != NULL && isset($lagg['members'])) {
5880
			$parents = array_filter(explode(",", $lagg['members']));
5881
		}
5882
	}
5883

    
5884
	if (empty($parents)) {
5885
		$parents[0] = $realif;
5886
	}
5887

    
5888
	return $parents;
5889
}
5890

    
5891
/*
5892
 *  get_parent_physical_interface($interface):
5893
 *   - returns an array of parent interface(s) for a given interface friendly name (e.g. wan)
5894
 *  differs from get_parent_interface in that it traverses to find the physical NICs on lagg
5895
 */
5896
function get_parent_physical_interface($interface) {
5897

    
5898
	$realif = get_parent_interface($interface);
5899

    
5900
	if (substr($realif[0], 0, 4) == "lagg") {
5901
		foreach (config_get_path('laggs/lagg', []) as $lagg) {
5902
			if ($realif[0] == $lagg['laggif']) {
5903
				return explode(",", $lagg['members']);
5904
			}
5905
		}
5906
	} else {
5907
		return $realif;
5908
	}
5909
}
5910

    
5911
function interface_is_wireless_clone($wlif) {
5912
	if (!stristr($wlif, "_wlan")) {
5913
		return false;
5914
	} else {
5915
		return true;
5916
	}
5917
}
5918

    
5919
function interface_get_wireless_base($wlif) {
5920
	if (!stristr($wlif, "_wlan")) {
5921
		return $wlif;
5922
	} else {
5923
		return substr($wlif, 0, stripos($wlif, "_wlan"));
5924
	}
5925
}
5926

    
5927
function interface_get_wireless_clone($wlif) {
5928
	if (!stristr($wlif, "_wlan")) {
5929
		return $wlif . "_wlan0";
5930
	} else {
5931
		return $wlif;
5932
	}
5933
}
5934

    
5935
function interface_list_wireless() {
5936
	$portlist = explode(" ", get_single_sysctl("net.wlan.devices"));
5937

    
5938
	$result = array();
5939
	foreach ($portlist as $port) {
5940
		if (preg_match("/^([^0-9]+)([0-9]+)/", $port, $matches) != 1) {
5941
			continue;
5942
		}
5943

    
5944
		$desc = $port . " ( " . get_single_sysctl(
5945
		    "dev.{$matches[1]}.{$matches[2]}.%desc") . " )";
5946

    
5947
		$result[] = array(
5948
		    "if" => $port,
5949
		    "descr" => $desc
5950
		);
5951
	}
5952

    
5953
	return $result;
5954
}
5955

    
5956
function get_real_interface($interface = "wan", $family = "all", $realv6iface = false, $flush = false) {
5957
	global $g;
5958

    
5959
	if (empty($interface)) {
5960
		return null;
5961
	}
5962

    
5963
	$wanif = NULL;
5964

    
5965
	switch ($interface) {
5966
		case "l2tp":
5967
			$wanif = "l2tp";
5968
			break;
5969
		case "pptp":
5970
			$wanif = "pptp";
5971
			break;
5972
		case "pppoe":
5973
			$wanif = "pppoe";
5974
			break;
5975
		case "openvpn":
5976
			$wanif = "openvpn";
5977
			break;
5978
		case "IPsec":
5979
		case "ipsec":
5980
		case "enc0":
5981
			$wanif = "enc0";
5982
			break;
5983
		case "ppp":
5984
			$wanif = "ppp";
5985
			break;
5986
		default:
5987
			if (substr($interface, 0, 4) == '_vip') {
5988
				$wanif = get_configured_vip_interface($interface);
5989
				if (!empty($wanif)) {
5990
					$wanif = get_real_interface($wanif);
5991
				}
5992
				break;
5993
			} elseif (substr($interface, 0, 5) == '_lloc') {
5994
				$interface = substr($interface, 5);
5995
			} elseif (interface_is_vlan($interface) != NULL ||
5996
			    does_interface_exist($interface, $flush)) {
5997
				/*
5998
				 * If a real interface was already passed simply
5999
				 * pass the real interface back.  This encourages
6000
				 * the usage of this function in more cases so that
6001
				 * we can combine logic for more flexibility.
6002
				 */
6003
				$wanif = $interface;
6004
				break;
6005
			}
6006

    
6007
			$cfg = config_get_path("interfaces/{$interface}");
6008
			if (empty($cfg))
6009
				break;
6010

    
6011
			if ($family == "inet6") {
6012
				switch ($cfg['ipaddrv6']) {
6013
					case "6rd":
6014
					case "6to4":
6015
						$wanif = "{$interface}_stf";
6016
						break;
6017
					case 'pppoe':
6018
					case 'ppp':
6019
					case 'l2tp':
6020
					case 'pptp':
6021
						if (is_array($cfg['wireless']) || preg_match(g_get('wireless_regex'), $cfg['if'])) {
6022
							$wanif = interface_get_wireless_clone($cfg['if']);
6023
						} else {
6024
							$wanif = $cfg['if'];
6025
						}
6026
						break;
6027
					default:
6028
						switch ($cfg['ipaddr']) {
6029
							case 'pppoe':
6030
							case 'ppp':
6031
							case 'l2tp':
6032
							case 'pptp':
6033
								// Added catch for static v6 but using v4 link. Sets things to use pppoe link
6034
								if ((isset($cfg['dhcp6usev4iface']) && $realv6iface === false) ||
6035
								    isset($cfg['ipv6usev4iface']) || isset($cfg['slaacusev4iface'])) {
6036
									$wanif = $cfg['if'];
6037
								} else {
6038
									$parents = get_parent_interface($interface);
6039
									if (!empty($parents[0])) {
6040
										$wanif = $parents[0];
6041
									} else {
6042
										$wanif = $cfg['if'];
6043
									}
6044
								}
6045
								break;
6046
							default:
6047
								if (is_array($cfg['wireless']) || preg_match(g_get('wireless_regex'), $cfg['if'])) {
6048
									$wanif = interface_get_wireless_clone($cfg['if']);
6049
								} else {
6050
									$wanif = $cfg['if'];
6051
								}
6052
								break;
6053
						}
6054
						break;
6055
				}
6056
			} else {
6057
				// Wireless cloned NIC support (FreeBSD 8+)
6058
				// interface name format: $parentnic_wlanparentnic#
6059
				// example: ath0_wlan0
6060
				if (is_array($cfg['wireless']) || preg_match(g_get('wireless_regex'), $cfg['if'])) {
6061
					$wanif = interface_get_wireless_clone($cfg['if']);
6062
				} else {
6063
					$wanif = $cfg['if'];
6064
				}
6065
			}
6066
			break;
6067
	}
6068

    
6069
	return $wanif;
6070
}
6071

    
6072
/* Guess the physical interface by providing a IP address */
6073
function guess_interface_from_ip($ipaddress) {
6074

    
6075
	if (!is_ipaddr($ipaddress)) {
6076
		return false;
6077
	}
6078

    
6079
	$route = route_get($ipaddress, '', true);
6080
	if (empty($route)) {
6081
		return false;
6082
	}
6083

    
6084
	if (!empty($route[0]['interface-name'])) {
6085
		return $route[0]['interface-name'];
6086
	}
6087

    
6088
	return false;
6089
}
6090

    
6091
/*
6092
 * find_ip_interface($ip): return the interface where an ip is defined
6093
 *   (or if $bits is specified, where an IP within the subnet is defined)
6094
 */
6095
function find_ip_interface($ip, $bits = null) {
6096
	if (!is_ipaddr($ip)) {
6097
		return false;
6098
	}
6099

    
6100
	$isv6ip = is_ipaddrv6($ip);
6101

    
6102
	/* if list */
6103
	$ifdescrs = get_configured_interface_list();
6104

    
6105
	foreach ($ifdescrs as $ifname) {
6106
		$ifip = ($isv6ip) ? get_interface_ipv6($ifname) : get_interface_ip($ifname);
6107
		if (is_null($ifip)) {
6108
			continue;
6109
		}
6110
		if (is_null($bits)) {
6111
			if ($ip == $ifip) {
6112
				$int = get_real_interface($ifname);
6113
				return $int;
6114
			}
6115
		} else {
6116
			if (ip_in_subnet($ifip, $ip . "/" . $bits)) {
6117
				$int = get_real_interface($ifname);
6118
				return $int;
6119
			}
6120
		}
6121
	}
6122

    
6123
	return false;
6124
}
6125

    
6126
/*
6127
 * find_virtual_ip_alias($ip): return the virtual IP alias where an IP is found
6128
 *   (or if $bits is specified, where an IP within the subnet is found)
6129
 */
6130
function find_virtual_ip_alias($ip, $bits = null) {
6131

    
6132
	if (!is_ipaddr($ip)) {
6133
		return false;
6134
	}
6135

    
6136
	$isv6ip = is_ipaddrv6($ip);
6137

    
6138
	foreach (config_get_path('virtualip/vip', []) as $vip) {
6139
		if ($vip['mode'] === "ipalias") {
6140
			if (is_ipaddrv6($vip['subnet']) != $isv6ip) {
6141
				continue;
6142
			}
6143
			if (is_null($bits)) {
6144
				if (ip_in_subnet($ip, $vip['subnet'] . "/" . $vip['subnet_bits'])) {
6145
					return $vip;
6146
				}
6147
			} else {
6148
				if (($isv6ip && check_subnetsv6_overlap($ip, $bits, $vip['subnet'], $vip['subnet_bits'])) ||
6149
				    (!$isv6ip && check_subnets_overlap($ip, $bits, $vip['subnet'], $vip['subnet_bits']))) {
6150
					return $vip;
6151
				}
6152
			}
6153
		}
6154
	}
6155
	return false;
6156
}
6157

    
6158
function link_interface_to_track6($int, $action = "") {
6159
	$list = array();
6160
	if (empty($int)) {
6161
		return $list;
6162
	}
6163

    
6164
	foreach (config_get_path('interfaces', []) as $ifname => $ifcfg) {
6165
		if (!isset($ifcfg['enable'])) {
6166
			continue;
6167
		}
6168
		if (!empty($ifcfg['ipaddrv6']) && $ifcfg['track6-interface'] == $int) {
6169
			if ($action == "update") {
6170
				interface_track6_configure($ifname, $ifcfg);
6171
			} elseif ($action == "") {
6172
				$list[$ifname] = $ifcfg;
6173
			}
6174
		}
6175
	}
6176
	return $list;
6177
}
6178

    
6179
function interface_find_child_cfgmtu($realiface) {
6180
	$vlans = link_interface_to_vlans($realiface);
6181
	$qinqs = link_interface_to_qinqs($realiface);
6182
	$bridge = link_interface_to_bridge($realiface);
6183
	$mtu = 0;
6184

    
6185
	foreach ([$vlans, $qinqs, ['bridge' => $bridge]] as $ints) {
6186
		if (is_array($ints)) {
6187
			foreach ($ints as $int) {
6188
				$ifass = convert_real_interface_to_friendly_interface_name($int['vlanif']);
6189
				if (!empty($ifass) &&
6190
				    config_path_enabled("interfaces/{$ifass}") &&
6191
				    (intval(config_get_path("interfaces/{$ifass}/mtu")) > $mtu)) {
6192
					$mtu = config_get_path("interfaces/{$ifass}/mtu");
6193
				}
6194
			}
6195
		}
6196
	}
6197

    
6198
	unset($vlans, $qinqs, $bridge);
6199
	return $mtu;
6200
}
6201

    
6202
function link_interface_to_vlans($int, $action = "") {
6203
	if (empty($int)) {
6204
		return;
6205
	}
6206

    
6207
	$ifaces = array();
6208
	foreach (config_get_path('vlans/vlan', []) as $vlan) {
6209
		if ($int == $vlan['if']) {
6210
			if ($action == "update") {
6211
				interfaces_bring_up($int);
6212
			} else {
6213
				$ifaces[$vlan['tag']] = $vlan;
6214
			}
6215
		}
6216
	}
6217
	if (!empty($ifaces)) {
6218
		return $ifaces;
6219
	}
6220
}
6221

    
6222
function link_interface_to_qinqs($int, $action = "") {
6223
	if (empty($int)) {
6224
		return;
6225
	}
6226

    
6227
	$ifaces = array();
6228
	foreach (config_get_path('qinqs/qinqentry', []) as $qinq) {
6229
		if ($int == $qinq['if']) {
6230
			if ($action == "update") {
6231
				interfaces_bring_up($int);
6232
			} else {
6233
				$ifaces[$qinq['tag']] = $qinq;
6234
			}
6235
		}
6236
	}
6237
	if (!empty($ifaces)) {
6238
		return $ifaces;
6239
	}
6240
}
6241

    
6242
function link_interface_to_vips($int, $action = "", $vhid = '') {
6243
	$updatevips = false;
6244

    
6245
	$result = array();
6246
	foreach (config_get_path('virtualip/vip', []) as $vip) {
6247
		if (substr($vip['interface'], 0, 4) == "_vip") {
6248
			$iface = get_configured_vip_interface($vip['interface']);
6249
		} else {
6250
			$iface = $vip['interface'];
6251
		}
6252
		if ($int != $iface) {
6253
			continue;
6254
		}
6255
		if ($action == "update") {
6256
			$updatevips = true;
6257
		} else {
6258
			if (empty($vhid) || ($vhid == $vip['vhid']) ||
6259
				substr($vip['interface'], 0, 4) == "_vip") {
6260
				$result[] = $vip;
6261
			}
6262
		}
6263
	}
6264
	if ($updatevips === true) {
6265
		interfaces_vips_configure($int);
6266
	}
6267
	return $result;
6268

    
6269
	return NULL;
6270
}
6271

    
6272
/****f* interfaces/link_interface_to_bridge
6273
 * NAME
6274
 *   link_interface_to_bridge - Finds out a bridge group for an interface
6275
 * INPUTS
6276
 *   $ip
6277
 * RESULT
6278
 *   bridge[0-99]
6279
 ******/
6280
function link_interface_to_bridge($int) {
6281
	foreach (config_get_path('bridges/bridged', []) as $bridge) {
6282
		if (in_array($int, explode(',', $bridge['members']))) {
6283
			return "{$bridge['bridgeif']}";
6284
		}
6285
	}
6286
}
6287

    
6288
function link_interface_to_lagg($int) {
6289
	foreach (config_get_path('laggs/lagg', []) as $lagg) {
6290
		if (in_array($int, explode(',', $lagg['members']))) {
6291
			return "{$lagg['laggif']}";
6292
		}
6293
	}
6294
}
6295

    
6296
function link_interface_to_group($int) {
6297
	$result = array();
6298

    
6299
	foreach (config_get_path('ifgroups/ifgroupentry', []) as $group) {
6300
		if (in_array($int, explode(" ", $group['members']))) {
6301
			$result[$group['ifname']] = $int;
6302
		}
6303
	}
6304

    
6305
	return $result;
6306
}
6307

    
6308
function link_interface_to_tunnelif($interface, $type, $remote = 'any') {
6309
	$result = array();
6310

    
6311
	if (empty($interface)) {
6312
		return $result;
6313
	}
6314

    
6315
	if (!in_array($type, array('gre', 'gif'))) {
6316
		return $result;
6317
	}
6318

    
6319
	foreach (config_get_path("{$type}s/{$type}", []) as $tunnel) {
6320
		if (($tunnel['if'] == $interface) &&
6321
			(($remote == 'any') ||
6322
			 (is_ipaddrv4($tunnel['remote-addr']) && ($remote == 'inet')) ||
6323
			 (is_ipaddrv6($tunnel['remote-addr']) && ($remote == 'inet6')))) {
6324
			$result[] = $tunnel;
6325
		}
6326
	}
6327

    
6328
	return $result;
6329
}
6330

    
6331
function link_interface_to_ppp_tunnelif($interface) {
6332
	$result = array();
6333

    
6334
	if (empty($interface)) {
6335
		return $result;
6336
	}
6337

    
6338
	$realif = get_real_interface($interface);
6339
	foreach (config_get_path('ppps/ppp', []) as $ppp) {
6340
		if (($ppp['ports'] == $realif) && in_array($ppp['type'], array('l2tp', 'pptp'))) {
6341
			$result[] = $ppp;
6342
		}
6343
	}
6344

    
6345
	return $result;
6346
}
6347

    
6348
/*
6349
 * find_interface_ip($interface): return the interface ip (first found)
6350
 */
6351
function find_interface_ip($interface, $flush = false) {
6352
	global $interface_ip_arr_cache;
6353
	global $interface_sn_arr_cache;
6354

    
6355
	$interface = str_replace("\n", "", $interface);
6356

    
6357
	if (!does_interface_exist($interface)) {
6358
		return;
6359
	}
6360

    
6361
	/* Setup IP cache */
6362
	if (!isset($interface_ip_arr_cache[$interface]) or $flush) {
6363
		if (file_exists("/var/db/{$interface}_ip")) {
6364
			$ifip = chop(file_get_contents("/var/db/{$interface}_ip"));
6365
			$ifaddrs = pfSense_getall_interface_addresses($interface);
6366
			foreach ($ifaddrs as $ifaddr) {
6367
				list($ip, $mask) = explode("/", $ifaddr);
6368
				if ($ip == $ifip) {
6369
					$interface_ip_arr_cache[$interface] = $ip;
6370
					$interface_sn_arr_cache[$interface] = $mask;
6371
					break;
6372
				}
6373
			}
6374
		}
6375
		if (!isset($interface_ip_arr_cache[$interface])) {
6376
			$ifinfo = get_interface_addresses($interface);
6377
			$interface_ip_arr_cache[$interface] = $ifinfo['ipaddr'];
6378
			$interface_sn_arr_cache[$interface] = $ifinfo['subnetbits'];
6379
		}
6380
	}
6381

    
6382
	return $interface_ip_arr_cache[$interface];
6383
}
6384

    
6385
/*
6386
 * find_interface_ipv6($interface): return the interface ip (first found)
6387
 */
6388
function find_interface_ipv6($interface, $flush = false) {
6389
	global $interface_ipv6_arr_cache;
6390
	global $interface_snv6_arr_cache;
6391

    
6392
	$interface = trim($interface);
6393
	$interface = get_real_interface($interface);
6394

    
6395
	if (!does_interface_exist($interface)) {
6396
		return;
6397
	}
6398

    
6399
	/* Setup IP cache */
6400
	if (!isset($interface_ipv6_arr_cache[$interface]) or $flush) {
6401
		$ifinfo = get_interface_addresses($interface);
6402
		$interface_ipv6_arr_cache[$interface] = $ifinfo['ipaddr6'];
6403
		$interface_snv6_arr_cache[$interface] = $ifinfo['subnetbits6'];
6404
	}
6405

    
6406
	return $interface_ipv6_arr_cache[$interface];
6407
}
6408

    
6409
/*
6410
 * Return the interface ipv6 link local address
6411
 */
6412
function find_interface_ipv6_ll($interface, $flush = false, $usefirst = true) {
6413
	global $interface_llv6_arr_cache;
6414

    
6415
	$interface = str_replace("\n", "", $interface);
6416

    
6417
	if (!does_interface_exist($interface)) {
6418
		return;
6419
	}
6420

    
6421
	/* Setup IP cache */
6422
	if (!isset($interface_llv6_arr_cache[$interface]) or $flush) {
6423
		$ifinfo = pfSense_getall_interface_addresses($interface);
6424
		foreach ($ifinfo as $line) {
6425
			if (strstr($line, ":")) {
6426
				$parts = explode("/", $line);
6427
				if (is_linklocal($parts[0])) {
6428
					$ifinfo['linklocal'] = $parts[0];
6429
					if ($usefirst) {
6430
						break;
6431
					}
6432
				}
6433
			}
6434
		}
6435
		$interface_llv6_arr_cache[$interface] = $ifinfo['linklocal'];
6436
	}
6437
	return $interface_llv6_arr_cache[$interface];
6438
}
6439

    
6440
function find_interface_subnet($interface, $flush = false) {
6441
	global $interface_sn_arr_cache;
6442
	global $interface_ip_arr_cache;
6443

    
6444
	$interface = str_replace("\n", "", $interface);
6445
	if (does_interface_exist($interface) == false) {
6446
		return;
6447
	}
6448

    
6449
	if (!isset($interface_sn_arr_cache[$interface]) or $flush) {
6450
		$ifinfo = get_interface_addresses($interface);
6451
		$interface_ip_arr_cache[$interface] = $ifinfo['ipaddr'];
6452
		$interface_sn_arr_cache[$interface] = $ifinfo['subnetbits'];
6453
	}
6454

    
6455
	return $interface_sn_arr_cache[$interface];
6456
}
6457

    
6458
function find_interface_subnetv6($interface, $flush = false) {
6459
	global $interface_snv6_arr_cache;
6460
	global $interface_ipv6_arr_cache;
6461

    
6462
	$interface = str_replace("\n", "", $interface);
6463
	if (does_interface_exist($interface) == false) {
6464
		return;
6465
	}
6466

    
6467
	if (!isset($interface_snv6_arr_cache[$interface]) or $flush) {
6468
		$ifinfo = get_interface_addresses($interface);
6469
		$interface_ipv6_arr_cache[$interface] = $ifinfo['ipaddr6'];
6470
		$interface_snv6_arr_cache[$interface] = $ifinfo['subnetbits6'];
6471
	}
6472

    
6473
	return $interface_snv6_arr_cache[$interface];
6474
}
6475

    
6476
function ip_in_interface_alias_subnet($interface, $ipalias) {
6477
	if (empty($interface) || !is_ipaddr($ipalias)) {
6478
		return false;
6479
	}
6480
	foreach (config_get_path('virtualip/vip',[]) as $vip) {
6481
		switch ($vip['mode']) {
6482
		case "ipalias":
6483
			if ($vip['interface'] <> $interface) {
6484
				break;
6485
			}
6486
			$subnet = is_ipaddrv6($ipalias) ? gen_subnetv6($vip['subnet'], $vip['subnet_bits']) : gen_subnet($vip['subnet'], $vip['subnet_bits']);
6487
			if (ip_in_subnet($ipalias, $subnet . "/" . $vip['subnet_bits'])) {
6488
				return true;
6489
			}
6490
			break;
6491
		}
6492
	}
6493

    
6494
	return false;
6495
}
6496

    
6497
function get_possible_listen_ips($include_ipv6_link_local=false) {
6498

    
6499
	$interfaces = get_configured_interface_with_descr();
6500
	foreach ($interfaces as $iface => $ifacename) {
6501
		if ($include_ipv6_link_local) {
6502
			/* This is to avoid going though added ll below */
6503
			if (substr($iface, 0, 5) == '_lloc') {
6504
				continue;
6505
			}
6506
			$llip = find_interface_ipv6_ll(get_real_interface($iface));
6507
			if (!empty($llip)) {
6508
				$interfaces["_lloc{$iface}"] = "{$ifacename} IPv6 Link-Local";
6509
			}
6510
		}
6511
	}
6512
	$viplist = get_configured_vip_list();
6513
	foreach ($viplist as $vip => $address) {
6514
		$interfaces[$vip] = $address;
6515
		if (get_vip_descr($address)) {
6516
			$interfaces[$vip] .= " (". get_vip_descr($address) .")";
6517
		}
6518
	}
6519

    
6520
	$interfaces['lo0'] = 'Localhost';
6521

    
6522
	return $interfaces;
6523
}
6524

    
6525
function get_possible_traffic_source_addresses($include_ipv6_link_local=false) {
6526
	$sourceips = get_possible_listen_ips($include_ipv6_link_local);
6527
	foreach (array('server', 'client') as $mode) {
6528
		foreach (config_get_path("openvpn/openvpn-{$mode}", []) as $setting) {
6529
			if (!isset($setting['disable'])) {
6530
				$sourceips_key = 'ovpn' . substr($mode, 0, 1) . $setting['vpnid'];
6531
				$sourceips[$sourceips_key] = gettext("OpenVPN") . " " . $mode . ": " . htmlspecialchars($setting['description']);
6532
			}
6533
		}
6534
	}
6535

    
6536
	foreach (config_get_path('ipsec/phase1', []) as $p1) {
6537
		if ($p1['disabled']) {
6538
			continue;
6539
		}
6540
		if (ipsec_vti($p1)) {
6541
			$vtiiflist = interface_ipsec_vti_list_p1($p1);
6542
			if (!empty($vtiiflist)) {
6543
				$sourceips = array_merge($sourceips, $vtiiflist);
6544
			}
6545
		}
6546
	}
6547
	return $sourceips;
6548
}
6549

    
6550
function get_interface_ip($interface = "wan") {
6551
	if (empty($interface)) {
6552
		return null;
6553
	}
6554
	if (substr($interface, 0, 4) == '_vip') {
6555
		return get_configured_vip_ipv4($interface);
6556
	} elseif (substr($interface, 0, 5) == '_lloc') {
6557
		/* No link-local address for v4. */
6558
		return null;
6559
	}
6560

    
6561
	$realif = get_failover_interface($interface, 'inet');
6562
	if (!$realif) {
6563
		return null;
6564
	}
6565

    
6566
	if (substr($realif, 0, 4) == '_vip') {
6567
		return get_configured_vip_ipv4($realif);
6568
	} elseif (substr($realif, 0, 5) == '_lloc') {
6569
		/* No link-local address for v4. */
6570
		return null;
6571
	}
6572

    
6573
	$iface = config_get_path("interfaces/{$interface}");
6574
	if (is_array($iface) && is_ipaddr($iface['ipaddr'])) {
6575
		return ($iface['ipaddr']);
6576
	}
6577

    
6578
	/*
6579
	 * Beware that find_interface_ip() is our last option, it will
6580
	 * return the first IP it find on interface, not necessarily the
6581
	 * main IP address.
6582
	 */
6583
	$curip = find_interface_ip($realif);
6584
	if ($curip && is_ipaddr($curip) && ($curip != "0.0.0.0")) {
6585
		return $curip;
6586
	} else {
6587
		return null;
6588
	}
6589
}
6590

    
6591
function get_interface_ipv6($interface = "wan", $flush = false, $linklocal_fallback = false) {
6592
	if (empty($interface)) {
6593
		return null;
6594
	}
6595
	if (substr($interface, 0, 4) == '_vip') {
6596
		return get_configured_vip_ipv6($interface);
6597
	} elseif (substr($interface, 0, 5) == '_lloc') {
6598
		return get_interface_linklocal($interface);
6599
	}
6600

    
6601
	$realif = get_failover_interface($interface, 'inet6');
6602
	if (!$realif) {
6603
		return null;
6604
	}
6605

    
6606
	if (substr($realif, 0, 4) == '_vip') {
6607
		return get_configured_vip_ipv6($realif);
6608
	} elseif (substr($realif, 0, 5) == '_lloc') {
6609
		return get_interface_linklocal($realif);
6610
	}
6611

    
6612
	$iface = config_get_path("interfaces/{$interface}");
6613
	if (is_array($iface)) {
6614
		switch ($iface['ipaddr']) {
6615
			case 'pppoe':
6616
			case 'l2tp':
6617
			case 'pptp':
6618
			case 'ppp':
6619
				if (($iface['ipaddrv6'] == 'dhcp6') ||
6620
				    ($iface['ipaddrv6'] == 'slaac')) {
6621
					$realif = get_real_interface($interface, 'inet6', false);
6622
				}
6623
				break;
6624
		}
6625
		if (is_ipaddrv6($iface['ipaddrv6'])) {
6626
			return ($iface['ipaddrv6']);
6627
		}
6628
	}
6629

    
6630
	/* try to find track6 address, see https://redmine.pfsense.org/issues/5999 */
6631
	$checkif = is_array($iface) ? $interface : convert_real_interface_to_friendly_interface_name($interface);
6632
	if (!empty($checkif) && config_get_path("interfaces/{$checkif}/ipaddrv6", "") == 'track6') {
6633
		$curip = get_interface_track6ip($checkif);
6634
		if ($curip) {
6635
			return $curip[0];
6636
		}
6637
	}
6638

    
6639
	/*
6640
	 * Beware that find_interface_ip() is our last option, it will
6641
	 * return the first IP it find on interface, not necessarily the
6642
	 * main IP address.
6643
	 */
6644
	$curip = find_interface_ipv6($realif, $flush);
6645
	if ($curip && is_ipaddrv6($curip) && ($curip != "::")) {
6646
		return $curip;
6647
	} else {
6648
		/*
6649
		 * NOTE: On the case when only the prefix is requested,
6650
		 * the communication on WAN will be done over link-local.
6651
		 */
6652
		$iface = config_get_path("interfaces/{$interface}");
6653
		if ($linklocal_fallback || (is_array($iface) && isset($iface['dhcp6prefixonly']))) {
6654
			$curip = find_interface_ipv6_ll($realif, $flush);
6655
			if ($curip && is_ipaddrv6($curip) && ($curip != "::")) {
6656
				return $curip;
6657
			}
6658
		}
6659
	}
6660
	return null;
6661
}
6662

    
6663
function get_interface_linklocal($interface = "wan") {
6664

    
6665
	$realif = get_failover_interface($interface, 'inet6');
6666
	if (!$realif) {
6667
		return null;
6668
	}
6669

    
6670
	if (substr($interface, 0, 4) == '_vip') {
6671
		$realif = get_real_interface($interface);
6672
	} elseif (substr($interface, 0, 5) == '_lloc') {
6673
		$realif = get_real_interface(substr($interface, 5));
6674
	}
6675

    
6676
	$curip = find_interface_ipv6_ll($realif);
6677
	if ($curip && is_ipaddrv6($curip) && ($curip != "::")) {
6678
		return $curip;
6679
	} else {
6680
		return null;
6681
	}
6682
}
6683

    
6684
/**
6685
 * Get the first IPv6 address that is not a VIP or Link-Local address.
6686
 * Prioritize IPv6 GUA over ULA.
6687
 * 
6688
 * @param string $interface Interface to check
6689
 * @return array|bool IP address and prefix length; false If no match is found
6690
 */
6691
function get_interface_track6ip($interface = "wan") {
6692
	$ifinfo = get_interface_addresses(get_real_interface($interface));
6693

    
6694
	if (isset($ifinfo['ipaddr6']) && isset($ifinfo['subnetbits6'])) {
6695
		return [$ifinfo['ipaddr6'], $ifinfo['subnetbits6']];
6696
	}
6697

    
6698
	return false;
6699
}
6700

    
6701
function get_interface_subnet($interface = "wan") {
6702
	if (substr($interface, 0, 4) == '_vip') {
6703
		return (get_configured_vip_subnetv4($interface));
6704
	}
6705

    
6706
	$iface = config_get_path("interfaces/{$interface}");
6707
	if (is_array($iface) && !empty($iface['subnet']) && is_ipaddrv4($iface['ipaddr'])) {
6708
		return ($iface['subnet']);
6709
	}
6710

    
6711
	$realif = get_real_interface($interface);
6712
	if (!$realif) {
6713
		return (NULL);
6714
	}
6715

    
6716
	$cursn = find_interface_subnet($realif);
6717
	if (!empty($cursn)) {
6718
		return ($cursn);
6719
	}
6720

    
6721
	return (NULL);
6722
}
6723

    
6724
function get_interface_subnetv6($interface = "wan") {
6725
	if (substr($interface, 0, 4) == '_vip') {
6726
		return (get_configured_vip_subnetv6($interface));
6727
	} elseif (substr($interface, 0, 5) == '_lloc') {
6728
		$interface = substr($interface, 5);
6729
	}
6730

    
6731
	$iface = config_get_path("interfaces/{$interface}");
6732
	if (is_array($iface) && !empty($iface['subnetv6']) && is_ipaddrv6($iface['ipaddrv6'])) {
6733
		return ($iface['subnetv6']);
6734
	}
6735

    
6736
	$realif = get_real_interface($interface, 'inet6');
6737
	if (!$realif) {
6738
		return (NULL);
6739
	}
6740

    
6741
	/* try to find track6 address, see https://redmine.pfsense.org/issues/5999 */
6742
	if (config_get_path("interfaces/{$interface}/ipaddrv6") == 'track6') {
6743
		$curip = get_interface_track6ip($interface);
6744
		if ($curip) {
6745
			return $curip[1];
6746
		}
6747
	}
6748

    
6749
	$cursn = find_interface_subnetv6($realif);
6750
	if (!empty($cursn)) {
6751
		return ($cursn);
6752
	}
6753

    
6754
	return (NULL);
6755
}
6756

    
6757
/* return outside interfaces with a gateway */
6758
function get_interfaces_with_gateway() {
6759

    
6760
	$ints = array();
6761

    
6762
	/* loop interfaces, check config for outbound */
6763
	foreach (config_get_path('interfaces', []) as $ifdescr => $ifname) {
6764
		switch ($ifname['ipaddr']) {
6765
			case "dhcp":
6766
			case "pppoe":
6767
			case "pptp":
6768
			case "l2tp":
6769
			case "ppp":
6770
				$ints[$ifdescr] = $ifdescr;
6771
				break;
6772
			default:
6773
				if (substr($ifname['if'], 0, 4) == "ovpn" ||
6774
				    !empty($ifname['gateway'])) {
6775
					$ints[$ifdescr] = $ifdescr;
6776
				} elseif (substr($ifname['if'], 0, 5) == "ipsec" ||
6777
				    !empty($ifname['gateway'])) {
6778
					$ints[$ifdescr] = $ifdescr;
6779
				}
6780

    
6781
				break;
6782
		}
6783
	}
6784
	return $ints;
6785
}
6786

    
6787
/* return true if interface has a gateway */
6788
function interface_has_gateway($friendly) {
6789
	$ifname = config_get_path("interfaces/{$friendly}");
6790
	if (!empty($ifname)) {
6791
		switch ($ifname['ipaddr']) {
6792
			case "dhcp":
6793
				/* see https://redmine.pfsense.org/issues/5135 */
6794
				if (get_interface_gateway($friendly)) {
6795
					return true;
6796
				}
6797
				break;
6798
			case "pppoe":
6799
			case "pptp":
6800
			case "l2tp":
6801
			case "ppp":
6802
				return true;
6803
				break;
6804
			default:
6805
				if ((substr($ifname['if'], 0, 4) == "ovpn") ||
6806
				    (substr($ifname['if'], 0, 5) == "ipsec")) {
6807
					return true;
6808
				}
6809
				$tunnelif = substr($ifname['if'], 0, 3);
6810
				if ($tunnelif == "gif" || $tunnelif == "gre") {
6811
					if (find_interface_ip($ifname['if'])) {
6812
						return true;
6813
					}
6814
				}
6815
				if (!empty($ifname['gateway'])) {
6816
					return true;
6817
				}
6818
				break;
6819
		}
6820
	}
6821

    
6822
	return false;
6823
}
6824

    
6825
/* return true if interface has a gateway */
6826
function interface_has_gatewayv6($friendly) {
6827
	$ifname = config_get_path("interfaces/{$friendly}");
6828
	if (!empty($ifname)) {
6829
		switch ($ifname['ipaddrv6']) {
6830
			case "slaac":
6831
			case "dhcp6":
6832
			case "6to4":
6833
			case "6rd":
6834
				return true;
6835
				break;
6836
			default:
6837
				if ((substr($ifname['if'], 0, 4) == "ovpn") ||
6838
				    (substr($ifname['if'], 0, 5) == "ipsec")) {
6839
					return true;
6840
				}
6841
				$tunnelif = substr($ifname['if'], 0, 3);
6842
				if ($tunnelif == "gif" || $tunnelif == "gre") {
6843
					if (find_interface_ipv6($ifname['if'])) {
6844
						return true;
6845
					}
6846
				}
6847
				if (!empty($ifname['gatewayv6'])) {
6848
					return true;
6849
				}
6850
				break;
6851
		}
6852
	}
6853

    
6854
	return false;
6855
}
6856

    
6857
/****f* interfaces/is_altq_capable
6858
 * NAME
6859
 *   is_altq_capable - Test if interface is capable of using ALTQ
6860
 * INPUTS
6861
 *   $int            - string containing interface name
6862
 * RESULT
6863
 *   boolean         - true or false
6864
 ******/
6865

    
6866
function is_altq_capable($int) {
6867
	/* Per:
6868
	 * https://www.freebsd.org/cgi/man.cgi?query=altq&apropos=0&sektion=0&manpath=FreeBSD+11.0-RELEASE&arch=default&format=html
6869
	 * Only the following drivers have ALTQ support
6870
	 * 20150328 - removed wireless drivers - ath, awi, bwn, iwi, ipw, ral, rum, run, wi - for now. redmine #4406
6871
	 */
6872
	$capable = array("ae", "age", "alc", "ale", "an", "aue", "axe", "bce",
6873
			"bfe", "bge", "bnxt", "bridge", "bxe", "cas", "cpsw", "dc", "de",
6874
			"ed", "em", "ep", "epair", "et", "fxp", "gem", "hme", "hn",
6875
			"igb", "igc", "ix", "ixv", "jme", "l2tp", "le", "lem", "msk", "mxge", "my",
6876
			"ndis", "nfe", "ng", "nge", "npe", "nve", "ql", "ovpnc",
6877
			"ovpns", "ppp", "pppoe", "pptp", "re", "rl", "sf", "sge",
6878
			"sis", "sk", "ste", "stge", "ti", "tun", "txp", "udav",
6879
			"ural", "vge", "vlan", "vmx", "vr", "vte", "vtnet", "xl");
6880

    
6881
	$int_family = remove_ifindex($int);
6882

    
6883
	if (in_array($int_family, $capable)) {
6884
		return true;
6885
	} elseif (stristr($int, "l2tp")) { /* VLANs are named $parent_$vlan now */
6886
		return true;
6887
	} elseif (interface_is_vlan($int) != NULL) { /* VLANs are named $parent.$vlan now */
6888
		return true;
6889
	} elseif (stristr($int, "_wlan")) { /* WLANs are named $parent_$wlan now */
6890
		return true;
6891
	} else {
6892
		return false;
6893
	}
6894
}
6895

    
6896
/****f* interfaces/is_interface_wireless
6897
 * NAME
6898
 *   is_interface_wireless - Returns if an interface is wireless
6899
 * RESULT
6900
 *   $tmp       - Returns if an interface is wireless
6901
 ******/
6902
function is_interface_wireless($interface) {
6903
	global $g;
6904

    
6905
	$friendly = convert_real_interface_to_friendly_interface_name($interface);
6906
	if (!empty($friendly) && config_get_path("interfaces/{$friendly}/wireless") === null) {
6907
		if (preg_match(g_get('wireless_regex'), $interface)) {
6908
			config_init_path("interfaces/{$friendly}/wireless");
6909
			return true;
6910
		}
6911
		return false;
6912
	} else {
6913
		return true;
6914
	}
6915
}
6916

    
6917
function get_wireless_modes($interface) {
6918
	/* return wireless modes and channels */
6919
	$wireless_modes = array();
6920

    
6921
	$cloned_interface = get_real_interface($interface);
6922

    
6923
	if ($cloned_interface && is_interface_wireless($cloned_interface)) {
6924
		$chan_list = "/sbin/ifconfig {$cloned_interface} list chan";
6925
		$stack_list = "/usr/bin/awk -F\"Channel \" '{ gsub(/\\*/, \" \"); print \$2 \"\\n\" \$3 }'";
6926
		$format_list = "/usr/bin/awk '{print \$5 \" \" \$6 \",\" \$1}'";
6927

    
6928
		$interface_channels = [];
6929
		exec("$chan_list | $stack_list | /usr/bin/sort -u | $format_list 2>&1", $interface_channels);
6930
		$interface_channel_count = count($interface_channels);
6931

    
6932
		$c = 0;
6933
		while ($c < $interface_channel_count) {
6934
			$channel_line = explode(",", $interface_channels["$c"]);
6935
			$wireless_mode = trim($channel_line[0]);
6936
			$wireless_channel = trim($channel_line[1]);
6937
			if (trim($wireless_mode) != "") {
6938
				/* if we only have 11g also set 11b channels */
6939
				if ($wireless_mode == "11g") {
6940
					if (!isset($wireless_modes["11b"])) {
6941
						$wireless_modes["11b"] = array();
6942
					}
6943
				} elseif ($wireless_mode == "11g ht") {
6944
					if (!isset($wireless_modes["11b"])) {
6945
						$wireless_modes["11b"] = array();
6946
					}
6947
					if (!isset($wireless_modes["11g"])) {
6948
						$wireless_modes["11g"] = array();
6949
					}
6950
					$wireless_mode = "11ng";
6951
				} elseif ($wireless_mode == "11a ht") {
6952
					if (!isset($wireless_modes["11a"])) {
6953
						$wireless_modes["11a"] = array();
6954
					}
6955
					$wireless_mode = "11na";
6956
				}
6957
				$wireless_modes["$wireless_mode"]["$c"] = $wireless_channel;
6958
			}
6959
			$c++;
6960
		}
6961
	}
6962
	return($wireless_modes);
6963
}
6964

    
6965
function get_wireless_channels($interface) {
6966
		$chan_list = "/sbin/ifconfig -v " . escapeshellarg($interface) . " list chan";
6967
		$stack_list = "/usr/bin/awk -F\"Channel \" '{ gsub(/\\*/, \" \"); print \$2 \"\\n\" \$3 }'";
6968
		$format_list = "/usr/bin/awk '{print \$5 \",\" \$6 \",\" \$1}'";
6969

    
6970
		$interface_channels = [];
6971
		exec("$chan_list | $stack_list | /usr/bin/sort -u | $format_list 2>&1", $interface_channels);
6972
		return $interface_channels;
6973
}
6974

    
6975
/* return wireless HT modes */
6976
function get_wireless_ht_modes($interface) {
6977
	$wireless_hts_supported = array(0 => gettext('Auto'));
6978

    
6979
	$cloned_interface = get_real_interface($interface);
6980

    
6981
	if ($cloned_interface && is_interface_wireless($cloned_interface)) {
6982
		$interface_channels = get_wireless_channels($cloned_interface);
6983

    
6984
		foreach ($interface_channels as $channel) {
6985
			$channel_line = explode(",", $channel);
6986
			$wireless_ht = trim($channel_line[1]);
6987
			if (!empty($wireless_ht)) {
6988
				$wireless_hts_supported[$wireless_ht] = strtoupper($wireless_ht);
6989
			}
6990
		}
6991
	}
6992
	return($wireless_hts_supported);
6993
}
6994

    
6995
/* return wireless HT by channel/standard */
6996
function get_wireless_ht_list($interface) {
6997
	$wireless_hts = array();
6998

    
6999
	$cloned_interface = get_real_interface($interface);
7000

    
7001
	if ($cloned_interface && is_interface_wireless($cloned_interface)) {
7002
		$interface_channels = get_wireless_channels($cloned_interface);
7003
		$interface_channel_count = count($interface_channels);
7004

    
7005
		$c = 0;
7006
		while ($c < $interface_channel_count) {
7007
			$channel_line = explode(",", $interface_channels["$c"]);
7008
			$wireless_mode = trim($channel_line[0]);
7009
			$wireless_ht = trim($channel_line[1]);
7010
			$wireless_channel = trim($channel_line[2]);
7011
			if (!empty($wireless_mode) && !empty($wireless_ht)) {
7012
				if ($wireless_mode == "11g") {
7013
					if (!isset($wireless_modes["11g"])) {
7014
						$wireless_hts["11g"] = array();
7015
					}
7016
					$wireless_mode = "11ng";
7017
				} elseif ($wireless_mode == "11a") {
7018
					if (!isset($wireless_modes["11a"])) {
7019
						$wireless_hts["11a"] = array();
7020
					}
7021
					$wireless_mode = "11na";
7022
				}
7023
				$wireless_hts["$wireless_mode"]["$wireless_channel"][] = $wireless_ht;
7024
			}
7025
			$c++;
7026
		}
7027
	}
7028
	return($wireless_hts);
7029
}
7030

    
7031
/* return channel numbers, frequency, max txpower, and max regulation txpower */
7032
function get_wireless_channel_info($interface) {
7033
	$wireless_channels = array();
7034

    
7035
	$cloned_interface = get_real_interface($interface);
7036

    
7037
	if ($cloned_interface && is_interface_wireless($cloned_interface)) {
7038
		$chan_list = "/sbin/ifconfig " . escapeshellarg($cloned_interface) . " list txpower";
7039
		$stack_list = "/usr/bin/awk -F\"Channel \" '{ gsub(/\\*/, \" \"); print \$2 \"\\n\" \$3 }'";
7040
		$format_list = "/usr/bin/awk '{print \$1 \",\" \$3 \" \" \$4 \",\" \$5 \",\" \$7}'";
7041

    
7042
		$interface_channels = [];
7043
		exec("$chan_list | $stack_list | /usr/bin/sort -u | $format_list 2>&1", $interface_channels);
7044

    
7045
		foreach ($interface_channels as $channel_line) {
7046
			$channel_line = explode(",", $channel_line);
7047
			if (!isset($wireless_channels[$channel_line[0]])) {
7048
				$wireless_channels[$channel_line[0]] = $channel_line;
7049
			}
7050
		}
7051
	}
7052
	return($wireless_channels);
7053
}
7054

    
7055
function set_interface_mtu($interface, $mtu) {
7056

    
7057
	/* LAGG interface must be destroyed and re-created to change MTU */
7058
	if ((substr($interface, 0, 4) == 'lagg') &&
7059
	    (!strstr($interface, "."))) {
7060
		foreach (config_get_path('laggs/lagg', []) as $lagg) {
7061
			if ($lagg['laggif'] == $interface) {
7062
				interface_lagg_configure($lagg);
7063
				break;
7064
			}
7065
		}
7066
	} else {
7067
		pfSense_interface_mtu($interface, $mtu);
7068
		set_ipv6routes_mtu($interface, $mtu);
7069
	}
7070
}
7071

    
7072
/****f* interfaces/get_interface_mtu
7073
 * NAME
7074
 *   get_interface_mtu - Return the mtu of an interface
7075
 * RESULT
7076
 *   $tmp       - Returns the mtu of an interface
7077
 ******/
7078
function get_interface_mtu($interface) {
7079
	$mtu = pfSense_interface_getmtu($interface);
7080
	return $mtu['mtu'];
7081
}
7082

    
7083
function get_interface_mac($interface) {
7084
	$macinfo = get_interface_addresses($interface);
7085
	return $macinfo["macaddr"];
7086
}
7087

    
7088
function get_interface_vendor_mac($interface) {
7089
	global $g;
7090

    
7091
	$macinfo = get_interface_addresses($interface);
7092
	if (isset($macinfo["hwaddr"]) && $macinfo["hwaddr"] !=
7093
	    "00:00:00:00:00:00") {
7094
		return ($macinfo["hwaddr"]);
7095
	}
7096

    
7097
	$hwaddr_file = "{$g['tmp_path']}/{$interface}_hwaddr";
7098
	if (file_exists($hwaddr_file)) {
7099
		$macaddr = trim(file_get_contents($hwaddr_file));
7100
		if (is_macaddr($macaddr)) {
7101
			return ($macaddr);
7102
		}
7103
	} elseif (is_macaddr($macinfo['macaddr'])) {
7104
		/* Save original macaddress to be restored when necessary */
7105
		@file_put_contents($hwaddr_file, $macinfo['macaddr']);
7106
	}
7107

    
7108
	return (NULL);
7109
}
7110

    
7111
/****f* pfsense-utils/generate_random_mac_address
7112
 * NAME
7113
 *   generate_random_mac - generates a random mac address
7114
 * INPUTS
7115
 *   none
7116
 * RESULT
7117
 *   $mac - a random mac address
7118
 ******/
7119
function generate_random_mac_address() {
7120
	$mac = "02";
7121
	for ($x = 0; $x < 5; $x++) {
7122
		$mac .= ":" . dechex(rand(16, 255));
7123
	}
7124
	return $mac;
7125
}
7126

    
7127
function interface_setup_pppoe_reset_file($pppif, $iface="") {
7128
	global $g;
7129

    
7130
	$cron_file = "{$g['varetc_path']}/pppoe_restart_{$pppif}";
7131

    
7132
	if (!empty($iface) && !empty($pppif)) {
7133
		$cron_cmd = <<<EOD
7134
#!/bin/sh
7135
/usr/local/sbin/pfSctl -c 'interface reload {$iface}'
7136
/usr/bin/logger -t {$pppif} "PPPoE periodic reset executed on {$iface}"
7137

    
7138
EOD;
7139

    
7140
		@file_put_contents($cron_file, $cron_cmd);
7141
		chmod($cron_file, 0755);
7142
		sigkillbypid("{$g['varrun_path']}/cron.pid", "HUP");
7143
	} else {
7144
		unlink_if_exists($cron_file);
7145
	}
7146
}
7147

    
7148
function get_interface_default_mtu($type = "ethernet") {
7149
	switch ($type) {
7150
		case "gre":
7151
			return 1476;
7152
			break;
7153
		case "gif":
7154
			return 1280;
7155
			break;
7156
		case "tun":
7157
		case "vlan":
7158
		case "tap":
7159
		case "ethernet":
7160
		default:
7161
			return 1500;
7162
			break;
7163
	}
7164

    
7165
	/* Never reached */
7166
	return 1500;
7167
}
7168

    
7169
function get_vip_descr($ipaddress) {
7170

    
7171
	foreach (config_get_path('virtualip/vip', []) as $vip) {
7172
		if ($vip['subnet'] == $ipaddress) {
7173
			return ($vip['descr']);
7174
		}
7175
	}
7176
	return "";
7177
}
7178

    
7179
function interfaces_staticarp_configure($if) {
7180
	if (config_get_path('system/developerspew')) {
7181
		$mt = microtime();
7182
		echo "interfaces_staticarp_configure($if) being called $mt\n";
7183
	}
7184

    
7185
	if (empty(config_get_path("interfaces/{$if}"))) {
7186
		return 0;
7187
	}
7188

    
7189
	$ifcfg = config_get_path("interfaces/{$if}");
7190

    
7191
	/* Enable staticarp, if enabled */
7192
	$staticarp = config_get_path("dhcpd/{$if}/staticarp");
7193
	if ($staticarp) {
7194
		mwexec("/sbin/ifconfig " . escapeshellarg($ifcfg['if']) . " staticarp ");
7195
		mwexec("/usr/sbin/arp -d -i " . escapeshellarg($ifcfg['if']) . " -a > /dev/null 2>&1 ");
7196
	} else {
7197
		/*
7198
		 * Interfaces do not have staticarp enabled by default
7199
		 * Let's not disable staticarp on freshly created interfaces
7200
		 */
7201
		if (!is_platform_booting()) {
7202
			mwexec("/sbin/ifconfig " . escapeshellarg($ifcfg['if']) . " -staticarp ");
7203
			mwexec("/usr/sbin/arp -d -i " . escapeshellarg($ifcfg['if']) . " -a > /dev/null 2>&1 ");
7204
		}
7205
	}
7206

    
7207
	/* Enable static arp entries */
7208
	$staticmap = config_get_path("dhcpd/{$if}/staticmap", []);
7209
	if (is_array($staticmap)) {
7210
		foreach ($staticmap as $arpent) {
7211
			if (empty($arpent['ipaddr']) || empty($arpent['mac'])) {
7212
				continue;
7213
			}
7214
			if ($staticarp || isset($arpent['arp_table_static_entry'])) {
7215
				mwexec("/usr/sbin/arp -s " . escapeshellarg($arpent['ipaddr']) . " " . escapeshellarg($arpent['mac']));
7216
			}
7217
		}
7218
	}
7219

    
7220
	return 0;
7221
}
7222

    
7223
function get_failover_interface($interface, $family = "all") {
7224
	/* shortcut to get_real_interface if we find it in the config */
7225
	if (is_array(config_get_path("interfaces/{$interface}"))) {
7226
		return get_real_interface($interface, $family);
7227
	}
7228

    
7229
	/* compare against gateway groups */
7230
	$a_groups = return_gateway_groups_array(true);
7231
	if (is_array($a_groups[$interface])) {
7232
		/* we found a gateway group, fetch the interface or vip */
7233
		if (!empty($a_groups[$interface][0]['vip'])) {
7234
			return $a_groups[$interface][0]['vip'];
7235
		} else {
7236
			return $a_groups[$interface][0]['int'];
7237
		}
7238
	}
7239
	/* fall through to get_real_interface */
7240
	/* XXX: Really needed? */
7241
	return get_real_interface($interface, $family);
7242
}
7243

    
7244
/****f* interfaces/interface_has_dhcp
7245
 * NAME
7246
 *   interface_has_dhcp - determine if the interface or gateway group uses DHCP
7247
 * INPUTS
7248
 *   interface or gateway group name
7249
 *   family - 4 (check for IPv4 DHCP) or 6 (check for IPv6 DHCP)
7250
 * RESULT
7251
 *   true - if the interface uses DHCP/DHCP6, or the name is a gateway group which has any member that uses DHCP/DHCP6
7252
 *   false - otherwise (DHCP/DHCP6 not in use, or the name is not an interface or gateway group)
7253
 ******/
7254
function interface_has_dhcp($interface, $family = 4) {
7255
	$iface = config_get_path("interfaces/{$interface}");
7256
	if ($iface) {
7257
		if (($family == 4) && ($iface['ipaddr'] == "dhcp")) {
7258
			return true;
7259
		} elseif (($family == 6) && ($iface['ipaddrv6'] == "dhcp6")) {
7260
			return true;
7261
		} else {
7262
			return false;
7263
		}
7264
	}
7265

    
7266
	if ($family == 6) {
7267
		$dhcp_string = "_DHCP6";
7268
	} else {
7269
		$dhcp_string = "_DHCP";
7270
	}
7271

    
7272
	foreach (config_get_path('gateways/gateway_group', []) as $group) {
7273
		if (($group['name'] != $interface) || !is_array($group['item'])) {
7274
			continue;
7275
		}
7276
		foreach ($group['item'] as $item) {
7277
			$item_data = explode("|", $item);
7278
			if (substr($item_data[0], -strlen($dhcp_string)) == $dhcp_string) {
7279
				return true;
7280
			}
7281
		}
7282
	}
7283

    
7284
	return false;
7285
}
7286

    
7287
function remove_ifindex($ifname) {
7288
	return preg_replace("/[0-9]+$/", "", $ifname);
7289
}
7290

    
7291
function get_configured_vip_list_with_descr($family = 'all', $type = VIP_ALL) {
7292
	$interfaces = array();	// In case there are no VIPs defined
7293

    
7294
	$viplist = get_configured_vip_list($family, $type);
7295
	foreach ($viplist as $vipid => $address) {
7296
		$interfaces[$vipid] = $address;
7297
		if ($type = VIP_CARP) {
7298
			$vip = get_configured_vip($vipid);
7299
			if (isset($vip) && is_array($vip) ) {
7300
				$interfaces[$vipid] .= " - vhid {$vip['vhid']}";
7301
			}
7302
		}
7303
		if (get_vip_descr($address)) {
7304
			$interfaces[$vipid] .= " (" . get_vip_descr($address) . ")";
7305
		}
7306
	}
7307
	return $interfaces;
7308
}
7309

    
7310
function return_gateway_groups_array_with_descr() {
7311
	$interfaces = array();
7312
	$grouplist = return_gateway_groups_array();
7313
	foreach (array_keys($grouplist) as $name) {
7314
		$interfaces[$name] = "GW Group {$name}";
7315
	}
7316
	return $interfaces;
7317
}
7318

    
7319
function get_serial_ports($short=false) {
7320
	$linklist = array();
7321
	if (!is_dir("/var/spool/lock")) {
7322
		mwexec("/bin/mkdir -p /var/spool/lock");
7323
	}
7324
	$serialports = glob("/dev/cua[a-zA-Z][0-9]{,.[0-9],.[0-9][0-9],[0-9],[0-9].[0-9],[0-9].[0-9][0-9]}", GLOB_BRACE);
7325
	foreach ($serialports as $port) {
7326
		$port = trim($port);
7327
		$port = ($short) ? basename($port) : $port;
7328
		$linklist[$port] = $port;
7329
	}
7330
	return $linklist;
7331
}
7332

    
7333
function get_interface_ports() {
7334
	$linklist = array();
7335
	$portlist = get_interface_list();
7336

    
7337
	foreach (config_get_path('vlans/vlan', []) as $vlan) {
7338
		if (empty($vlan)) {
7339
			continue;
7340
		}
7341
		$portlist[$vlan['vlanif']] = $vlan;
7342
	}
7343

    
7344
	foreach (config_get_path('qinqs/qinqentry', []) as $qinq) {
7345
		if (empty($qinq)) {
7346
			continue;
7347
		}
7348
		$members = explode(" ", $qinq['members']);
7349
		foreach ($members as $mem) {
7350
			$qentry = $qinq['vlanif'] . "." . $mem;
7351
			$portlist[$qentry] = $qentry;
7352
		}
7353
	}
7354

    
7355
	foreach ($portlist as $ifn => $ifinfo) {
7356
		$string = "";
7357
		if (is_array($ifinfo)) {
7358
			$string .= $ifn;
7359
			if ($ifinfo['mac']) {
7360
				$string .= " ({$ifinfo['mac']})";
7361
			}
7362
			if ($ifinfo['friendly']) {
7363
				$string .= " - " . convert_friendly_interface_to_friendly_descr($ifinfo['friendly']);
7364
			} elseif ($ifinfo['descr']) {
7365
				$string .= " - {$ifinfo['descr']}";
7366
			}
7367
		} else {
7368
			$string .= $ifinfo;
7369
		}
7370

    
7371
		$linklist[$ifn] = $string;
7372
	}
7373
	return $linklist;
7374
}
7375

    
7376
function build_ppps_link_list() {
7377
	global $pconfig;
7378

    
7379
	$linklist = array('list' => array(), 'selected' => array());
7380

    
7381
	if ($pconfig['type'] == 'ppp') {
7382
		$linklist['list'] = get_serial_ports();
7383
	} else {
7384
		$iflist = get_interface_ports();
7385

    
7386
		$viplist = array();
7387
		$carplist = get_configured_vip_list_with_descr('all', VIP_CARP);
7388
		foreach ($carplist as $vid => $vaddr) {
7389
			$vip = get_configured_vip($vid);
7390
			$viplist[$vid] = "{$vaddr} (vhid: {$vip['vhid']})";
7391
		}
7392

    
7393
		$linklist['list'] = array_merge($iflist, $viplist);
7394

    
7395
		// The members of a LAGG cannot be assigned, used in VLANs, QinQ, or PPP.
7396
		$lagglist = get_lagg_interface_list();
7397
		foreach ($lagglist as $lagg) {
7398
			/* LAGG members cannot be assigned */
7399
			$laggmembers = explode(',', $lagg['members']);
7400
			foreach ($laggmembers as $lagm) {
7401
				if (isset($linklist['list'][$lagm])) {
7402
					unset($linklist['list'][$lagm]);
7403
				}
7404
			}
7405
		}
7406
	}
7407

    
7408
	$selected_ports = array();
7409
	if (is_array($pconfig['interfaces'])) {
7410
		$selected_ports = $pconfig['interfaces'];
7411
	} elseif (!empty($pconfig['interfaces'])) {
7412
		$selected_ports = explode(',', $pconfig['interfaces']);
7413
	}
7414
	foreach ($selected_ports as $port) {
7415
		if (isset($linklist['list'][$port])) {
7416
			array_push($linklist['selected'], $port);
7417
		}
7418
	}
7419
	return($linklist);
7420
}
7421

    
7422
function create_interface_list($open = false) {
7423
	$iflist = array();
7424

    
7425
	// add group interfaces
7426
	config_get_path('ifgroups/ifgroupentry', []);
7427
	foreach (config_get_path('ifgroups/ifgroupentry', []) as $ifgen) {
7428
		if ($open || have_ruleint_access($ifgen['ifname'])) {
7429
			$iflist[$ifgen['ifname']] = $ifgen['ifname'];
7430
		}
7431
	}
7432

    
7433
	foreach (get_configured_interface_with_descr() as $ifent => $ifdesc) {
7434
		if ($open || have_ruleint_access($ifent)) {
7435
			$iflist[$ifent] = $ifdesc;
7436
		}
7437
	}
7438

    
7439
	if (config_get_path('l2tp/mode', "") == "server" && ($open || have_ruleint_access("l2tp"))) {
7440
		$iflist['l2tp'] = gettext('L2TP VPN');
7441
	}
7442

    
7443
	if (is_pppoe_server_enabled() && ($open || have_ruleint_access("pppoe"))) {
7444
		$iflist['pppoe'] = gettext("PPPoE Server");
7445
	}
7446

    
7447
	// add ipsec interfaces
7448
	if (ipsec_enabled() && ($open || have_ruleint_access("enc0"))) {
7449
		$iflist["enc0"] = gettext("IPsec");
7450
	}
7451

    
7452
	// add openvpn/tun interfaces
7453
	if (config_get_path('openvpn/openvpn-server') || config_get_path('openvpn/openvpn-client')) {
7454
		$iflist["openvpn"] = gettext("OpenVPN");
7455
	}
7456

    
7457
	return($iflist);
7458
}
7459

    
7460
function is_pseudo_interface($inf, $tap=true) {
7461
	$psifs = array('ovpn', 'ipsec', 'l2tp', 'pptp', 'gif', 'gre', 'ppp', 'pppoe');
7462
	foreach ($psifs as $pif) {
7463
		if (substr($inf, 0, strlen($pif)) == $pif) {
7464
			if (($pif == 'ovpn') && $tap) {
7465
				preg_match('/ovpn([cs])([1-9]+)/', $inf, $m);
7466
				$type = ($m[1] == 'c') ? 'client' : 'server';
7467
				foreach (config_get_path("openvpn/openvpn-{$type}", []) as $ovpn) {
7468
					if (($ovpn['vpnid'] == $m[2]) && ($ovpn['dev_mode'] == 'tap')) {
7469
						return false;
7470
					} elseif ($ovpn['vpnid'] == $m[2]) {
7471
						return true;
7472
					}
7473
				}
7474
			} else {
7475
				return true;
7476
			}
7477
		}
7478
	}
7479
	return false;
7480
}
7481

    
7482
function is_stf_interface($inf) {
7483
	switch (config_get_path("interfaces/{$inf}/ipaddrv6")) {
7484
		case '6rd':
7485
		case '6to4':
7486
			return true;
7487
		default:
7488
			return false;
7489
	}
7490
}
7491

    
7492
function restart_interface_services($interface, $ipv6type = "") {
7493

    
7494
	services_unbound_configure(true, $interface);
7495

    
7496
	services_igmpproxy_configure($interface);
7497
	services_snmpd_configure($interface);
7498
	vpn_l2tp_configure($interface);
7499

    
7500
	if (substr(config_get_path("interfaces/{$interface}/if",""), 0, 4) != "ovpn") {
7501
		openvpn_resync_all($interface);
7502
	}
7503
	ipsec_force_reload($interface);
7504

    
7505
	/* restart RADVD to announce correct IPv6 prefix
7506
	 * see https://redmine.pfsense.org/issues/12604 */
7507
	if ((is_ipaddrv6($ipv6type) || ($ipv6type == "track6")) &&
7508
	    (config_get_path("dhcpdv6/{$interface}/ramode", "disabled") != "disabled")) {
7509
		services_radvd_configure();
7510
	}
7511

    
7512
	if (config_path_enabled("dhcpd/{$interface}") ||
7513
	    config_path_enabled("dhcpdv6/{$interface}")) {
7514
		services_dhcpd_configure();
7515
	}
7516

    
7517
	if (config_path_enabled('syslog') && ($interface == config_get_path('syslog/sourceip'))) {
7518
		system_syslogd_start();
7519
	}
7520
}
7521

    
7522
/**
7523
 * Return interface parameters and primary ipv4 and ipv6 addresses for the real iface
7524
 * $interface, identified by exclusion of VIPs. Deprecates pfSense_get_interface_addresses()
7525
 *
7526
 * Result array contains keyed values:
7527
 * - ipaddr: ipv4 address
7528
 * - subnetbits: ipv4 subnet bits
7529
 * - subnet: ipv4 subnet
7530
 * - broadcast: ipv4 broadcast addr (if applicable)
7531
 * - tunnel: ipv4 PPP endpoint (if applicable)
7532
 * - ipaddr6: ipv6 address
7533
 * - tentative: ipv6 tentative flag (if applicable)
7534
 * - subnetbits6: ipv6 subnet bits
7535
 * - tunnel6: ipv6 tunnel endpoint
7536
 * - status: up or down interface status
7537
 * - link0: per link layer defined flag
7538
 * - link1: per link layer defined flag
7539
 * - link2: per link layer defined flag
7540
 * - multicast: multicast support
7541
 * - loopback: interface is a loopback interface
7542
 * - pointtopoint: interface is point-to-point
7543
 * - promisc: interface in promisc mode
7544
 * - permanentpromisc: interface permanently in promisc mode
7545
 * - oactive: interface tx hardware queue is full
7546
 * - allmulti: interface receives all multicast packets
7547
 * - simplex: interface is simplex
7548
 * - linkstateup: interface link is up
7549
 * - iftype: wireless, ether, vlan, bridge, virtual, other
7550
 * - mtu: mtu of interface
7551
 * - caps: interface capabilities array
7552
 * - encaps: enabled capabilities array
7553
 * - macaddr: interface configured ethernet address
7554
 * - hwaddr: hardware ethernet address
7555
 */
7556
function get_interface_addresses($interface) {
7557
	$v4addrs = array();
7558
	$v6addrs = array();
7559
	$v4vips = array();
7560
	$v6vips = array();
7561
	$ifaddrs = pfSense_get_ifaddrs($interface);
7562

    
7563
	foreach (array_keys(get_configured_vip_list()) as $viface) {
7564
		$vip = get_configured_vip($viface);
7565
		if (is_ipaddrv4($vip['subnet'])) {
7566
			array_push($v4vips, $vip['subnet']);
7567
		} else if (is_ipaddrv6($vip['subnet'])) {
7568
			array_push($v6vips, Net_IPv6::Uncompress($vip['subnet']));
7569
		}
7570
	}
7571

    
7572
	if ($ifaddrs['addrs']) {
7573
		$v4addrs = array_filter($ifaddrs['addrs'], function($addr) use ($v4vips){
7574
			return (array_search($addr['addr'], $v4vips) === false);
7575
		});
7576
	}
7577

    
7578
	if ($ifaddrs['addrs6']) {
7579
		$v6addrs = array_filter($ifaddrs['addrs6'], function($addr) use ($v6vips){
7580
			return (array_search(Net_IPv6::Uncompress($addr['addr']), $v6vips) === false);
7581
		});
7582
	}
7583
	/* Transform output to conform to pfSense_get_interface_addresses() */
7584
	if ($v4addrs) {
7585
		$v4addr = array_shift($v4addrs);
7586
		$ifaddrs['ipaddr'] = $v4addr['addr'];
7587
		foreach(array("subnetbits", "subnet", "broadcast", "tunnel") as $key) {
7588
			if (array_key_exists($key, $v4addr)) {
7589
				$ifaddrs[$key] = $v4addr[$key];
7590
			}
7591
		}
7592
	}
7593

    
7594
	if ($v6addrs) {
7595
		// use the first IPv6 GUA address if one exists, otherwise the first address
7596
		$v6addr = $v6addrs[array_key_first($v6addrs)];
7597
		foreach ($v6addrs as $addr) {
7598
			if (is_v6gua($addr['addr'])) {
7599
				$v6addr = $addr;
7600
				break;
7601
			}
7602
		}
7603
		$ifaddrs['ipaddr6'] = $v6addr['addr'];
7604
		foreach(array("subnetbits", "tunnel") as $key) {
7605
			if (array_key_exists($key, $v6addr)) {
7606
				$ifaddrs[$key.'6'] = $v6addr[$key];
7607
			}
7608
		}
7609
		if (array_key_exists('tentative', $v6addr)) {
7610
			$ifaddrs['tentative'] = $v6addr['tentative'];
7611
		}
7612
	}
7613
	unset($ifaddrs['addrs']);
7614
	unset($ifaddrs['addrs6']);
7615

    
7616
	return($ifaddrs);
7617
}
7618

    
7619
/**
7620
 * Returns all interface addresses, including IPv6 LL addresses.
7621
 * XXX: Review/refactor functions used to retrieve interfaces addresses.
7622
 */
7623
function get_interface_addresses_all(string $interface): array {
7624
	$addresses = [];
7625

    
7626
	// Get primary IPv4/6 addresses
7627
	$addresses_primary = get_interface_addresses($interface);
7628
	if (!empty($addresses_primary['ipaddr'])) {
7629
		$addresses[] = $addresses_primary['ipaddr'];
7630
	}
7631
	if (!empty($addresses_primary['ipaddr6'])) {
7632
		$addresses[] = $addresses_primary['ipaddr6'];
7633
	}
7634

    
7635
	// Get IPv6 link-local address
7636
	$addresses_linklocal = find_interface_ipv6_ll($interface);
7637
	if (!empty($addresses_linklocal)) {
7638
		$addresses_linklocal_pos = strpos($addresses_linklocal, '%');
7639
		if ($addresses_linklocal_pos !== false) {
7640
			$addresses_linklocal = substr($addresses_linklocal, 0, $addresses_linklocal_pos);
7641
		}
7642
		$addresses[] = $addresses_linklocal;
7643
	}
7644

    
7645
	// Get VIPs
7646
	foreach (array_keys(get_configured_vip_list()) as $viface) {
7647
		$vip = get_configured_vip($viface);
7648
		if ($vip['interface'] == $interface && !empty($vip['subnet'])) {
7649
			$addresses[] = $vip['subnet'];
7650
		}
7651
	}
7652

    
7653
	return $addresses;
7654
}
7655
?>
(22-22/61)