commit 82bcf46cc0246c67becbd6b5065b2771bf324986
Author: Steve Beaver <sbeaver@netgate.com>
Date:   Mon Oct 26 15:32:46 2020 -0400

    Outlined Wireguard GUI pages and added it to the VPN menu

diff --git a/src/usr/local/www/head.inc b/src/usr/local/www/head.inc
index 01d2aa8507..582a2e38d2 100644
--- a/src/usr/local/www/head.inc
+++ b/src/usr/local/www/head.inc
@@ -347,6 +347,7 @@ $vpn_menu[] = array(gettext("IPsec"), "/vpn_ipsec.php");
 $vpn_menu[] = array(gettext("OpenVPN"), "/vpn_openvpn_server.php");
 //$vpn_menu[] = array(gettext("PPTP"), "/vpn_pptp.php");
 $vpn_menu[] = array(gettext("L2TP"), "/vpn_l2tp.php");
+$vpn_menu[] = array(gettext("Wireguard"), "/vpn_wg.php");
 $vpn_menu = msort(array_merge($vpn_menu, return_ext_menu("VPN")), 0);
 
 // Status
diff --git a/src/usr/local/www/vpn_wg.php b/src/usr/local/www/vpn_wg.php
new file mode 100644
index 0000000000..2f09068134
--- /dev/null
+++ b/src/usr/local/www/vpn_wg.php
@@ -0,0 +1,639 @@
+<?php
+/*
+ * vpn_ipsec.php
+ *
+ * part of pfSense (https://www.pfsense.org)
+ * Copyright (c) 2004-2013 BSD Perimeter
+ * Copyright (c) 2013-2016 Electric Sheep Fencing
+ * Copyright (c) 2014-2020 Rubicon Communications, LLC (Netgate)
+ * All rights reserved.
+ *
+ * originally based on m0n0wall (http://m0n0.ch/wall)
+ * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+##|+PRIV
+##|*IDENT=page-vpn-ipsec
+##|*NAME=VPN: IPsec
+##|*DESCR=Allow access to the 'VPN: IPsec' page.
+##|*MATCH=vpn_ipsec.php*
+##|-PRIV
+
+require_once("guiconfig.inc");
+require_once("functions.inc");
+require_once("filter.inc");
+require_once("shaper.inc");
+require_once("ipsec.inc");
+require_once("vpn.inc");
+
+init_config_arr(array('ipsec', 'phase1'));
+init_config_arr(array('ipsec', 'phase2'));
+$a_phase1 = &$config['ipsec']['phase1'];
+$a_phase2 = &$config['ipsec']['phase2'];
+
+
+if ($_POST['apply']) {
+	$ipsec_dynamic_hosts = ipsec_configure();
+	ipsec_reload_package_hook();
+	/* reload the filter in the background */
+	$retval = 0;
+	$retval |= filter_configure();
+	if ($ipsec_dynamic_hosts >= 0) {
+		if (is_subsystem_dirty('ipsec')) {
+			clear_subsystem_dirty('ipsec');
+		}
+	}
+} else if (isset($_POST['del'])) {
+	/* delete selected p1 entries */
+	if (is_array($_POST['p1entry']) && count($_POST['p1entry'])) {
+		foreach ($_POST['p1entry'] as $p1entrydel) {
+			unset($a_phase1[$p1entrydel]);
+		}
+		if (write_config(gettext("Deleted selected IPsec Phase 1 entries."))) {
+			mark_subsystem_dirty('ipsec');
+		}
+	}
+} else if (isset($_POST['delp2'])) {
+	/* delete selected p2 entries */
+	if (is_array($_POST['p2entry']) && count($_POST['p2entry'])) {
+		foreach ($_POST['p2entry'] as $p2entrydel) {
+			if (is_interface_ipsec_vti_assigned($a_phase2[$p2entrydel]) && ($a_phase2[$p2entrydel]['mode'] == 'vti')) {
+				$input_errors[] = gettext("Cannot delete a VTI Phase 2 while the interface is assigned. Remove the interface assignment before deleting this P2.");
+			} else {
+				ipsec_del_vtimap($a_phase2[$p2entrydel]);
+				unset($a_phase2[$p2entrydel]);
+			}
+		}
+		if (write_config(gettext("Deleted selected IPsec Phase 2 entries."))) {
+			mark_subsystem_dirty('ipsec');
+		}
+	}
+} else  {
+	/* yuck - IE won't send value attributes for image buttons, while Mozilla does - so we use .x/.y to find move button clicks instead... */
+
+	// TODO: this. is. nasty.
+	unset($delbtn, $delbtnp2, $movebtn, $movebtnp2, $togglebtn, $togglebtnp2);
+	foreach ($_POST as $pn => $pd) {
+		if (preg_match("/del_(\d+)/", $pn, $matches)) {
+			$delbtn = $matches[1];
+		} else if (preg_match("/delp2_(\d+)/", $pn, $matches)) {
+			$delbtnp2 = $matches[1];
+		} else if (preg_match("/move_(\d+)/", $pn, $matches)) {
+			$movebtn = $matches[1];
+		} else if (preg_match("/movep2_(\d+)/", $pn, $matches)) {
+			$movebtnp2 = $matches[1];
+		} else if (preg_match("/toggle_(\d+)/", $pn, $matches)) {
+			$togglebtn = $matches[1];
+		} else if (preg_match("/togglep2_(\d+)/", $pn, $matches)) {
+			$togglebtnp2 = $matches[1];
+		}
+	}
+
+	$save = 1;
+
+	/* move selected p1 entries before this */
+	if (isset($movebtn) && is_array($_POST['p1entry']) && count($_POST['p1entry'])) {
+		$a_phase1_new = array();
+
+		/* copy all p1 entries < $movebtn and not selected */
+		for ($i = 0; $i < $movebtn; $i++) {
+			if (!in_array($i, $_POST['p1entry'])) {
+				$a_phase1_new[] = $a_phase1[$i];
+			}
+		}
+
+		/* copy all selected p1 entries */
+		for ($i = 0; $i < count($a_phase1); $i++) {
+			if ($i == $movebtn) {
+				continue;
+			}
+			if (in_array($i, $_POST['p1entry'])) {
+				$a_phase1_new[] = $a_phase1[$i];
+			}
+		}
+
+		/* copy $movebtn p1 entry */
+		if ($movebtn < count($a_phase1)) {
+			$a_phase1_new[] = $a_phase1[$movebtn];
+		}
+
+		/* copy all p1 entries > $movebtn and not selected */
+		for ($i = $movebtn+1; $i < count($a_phase1); $i++) {
+			if (!in_array($i, $_POST['p1entry'])) {
+				$a_phase1_new[] = $a_phase1[$i];
+			}
+		}
+		if (count($a_phase1_new) > 0) {
+			$a_phase1 = $a_phase1_new;
+		}
+
+	} else if (isset($movebtnp2) && is_array($_POST['p2entry']) && count($_POST['p2entry'])) {
+		/* move selected p2 entries before this */
+		$a_phase2_new = array();
+
+		/* copy all p2 entries < $movebtnp2 and not selected */
+		for ($i = 0; $i < $movebtnp2; $i++) {
+			if (!in_array($i, $_POST['p2entry'])) {
+				$a_phase2_new[] = $a_phase2[$i];
+			}
+		}
+
+		/* copy all selected p2 entries */
+		for ($i = 0; $i < count($a_phase2); $i++) {
+			if ($i == $movebtnp2) {
+				continue;
+			}
+			if (in_array($i, $_POST['p2entry'])) {
+				$a_phase2_new[] = $a_phase2[$i];
+			}
+		}
+
+		/* copy $movebtnp2 p2 entry */
+		if ($movebtnp2 < count($a_phase2)) {
+			$a_phase2_new[] = $a_phase2[$movebtnp2];
+		}
+
+		/* copy all p2 entries > $movebtnp2 and not selected */
+		for ($i = $movebtnp2+1; $i < count($a_phase2); $i++) {
+			if (!in_array($i, $_POST['p2entry'])) {
+				$a_phase2_new[] = $a_phase2[$i];
+			}
+		}
+		if (count($a_phase2_new) > 0) {
+			$a_phase2 = $a_phase2_new;
+		}
+
+	} else if (isset($togglebtn)) {
+		if (isset($a_phase1[$togglebtn]['disabled'])) {
+			unset($a_phase1[$togglebtn]['disabled']);
+		} else {
+			if (ipsec_vti($a_phase1[$togglebtn], false, false)) {
+				$input_errors[] = gettext("Cannot disable a Phase 1 with a child Phase 2 while the interface is assigned. Remove the interface assignment before disabling this P2.");
+			} else {
+				$a_phase1[$togglebtn]['disabled'] = true;
+			}
+		}
+	} else if (isset($togglebtnp2)) {
+		if (isset($a_phase2[$togglebtnp2]['disabled'])) {
+			unset($a_phase2[$togglebtnp2]['disabled']);
+		} else {
+			if (is_interface_ipsec_vti_assigned($a_phase2[$togglebtnp2]) && ($a_phase2[$togglebtnp2]['mode'] == 'vti')) {
+				$input_errors[] = gettext("Cannot disable a VTI Phase 2 while the interface is assigned. Remove the interface assignment before disabling this P2.");
+			} else {
+				$a_phase2[$togglebtnp2]['disabled'] = true;
+			}
+		}
+	} else if (isset($delbtn)) {
+		/* remove static route if interface is not WAN */
+		if ($a_phase1[$delbtn]['interface'] <> "wan") {
+			route_del($a_phase1[$delbtn]['remote-gateway']);
+		}
+
+		/* remove all phase2 entries that match the ikeid */
+		$ikeid = $a_phase1[$delbtn]['ikeid'];
+		$p1_has_vti = false;
+		$delp2ids = array();
+		foreach ($a_phase2 as $p2index => $ph2tmp) {
+			if ($ph2tmp['ikeid'] == $ikeid) {
+				if (is_interface_ipsec_vti_assigned($ph2tmp)) {
+					$p1_has_vti = true;
+				} else {
+					$delp2ids[] = $p2index;
+				}
+			}
+		}
+
+		if ($p1_has_vti) {
+			$input_errors[] = gettext("Cannot delete a Phase 1 which contains an active VTI Phase 2 with an interface assigned. Remove the interface assignment before deleting this P1.");
+		} else {
+			foreach ($delp2ids as $dp2idx) {
+				ipsec_del_vtimap($a_phase2[$dp2idx]);
+				unset($a_phase2[$dp2idx]);
+			}
+			unset($a_phase1[$delbtn]);
+		}
+
+	} else if (isset($delbtnp2)) {
+		if (is_interface_ipsec_vti_assigned($a_phase2[$delbtnp2]) && ($a_phase2[$delbtnp2]['mode'] == 'vti')) {
+			$input_errors[] = gettext("Cannot delete a VTI Phase 2 while the interface is assigned. Remove the interface assignment before deleting this P2.");
+		} else {
+			ipsec_del_vtimap($a_phase2[$delbtnp2]);
+			unset($a_phase2[$delbtnp2]);
+		}
+	} else {
+		$save = 0;
+	}
+
+	if ($save === 1) {
+		if (write_config(gettext("Saved configuration changes for IPsec tunnels."))) {
+			mark_subsystem_dirty('ipsec');
+		}
+	}
+}
+
+
+$pgtitle = array(gettext("VPN"), gettext("IPsec"), gettext("Tunnels"));
+$pglinks = array("", "@self", "@self");
+$shortcut_section = "ipsec";
+
+include("head.inc");
+
+if ($input_errors) {
+	print_input_errors($input_errors);
+}
+
+$tab_array = array();
+$tab_array[] = array(gettext("Tunnels"), true, "vpn_ipsec.php");
+$tab_array[] = array(gettext("Mobile Clients"), false, "vpn_ipsec_mobile.php");
+$tab_array[] = array(gettext("Pre-Shared Keys"), false, "vpn_ipsec_keys.php");
+$tab_array[] = array(gettext("Advanced Settings"), false, "vpn_ipsec_settings.php");
+display_top_tabs($tab_array);
+
+if ($_POST['apply']) {
+	print_apply_result_box($retval);
+}
+
+if (is_subsystem_dirty('ipsec')) {
+	print_apply_box(gettext("The IPsec tunnel configuration has been changed.") . "<br />" . gettext("The changes must be applied for them to take effect."));
+}
+?>
+
+<form name="mainform" method="post">
+	<div class="panel panel-default">
+		<div class="panel-heading"><h2 class="panel-title"><?=gettext('IPsec Tunnels')?></h2></div>
+		<div class="panel-body table-responsive">
+			<table class="table table-striped table-hover">
+				<thead>
+					<tr>
+						<th>&nbsp;</th>
+						<th>&nbsp;</th>
+						<th><?=gettext("IKE")?></th>
+						<th><?=gettext("Remote Gateway")?></th>
+						<th><?=gettext("Mode")?></th>
+						<th><?=gettext("P1 Protocol")?></th>
+						<th><?=gettext("P1 Transforms")?></th>
+						<th><?=gettext("P1 DH-Group")?></th>
+						<th><?=gettext("P1 Description")?></th>
+						<th><?=gettext("Actions")?></th>
+					</tr>
+				</thead>
+				<tbody class="p1-entries">
+<?php
+$iflabels = get_configured_interface_with_descr(false, true);
+$viplist = get_configured_vip_list();
+foreach ($viplist as $vip => $address) {
+	$iflabels[$vip] = $address;
+	if (get_vip_descr($address)) {
+		$iflabels[$vip] .= " (". get_vip_descr($address) .")";
+	}
+}
+$grouplist = return_gateway_groups_array();
+foreach ($grouplist as $name => $group) {
+	if ($group[0]['vip'] != "") {
+		$vipif = $group[0]['vip'];
+	} else {
+		$vipif = $group[0]['int'];
+	}
+	$iflabels[$name] = "GW Group {$name}";
+}
+
+$i = 0; foreach ($a_phase1 as $ph1ent):
+
+	$iconfn = "pass";
+
+	$entryStatus = (isset($ph1ent['disabled']) ? 'disabled' : 'enabled');
+
+	if ($entryStatus == 'disabled') {
+		$iconfn .= "_d";
+	}
+?>
+					<tr id="fr<?=$i?>" onclick="fr_toggle(<?=$i?>)" id="frd<?=$i?>" ondblclick="document.location='vpn_ipsec_phase1.php?p1index=<?=$i?>'" class="<?= $entryStatus ?>">
+						<td>
+							<input type="checkbox" id="frc<?=$i?>" onclick="fr_toggle(<?=$i?>)" name="p1entry[]" value="<?=$i?>"  />
+							<a	class="fa fa-anchor icon-pointer" id="Xmove_<?=$i?>" title="<?=gettext("Move checked entries to here")?>"></a>
+						</td>
+						<td>
+							<button value="toggle_<?=$i?>" name="toggle_<?=$i?>" title="<?=gettext("click to toggle enabled/disabled status")?>" class="btn btn-xs btn-<?= ($entryStatus == 'disabled' ? 'success' : 'warning') ?>" type="submit"><?= ($entryStatus == 'disabled' ? 'Enable' : 'Disable') ?></button>
+						</td>
+						<td id="frd<?=$i?>">
+<?php
+			if (empty($ph1ent['iketype']) || $ph1ent['iketype'] == "ikev1") {
+				echo "V1";
+			} elseif ($ph1ent['iketype'] == "ikev2") {
+				echo "V2";
+			} elseif ($ph1ent['iketype'] == "auto") {
+				echo "Auto";
+			}
+?>
+						</td>
+						<td>
+<?php
+			if ($ph1ent['interface']) {
+				if (isset($iflabels[$ph1ent['interface']])) {
+					$if = htmlspecialchars($iflabels[$ph1ent['interface']]);
+				} else {
+					$if = sprintf("Interface not found: '%s'", $ph1ent['interface']);
+				}
+			} else {
+				$if = "WAN";
+			}
+
+			if (!isset($ph1ent['mobile'])) {
+				echo $if."<br />".$ph1ent['remote-gateway'];
+			} else {
+				echo $if."<br /><strong>" . gettext("Mobile Client") . "</strong>";
+			}
+?>
+						</td>
+						<td id="frd<?=$i?>">
+					<?=$spans?>
+					<?php
+					if (empty($ph1ent['iketype']) || $ph1ent['iketype'] == "ikev1" || $ph1ent['iketype'] == "auto") {
+						echo "{$ph1ent['mode']}";
+					}
+					?>
+					<?=$spane?>
+				</td>
+				<td id="frd<?=$i?>">
+<?php
+				$first = true;
+				if (is_array($ph1ent['encryption']['item'])) {
+					foreach($ph1ent['encryption']['item'] as $p1algo) {
+						if (!$first) {
+							echo "<br/>";
+						}
+						echo $p1_ealgos[$p1algo['encryption-algorithm']['name']]['name'];
+						if ($p1algo['encryption-algorithm']['keylen']) {
+							echo " ({$p1algo['encryption-algorithm']['keylen']} " . gettext("bits") . ")";
+						}
+						$first = false;
+					}
+				}
+?>
+						</td>
+						<td>
+<?php			$first = true;
+				if (is_array($ph1ent['encryption']['item'])) {
+					foreach($ph1ent['encryption']['item'] as $p1algo) {
+						if (!$first) {
+							echo "<br/>";
+						}
+						echo $p1_halgos[$p1algo['hash-algorithm']];
+						if (isset($ph1ent['prfselect_enable'])) {
+							echo " / PRF" . $p1_halgos[$p1algo['prf-algorithm']];
+						}
+						$first = false;
+					}
+				}
+				?>
+						</td>
+						<td>
+<?php			$first = true;
+				if (is_array($ph1ent['encryption']['item'])) {
+					foreach($ph1ent['encryption']['item'] as $p1algo) {
+						if (!$first) {
+							echo "<br/>";
+						}
+						echo str_replace(" ","&nbsp;",$p1_dhgroups[$p1algo['dhgroup']]);
+						$first = false;
+					}
+				}
+				?>
+						</td>
+						<td>
+							<?=htmlspecialchars($ph1ent['descr'])?>
+						</td>
+						<td style="cursor: pointer;">
+<!--							<a	class="fa fa-anchor" id="Xmove_<?=$i?>" title="<?=gettext("Move checked entries to here")?>"></a> -->
+							<button style="display: none;" class="btn btn-default btn-xs" type="submit" id="move_<?=$i?>" name="move_<?=$i?>" value="move_<?=$i?>"><?=gettext("Move checked entries to here")?></button>
+							<a class="fa fa-pencil" href="vpn_ipsec_phase1.php?p1index=<?=$i?>" title="<?=gettext("Edit phase1 entry"); ?>"></a>
+<?php if (!isset($ph1ent['mobile'])): ?>
+							<a class="fa fa-clone" href="vpn_ipsec_phase1.php?dup=<?=$i?>" title="<?=gettext("Copy phase1 entry"); ?>"></a>
+<?php endif; ?>
+							<a	class="fa fa-trash no-confirm" id="Xdel_<?=$i?>" title="<?=gettext('Delete phase1 entry'); ?>"></a>
+							<button style="display: none;" class="btn btn-xs btn-warning" type="submit" id="del_<?=$i?>" name="del_<?=$i?>" value="del_<?=$i?>" title="<?=gettext('Delete phase1 entry'); ?>">delete</button>
+
+						</td>
+					</tr>
+					<tr class="<?= $entryStatus ?>">
+						<td colspan="2"></td>
+						<td colspan="7" class="contains-table">
+<?php
+			if (isset($_REQUEST["tdph2-{$i}-visible"])) {
+				$tdph2_visible = htmlspecialchars($_REQUEST["tdph2-{$i}-visible"]);
+			} else {
+				$tdph2_visible = 0;
+			}
+?>
+							<input type="hidden" name="tdph2-<?=$i?>-visible" id="tdph2-<?=$i?>-visible" value="<?=$tdph2_visible?>" />
+							<div id="shph2but-<?=$i?>" <?=($tdph2_visible == '1' ? 'style="display:none"' : '')?>>
+<?php
+				$phase2count=0;
+
+				foreach ($a_phase2 as $ph2ent) {
+					if ($ph2ent['ikeid'] != $ph1ent['ikeid']) {
+						continue;
+					}
+					$phase2count++;
+				}
+				$fr_prefix = "frp2{$i}";
+				$fr_header = $fr_prefix . "header";
+?>
+								<button class="btn btn-info" type="button" onclick="show_phase2('tdph2-<?=$i?>','shph2but-<?=$i?>')" value="+"><i class="fa fa-plus-circle"></i> <?php printf(gettext("Show Phase 2 Entries (%s)"), $phase2count); ?></button>
+							</div>
+							<div id="tdph2-<?=$i?>" <?=($tdph2_visible != '1' ? 'style="display:none"' : '')?>>
+								<table class="table table-striped table-hover">
+									<thead>
+										<tr>
+											<th>&nbsp;</th>
+											<th>&nbsp;</th>
+											<th><?=gettext("Mode"); ?></th>
+											<th><?=gettext("Local Subnet"); ?></th>
+											<th><?=gettext("Remote Subnet"); ?></th>
+											<th><?=gettext("P2 Protocol"); ?></th>
+											<th><?=gettext("P2 Transforms"); ?></th>
+											<th><?=gettext("P2 Auth Methods"); ?></th>
+											<th><?=gettext("P2 actions")?></th>
+										</tr>
+									</thead>
+									<tbody class="p2-entries">
+<?php $j = 0; foreach ($a_phase2 as $ph2index => $ph2ent): ?>
+<?php
+						if ($ph2ent['ikeid'] != $ph1ent['ikeid']) {
+							continue;
+						}
+
+						$fr_c = $fr_prefix . "c" . $j;
+						$fr_d = $fr_prefix . "d" . $j;
+
+						$iconfn = "pass";
+						$entryStatus = (isset($ph2ent['disabled']) || isset($ph1ent['disabled']) ? 'disabled' : 'enabled');
+
+						if ($entryStatus == 'disabled') {
+							$iconfn .= "_d";
+						}
+?>
+										<tr id="<?=$fr_prefix . $j?>" ondblclick="document.location='vpn_ipsec_phase2.php?p2index=<?=$ph2ent['uniqid']?>'" class="<?= $entryStatus ?>">
+											<td>
+												<input type="checkbox" id="<?=$fr_c?>" name="p2entry[]" value="<?=$ph2index?>" onclick="fr_bgcolor('<?=$j?>', '<?=$fr_prefix?>')" />
+												<button class="fa fa-anchor button-icon" type="submit" name="movep2_<?=$j?>" value="movep2_<?=$j?>" title="<?=gettext("Move checked P2s here")?>"></button>
+											</td>
+											<td>
+												<button value="togglep2_<?=$ph2index?>" name="togglep2_<?=$ph2index?>" title="<?=gettext("click to toggle enabled/disabled status")?>" class="btn btn-xs btn-<?= ($entryStatus == 'disabled'? 'success' : 'warning') ?>" type="submit"><?= ($entryStatus == 'disabled'? 'Enable' : 'Disable') ?></button>
+											</td>
+											<td id="<?=$fr_d?>" onclick="fr_toggle('<?=$j?>', '<?=$fr_prefix?>')">
+												<?=$ph2ent['mode']?>
+											</td>
+<?php if (($ph2ent['mode'] == "tunnel") or ($ph2ent['mode'] == "tunnel6") or ($ph2ent['mode'] == "vti")): ?>
+											<td id="<?=$fr_d?>" onclick="fr_toggle('<?=$j?>', '<?=$fr_prefix?>')">
+												<?=ipsec_idinfo_to_text($ph2ent['localid']); ?>
+											</td>
+											<td id="<?=$fr_d?>" onclick="fr_toggle('<?=$j?>', '<?=$fr_prefix?>')">
+												<?=ipsec_idinfo_to_text($ph2ent['remoteid']); ?>
+											</td>
+		<?php else: ?>
+											<td colspan="2"></td>
+<?php endif; ?>
+											<td id="<?=$fr_d?>" onclick="fr_toggle('<?=$j?>', '<?=$fr_prefix?>')">
+												<?=$p2_protos[$ph2ent['protocol']]; ?>
+											</td>
+											<td id="<?=$fr_d?>" onclick="fr_toggle('<?=$j?>', '<?=$fr_prefix?>')">
+<?php
+								foreach ($ph2ent['encryption-algorithm-option'] as $k => $ph2ea) {
+									if ($k) {
+										echo ", ";
+									}
+									echo $p2_ealgos[$ph2ea['name']]['name'];
+									if ($ph2ea['keylen']) {
+										if ($ph2ea['keylen'] == "auto") {
+											echo " (" . gettext("auto") . ")";
+										} else {
+											echo " ({$ph2ea['keylen']} " . gettext("bits") . ")";
+										}
+									}
+								}
+?>
+											</td>
+											<td id="<?=$fr_d?>" onclick="fr_toggle('<?=$j?>', '<?=$fr_prefix?>')">
+<?php
+								if (!empty($ph2ent['hash-algorithm-option']) && is_array($ph2ent['hash-algorithm-option'])) {
+									foreach ($ph2ent['hash-algorithm-option'] as $k => $ph2ha) {
+										if ($k) {
+											echo ", ";
+										}
+										echo $p2_halgos[$ph2ha];
+									}
+								}
+?>
+											</td>
+											<td style="cursor: pointer;">
+<!--												<button class="fa fa-anchor button-icon" type="submit" name="movep2_<?=$j?>" value="movep2_<?=$j?>" title="<?=gettext("Move checked P2s here")?>"></button> -->
+												<a class="fa fa-pencil" href="vpn_ipsec_phase2.php?p2index=<?=$ph2ent['uniqid']?>" title="<?=gettext("Edit phase2 entry"); ?>"></a>
+												<a class="fa fa-clone" href="vpn_ipsec_phase2.php?dup=<?=$ph2ent['uniqid']?>" title="<?=gettext("Add a new Phase 2 based on this one"); ?>"></a>
+												<a	class="fa fa-trash no-confirm" id="Xdelp2_<?=$ph2index?>" title="<?=gettext('Delete phase2 entry'); ?>"></a>
+												<button style="display: none;" class="btn btn-xs btn-warning" type="submit" id="delp2_<?=$ph2index?>" name="delp2_<?=$ph2index?>" value="delp2_<?=$ph2index?>" title="<?=gettext('delete phase2 entry'); ?>">delete</button>
+											</td>
+										</tr>
+<?php $j++; endforeach; ?>
+										<tr>
+											<td></td>
+											<td>
+												<a class="btn btn-xs btn-success" href="vpn_ipsec_phase2.php?ikeid=<?=$ph1ent['ikeid']?><?php if (isset($ph1ent['mobile'])) echo "&amp;mobile=true"?>">
+													<i class="fa fa-plus icon-embed-btn"></i>
+													<?=gettext("Add P2")?>
+												</a>
+											</td>
+											<td colspan="7"></td>
+										</tr>
+									</tbody>
+								</table>
+							</div>
+						</td>
+					</tr>
+<?php
+					$i++;
+				endforeach;	 // $a_phase1 as $ph1ent
+?>
+				</tbody>
+			</table>
+		</div>
+	</div>
+
+	<nav class="action-buttons">
+<?php
+/*
+	if ($i !== 0): ?>
+	<input type="submit" name="move_<?=$i?>" class="btn btn-default" value="<?=gettext("move selected phase1 entries to end")?>" />
+<?php endif;
+*/
+?>
+		<a href="vpn_ipsec_phase1.php" class="btn btn-success btn-sm"  usepost>
+			<i class="fa fa-plus icon-embed-btn"></i>
+			<?=gettext("Add P1")?>
+		</a>
+<?php if ($i !== 0): ?>
+		<button type="submit" name="del" class="btn btn-danger btn-sm" value="<?=gettext("Delete selected P1s")?>">
+			<i class="fa fa-trash icon-embed-btn"></i>
+			<?=gettext("Delete P1s")?>
+		</button>
+<?php endif; ?>
+	</nav>
+</form>
+
+<div class="infoblock">
+	<?php print_info_box(sprintf(gettext('The IPsec status can be checked at %1$s%2$s%3$s.'), '<a href="status_ipsec.php">', gettext("Status:IPsec"), '</a>') . '<br />' .
+	sprintf(gettext('IPsec debug mode can be enabled at %1$s%2$s%3$s.'), '<a href="vpn_ipsec_settings.php">', gettext("VPN:IPsec:Advanced Settings"), '</a>') . '<br />' .
+	sprintf(gettext('IPsec can be set to prefer older SAs at %1$s%2$s%3$s.'), '<a href="vpn_ipsec_settings.php">', gettext("VPN:IPsec:Advanced Settings"), '</a>'), 'info', false); ?>
+</div>
+
+<script type="text/javascript">
+//<![CDATA[
+function show_phase2(id, buttonid) {
+	document.getElementById(buttonid).innerHTML='';
+	document.getElementById(id).style.display = "block";
+	var visible = id + '-visible';
+	document.getElementById(visible).value = "1";
+}
+
+events.push(function() {
+	$('[id^=Xmove_]').click(function (event) {
+		// ToDo: We POST shift="yes" if the user has the shift key depressed, but that is not yet used
+		// by the $_POST code. It is intended to allow the user to choose to move stuff to the row before or
+		// after the clicked anchor icon
+		if (event.shiftKey) {
+			$('form').append('<input type="hidden" id="shift" name="shift" value="yes" />');
+		}
+
+		$('#' + event.target.id.slice(1)).click();
+	});
+
+	$('[id^=Xdel_]').click(function (event) {
+		if (confirm("<?=gettext('Confirmation required to delete this P1 entry.')?>")) {
+			$('#' + event.target.id.slice(1)).click();
+		}
+	});
+
+	$('[id^=Xdelp2_]').click(function (event) {
+		if (confirm("<?=gettext('Confirmation required to delete this P2 entry.')?>")) {
+			$('#' + event.target.id.slice(1)).click();
+		}
+	});
+});
+//]]>
+</script>
+
+<?php
+include("foot.inc");
diff --git a/src/usr/local/www/vpn_wg_edit.php b/src/usr/local/www/vpn_wg_edit.php
new file mode 100644
index 0000000000..4ddb5f7caa
--- /dev/null
+++ b/src/usr/local/www/vpn_wg_edit.php
@@ -0,0 +1,1437 @@
+<?php
+
+/*
+ * vpn_ipsec_phase1.php
+ *
+ * part of pfSense (https://www.pfsense.org)
+ * Copyright (c) 2004-2013 BSD Perimeter
+ * Copyright (c) 2013-2016 Electric Sheep Fencing
+ * Copyright (c) 2014-2020 Rubicon Communications, LLC (Netgate)
+ * Copyright (c) 2008 Shrew Soft Inc
+ * All rights reserved.
+ *
+ * originally based on m0n0wall (http://m0n0.ch/wall)
+ * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+##|+PRIV
+##|*IDENT=page-vpn-ipsec-editphase1
+##|*NAME=VPN: IPsec: Edit Phase 1
+##|*DESCR=Allow access to the 'VPN: IPsec: Edit Phase 1' page.
+##|*MATCH=vpn_ipsec_phase1.php*
+##|-PRIV
+
+require_once("functions.inc");
+require_once("guiconfig.inc");
+require_once("ipsec.inc");
+require_once("vpn.inc");
+require_once("filter.inc");
+
+if ($_REQUEST['generatekey']) {
+	$keyoutput = "";
+	$keystatus = "";
+	exec("/bin/dd status=none if=/dev/random bs=4096 count=1 | /usr/bin/openssl sha224 | /usr/bin/cut -f2 -d' '", $keyoutput, $keystatus);
+	print json_encode(['pskey' => $keyoutput[0]]);
+	exit;
+}
+
+init_config_arr(array('ipsec', 'phase1'));
+init_config_arr(array('ipsec', 'phase2'));
+init_config_arr(array('ipsec', 'vtimaps', 'item'));
+$a_phase1 = &$config['ipsec']['phase1'];
+$a_phase2 = &$config['ipsec']['phase2'];
+$a_vtimaps = &$config['ipsec']['vtimaps']['item'];
+
+if (is_numericint($_REQUEST['p1index'])) {
+	$p1index = $_REQUEST['p1index'];
+}
+
+if (is_numericint($_REQUEST['dup'])) {
+	$p1index = $_REQUEST['dup'];
+}
+
+if (isset($p1index) && $a_phase1[$p1index]) {
+	// don't copy the ikeid on dup
+	if (!isset($_REQUEST['dup']) || !is_numericint($_REQUEST['dup'])) {
+		$pconfig['ikeid'] = $a_phase1[$p1index]['ikeid'];
+	}
+
+	$old_ph1ent = $a_phase1[$p1index];
+
+	$pconfig['disabled'] = isset($a_phase1[$p1index]['disabled']);
+
+	if ($a_phase1[$p1index]['interface']) {
+		$pconfig['interface'] = $a_phase1[$p1index]['interface'];
+	} else {
+		$pconfig['interface'] = "wan";
+	}
+
+	list($pconfig['remotenet'], $pconfig['remotebits']) = explode("/", $a_phase1[$p1index]['remote-subnet']);
+
+	if (isset($a_phase1[$p1index]['mobile'])) {
+		$pconfig['mobile'] = 'true';
+	} else {
+		$pconfig['remotegw'] = $a_phase1[$p1index]['remote-gateway'];
+		$pconfig['ikeport'] = $a_phase1[$p1index]['ikeport'];
+		$pconfig['nattport'] = $a_phase1[$p1index]['nattport'];
+	}
+
+	if (empty($a_phase1[$p1index]['iketype'])) {
+		$pconfig['iketype'] = "ikev1";
+	} else {
+		$pconfig['iketype'] = $a_phase1[$p1index]['iketype'];
+	}
+	$pconfig['mode'] = $a_phase1[$p1index]['mode'];
+	$pconfig['protocol'] = $a_phase1[$p1index]['protocol'];
+	$pconfig['myid_type'] = $a_phase1[$p1index]['myid_type'];
+	$pconfig['myid_data'] = $a_phase1[$p1index]['myid_data'];
+	$pconfig['peerid_type'] = $a_phase1[$p1index]['peerid_type'];
+	$pconfig['peerid_data'] = $a_phase1[$p1index]['peerid_data'];
+	$pconfig['encryption'] = $a_phase1[$p1index]['encryption'];
+	$pconfig['rekey_time'] = $a_phase1[$p1index]['rekey_time'];
+	$pconfig['reauth_time'] = $a_phase1[$p1index]['reauth_time'];
+	$pconfig['over_time'] = $a_phase1[$p1index]['over_time'];
+	$pconfig['authentication_method'] = $a_phase1[$p1index]['authentication_method'];
+
+	if (($pconfig['authentication_method'] == "pre_shared_key") ||
+	    ($pconfig['authentication_method'] == "xauth_psk_server")) {
+		$pconfig['pskey'] = $a_phase1[$p1index]['pre-shared-key'];
+	} else {
+		$pconfig['pkcs11certref'] = $a_phase1[$p1index]['pkcs11certref'];
+		$pconfig['pkcs11pin'] = $a_phase1[$p1index]['pkcs11pin'];
+		$pconfig['certref'] = $a_phase1[$p1index]['certref'];
+		$pconfig['caref'] = $a_phase1[$p1index]['caref'];
+	}
+
+	$pconfig['descr'] = $a_phase1[$p1index]['descr'];
+	$pconfig['nat_traversal'] = $a_phase1[$p1index]['nat_traversal'];
+	$pconfig['mobike'] = $a_phase1[$p1index]['mobike'];
+
+	if (isset($a_phase1[$p1index]['gw_duplicates'])) {
+		$pconfig['gw_duplicates'] = true;
+	}
+
+	$pconfig['closeaction'] = $a_phase1[$p1index]['closeaction'];
+
+	if (isset($a_phase1[$p1index]['responderonly'])) {
+		$pconfig['responderonly'] = true;
+	}
+
+	if ($a_phase1[$p1index]['dpd_delay'] && $a_phase1[$p1index]['dpd_maxfail']) {
+		$pconfig['dpd_enable'] = true;
+		$pconfig['dpd_delay'] = $a_phase1[$p1index]['dpd_delay'];
+		$pconfig['dpd_maxfail'] = $a_phase1[$p1index]['dpd_maxfail'];
+	}
+
+	if (isset($a_phase1[$p1index]['prfselect_enable'])) {
+		$pconfig['prfselect_enable'] = 'yes';
+	}
+
+	if (isset($a_phase1[$p1index]['splitconn'])) {
+		$pconfig['splitconn'] = true;
+	}
+
+	if (isset($a_phase1[$p1index]['tfc_enable'])) {
+		$pconfig['tfc_enable'] = true;
+	}
+
+	if (isset($a_phase1[$p1index]['tfc_bytes'])) {
+		$pconfig['tfc_bytes'] = $a_phase1[$p1index]['tfc_bytes'];
+	}
+} else {
+	/* defaults */
+	$pconfig['interface'] = "wan";
+	if ($config['interfaces']['lan']) {
+		$pconfig['localnet'] = "lan";
+	}
+	$pconfig['mode'] = "main";
+	$pconfig['protocol'] = "inet";
+	$pconfig['myid_type'] = "myaddress";
+	$pconfig['peerid_type'] = "peeraddress";
+	$pconfig['authentication_method'] = "pre_shared_key";
+	$pconfig['reauth_time'] = "28800";
+	$pconfig['nat_traversal'] = 'on';
+	$pconfig['mobike'] = 'off';
+	$pconfig['prfselect_enable'] = false;
+	$pconfig['dpd_enable'] = true;
+	$pconfig['iketype'] = "ikev1";
+
+	/* mobile client */
+	if ($_REQUEST['mobile']) {
+		$pconfig['mobile'] = true;
+		$pconfig['mode'] = "aggressive";
+	}
+}
+// default value for new P1 and failsafe to always have at least 1 encryption item for the Form_ListItem
+if (!is_array($pconfig['encryption']['item']) || count($pconfig['encryption']['item']) == 0) {
+	$item = array();
+	$item['encryption-algorithm'] = array('name' => "aes", 'keylen' => 128);
+	$item['hash-algorithm'] = "sha256";
+	$item['prf-algorithm'] = "sha256";
+	$item['dhgroup'] = "14";
+	$pconfig['encryption']['item'][] = $item;
+}
+
+if (isset($_REQUEST['dup']) && is_numericint($_REQUEST['dup'])) {
+	unset($p1index);
+}
+
+if ($_POST['save']) {
+	unset($input_errors);
+	$pconfig = $_POST;
+
+	for($i = 0; $i < 100; $i++) {
+		if (isset($_POST['ealgo_algo'.$i])) {
+			$item = array();
+			$item['encryption-algorithm']['name'] = $_POST['ealgo_algo'.$i];
+			$item['encryption-algorithm']['keylen'] = $_POST['ealgo_keylen'.$i];
+			$item['hash-algorithm'] = $_POST['halgo'.$i];
+			$item['prf-algorithm'] = $_POST['prfalgo'.$i];
+			$item['dhgroup'] = $_POST['dhgroup'.$i];
+			$pconfig['encryption']['item'][] = $item;
+		}
+	}
+
+	/* input validation */
+
+	$method = $pconfig['authentication_method'];
+
+	// Unset ca and cert if not required to avoid storing in config
+	if ($method == "pre_shared_key" || $method == "xauth_psk_server") {
+		unset($pconfig['certref']);
+		unset($pconfig['pkcs11certref']);
+	}
+
+	if (!in_array($method, array('cert', 'eap-tls', 'xauth_cert_server', 'pkcs11'))) {
+		unset($pconfig['caref']);
+	}
+
+	// Only require PSK here for normal PSK tunnels (not mobile) or xauth.
+	// For certificate methods, require the CA/Cert.
+	switch ($method) {
+		case 'eap-mschapv2':
+			if ($pconfig['iketype'] != 'ikev2') {
+				$input_errors[] = gettext("EAP-MSChapv2 can only be used with IKEv2 type VPNs.");
+			}
+			break;
+		case "eap-tls":
+			if ($pconfig['iketype'] != 'ikev2') {
+				$input_errors[] = gettext("EAP-TLS can only be used with IKEv2 type VPNs.");
+			}
+			break;
+		case "eap-radius":
+			if ($pconfig['iketype'] != 'ikev2') {
+				$input_errors[] = gettext("EAP-RADIUS can only be used with IKEv2 type VPNs.");
+			}
+			break;
+		case "pre_shared_key":
+			// If this is a mobile PSK tunnel the user PSKs go on
+			//	  the PSK tab, not here, so skip the check.
+			if ($pconfig['mobile']) {
+				break;
+			}
+		case "xauth_psk_server":
+			$reqdfields = explode(" ", "pskey");
+			$reqdfieldsn = array(gettext("Pre-Shared Key"));
+			$validate_pskey = true;
+			break;
+		case "xauth_cert_server":
+		case "cert":
+			$reqdfields = explode(" ", "caref certref");
+			$reqdfieldsn = array(gettext("Certificate Authority"), gettext("Certificate"));
+			break;
+		case "pkcs11":
+			$reqdfields = explode(" ", "caref pkcs11certref pkcs11pin");
+			$reqdfieldsn = array(gettext("Certificate Authority"), gettext("Token Certificate"), gettext("Token PIN"));
+			break;
+		default:
+			/* Other types do not use this validation mechanism. */
+	}
+
+	if (!$pconfig['mobile']) {
+		$reqdfields[] = "remotegw";
+		$reqdfieldsn[] = gettext("Remote gateway");
+	}
+
+	do_input_validation($pconfig, $reqdfields, $reqdfieldsn, $input_errors);
+
+	if (isset($validate_pskey) && isset($pconfig['pskey']) && !preg_match('/^[[:ascii:]]*$/', $pconfig['pskey'])) {
+		unset($validate_pskey);
+		$input_errors[] = gettext("Pre-Shared Key contains invalid characters.");
+	}
+
+	if (!empty($pconfig['rekey_time']) && !is_numericint($pconfig['rekey_time'])) {
+		$input_errors[] = gettext("Rekey Time must be an integer.");
+	}
+	if (!empty($pconfig['reauth_time']) && !is_numericint($pconfig['reauth_time'])) {
+		$input_errors[] = gettext("Reauth Time must be an integer.");
+	}
+	if (!empty($pconfig['over_time']) && !is_numericint($pconfig['over_time'])) {
+		$input_errors[] = gettext("Over Time must be an integer.");
+	}
+
+	if (!empty($pconfig['closeaction']) && !array_key_exists($pconfig['closeaction'], $ipsec_closeactions)) {
+		$input_errors[] = gettext("Invalid Child SA Close Action.");
+	}
+
+	if ($pconfig['remotegw']) {
+		if (!is_ipaddr($pconfig['remotegw']) && !is_domain($pconfig['remotegw'])) {
+			$input_errors[] = gettext("A valid remote gateway address or host name must be specified.");
+		} elseif (is_ipaddrv4($pconfig['remotegw']) && ($pconfig['protocol'] == "inet6")) {
+			$input_errors[] = gettext("A valid remote gateway IPv4 address must be specified or protocol needs to be changed to IPv6");
+		} elseif (is_ipaddrv6($pconfig['remotegw']) && ($pconfig['protocol'] == "inet")) {
+			$input_errors[] = gettext("A valid remote gateway IPv6 address must be specified or protocol needs to be changed to IPv4");
+		}
+	}
+
+	if ($_POST['ikeport']) {
+		if (!is_port($pconfig['ikeport'])) {
+			$input_errors[] = gettext("The IKE port number is invalid.");
+		}
+	} else {
+		unset($pconfig['ikeport']);
+	}
+
+	if ($_POST['nattport']) {
+		if (!is_port($pconfig['nattport'])) {
+			$input_errors[] = gettext("The NAT-T port number is invalid.");
+		}
+	} else {
+		unset($pconfig['nattport']);
+	}
+
+	if (isset($pconfig['ikeport']) && isset($pconfig['nattport']) && $pconfig['ikeport'] == $pconfig['nattport']) {
+		$input_errors[] = gettext("IKE and NAT-T port numbers must be different.");
+	}
+
+	if ($pconfig['remotegw'] && is_ipaddr($pconfig['remotegw']) && !isset($pconfig['disabled'])) {
+		$t = 0;
+		foreach ($a_phase1 as $ph1tmp) {
+			if ($p1index != $t) {
+				$tremotegw = $pconfig['remotegw'];
+				if (($ph1tmp['remote-gateway'] == $tremotegw) && ($ph1tmp['remote-gateway'] != '0.0.0.0') &&
+				    ($ph1tmp['remote-gateway'] != '::') && !isset($ph1tmp['disabled']) &&
+				    (!isset($pconfig['gw_duplicates']) || !isset($ph1tmp['gw_duplicates']))) {
+					$input_errors[] = sprintf(gettext('The remote gateway "%1$s" is already used by phase1 "%2$s".'), $tremotegw, $ph1tmp['descr']);
+				}
+			}
+			$t++;
+		}
+	}
+
+	if (($pconfig['remotegw'] == '0.0.0.0') || ($pconfig['remotegw'] == '::')) {
+		if (!isset($pconfig['responderonly'])) {
+			$input_errors[] = gettext('The remote gateway "0.0.0.0" or "::" address can only be used with "Responder Only".');
+		}
+		if ($pconfig['peerid_type'] == "peeraddress") {
+			$input_errors[] = gettext('The remote gateway "0.0.0.0" or "::" address can not be used with IP address peer identifier.');
+		}
+
+	}
+
+	if (($pconfig['iketype'] == "ikev1") && is_array($a_phase2) && (count($a_phase2))) {
+		foreach ($a_phase2 as $phase2) {
+			if ($phase2['ikeid'] == $pconfig['ikeid']) {
+				if (($pconfig['protocol'] == "inet") && ($phase2['mode'] == "tunnel6")) {
+					$input_errors[] = gettext("There is a Phase 2 using IPv6, cannot use IPv4.");
+					break;
+				}
+				if (($pconfig['protocol'] == "inet6") && ($phase2['mode'] == "tunnel")) {
+					$input_errors[] = gettext("There is a Phase 2 using IPv4, cannot use IPv6.");
+					break;
+				}
+			}
+		}
+	}
+
+	/* My identity */
+
+	if ($pconfig['myid_type'] == "myaddress") {
+		$pconfig['myid_data'] = "";
+	}
+
+	if ($pconfig['myid_type'] == "address" and $pconfig['myid_data'] == "") {
+		$input_errors[] = gettext("Please enter an address for 'My Identifier'");
+	}
+
+	if ($pconfig['myid_type'] == "keyid tag" and $pconfig['myid_data'] == "") {
+		$input_errors[] = gettext("Please enter a keyid tag for 'My Identifier'");
+	}
+
+	if ($pconfig['myid_type'] == "fqdn" and $pconfig['myid_data'] == "") {
+		$input_errors[] = gettext("Please enter a fully qualified domain name for 'My Identifier'");
+	}
+
+	if ($pconfig['myid_type'] == "user_fqdn" and $pconfig['myid_data'] == "") {
+		$input_errors[] = gettext("Please enter a user and fully qualified domain name for 'My Identifier'");
+	}
+
+	if ($pconfig['myid_type'] == "dyn_dns" and $pconfig['myid_data'] == "") {
+		$input_errors[] = gettext("Please enter a dynamic domain name for 'My Identifier'");
+	}
+
+	if (($pconfig['myid_type'] == "address") && !is_ipaddr($pconfig['myid_data'])) {
+		$input_errors[] = gettext("A valid IP address for 'My identifier' must be specified.");
+	}
+
+	if (($pconfig['myid_type'] == "fqdn") && !is_domain($pconfig['myid_data'])) {
+		$input_errors[] = gettext("A valid domain name for 'My identifier' must be specified.");
+	}
+
+	if ($pconfig['myid_type'] == "fqdn") {
+		if (is_domain($pconfig['myid_data']) == false) {
+			$input_errors[] = gettext("A valid FQDN for 'My identifier' must be specified.");
+		}
+	}
+
+	if ($pconfig['myid_type'] == "user_fqdn") {
+		$user_fqdn = explode("@", $pconfig['myid_data']);
+		if (is_domain($user_fqdn[1]) == false) {
+			$input_errors[] = gettext("A valid User FQDN in the form of user@my.domain.com for 'My identifier' must be specified.");
+		}
+	}
+
+	if ($pconfig['myid_type'] == "dyn_dns") {
+		if (is_domain($pconfig['myid_data']) == false) {
+			$input_errors[] = gettext("A valid Dynamic DNS address for 'My identifier' must be specified.");
+		}
+	}
+
+	/* Peer identity */
+
+	if ($pconfig['myid_type'] == "peeraddress") {
+		$pconfig['peerid_data'] = "";
+	}
+
+	// Only enforce peer ID if we are not dealing with a pure-psk mobile config.
+	if (!(($pconfig['authentication_method'] == "pre_shared_key") && ($pconfig['mobile']))) {
+		if ($pconfig['peerid_type'] == "address" and $pconfig['peerid_data'] == "") {
+			$input_errors[] = gettext("Please enter an address for 'Peer Identifier'");
+		}
+
+		if ($pconfig['peerid_type'] == "keyid tag" and $pconfig['peerid_data'] == "") {
+			$input_errors[] = gettext("Please enter a keyid tag for 'Peer Identifier'");
+		}
+
+		if ($pconfig['peerid_type'] == "fqdn" and $pconfig['peerid_data'] == "") {
+			$input_errors[] = gettext("Please enter a fully qualified domain name for 'Peer Identifier'");
+		}
+
+		if ($pconfig['peerid_type'] == "user_fqdn" and $pconfig['peerid_data'] == "") {
+			$input_errors[] = gettext("Please enter a user and fully qualified domain name for 'Peer Identifier'");
+		}
+
+		if ((($pconfig['peerid_type'] == "address") && !is_ipaddr($pconfig['peerid_data']))) {
+			$input_errors[] = gettext("A valid IP address for 'Peer identifier' must be specified.");
+		}
+
+		if ((($pconfig['peerid_type'] == "fqdn") && !is_domain($pconfig['peerid_data']))) {
+			$input_errors[] = gettext("A valid domain name for 'Peer identifier' must be specified.");
+		}
+
+		if ($pconfig['peerid_type'] == "fqdn") {
+			if (is_domain($pconfig['peerid_data']) == false) {
+				$input_errors[] = gettext("A valid FQDN for 'Peer identifier' must be specified.");
+			}
+		}
+
+		if ($pconfig['peerid_type'] == "user_fqdn") {
+			$user_fqdn = explode("@", $pconfig['peerid_data']);
+			if (is_domain($user_fqdn[1]) == false) {
+				$input_errors[] = gettext("A valid User FQDN in the form of user@my.domain.com for 'Peer identifier' must be specified.");
+			}
+		}
+	}
+
+	if ($pconfig['dpd_enable']) {
+		if (!is_numericint($pconfig['dpd_delay'])) {
+			$input_errors[] = gettext("A numeric value must be specified for DPD delay.");
+		}
+
+		if (!is_numericint($pconfig['dpd_maxfail'])) {
+			$input_errors[] = gettext("A numeric value must be specified for DPD retries.");
+		}
+	}
+
+	if ($pconfig['tfc_bytes'] && !is_numericint($pconfig['tfc_bytes'])) {
+		$input_errors[] = gettext("A numeric value must be specified for TFC bytes.");
+	}
+
+	if (!empty($pconfig['iketype']) && $pconfig['iketype'] != "ikev1" && $pconfig['iketype'] != "ikev2" && $pconfig['iketype'] != "auto") {
+		$input_errors[] = gettext("Valid arguments for IKE type are v1, v2 or auto");
+	}
+
+	foreach($pconfig['encryption']['item'] as $p1algo) {
+		if (preg_match("/aes\d+gcm/", $p1algo['encryption-algorithm']['name']) && $_POST['iketype'] != "ikev2") {
+			$input_errors[] = gettext("Encryption Algorithm AES-GCM can only be used with IKEv2");
+		}
+	}
+	/* auth backend for mobile eap-radius VPNs should be a RADIUS server */
+	if (($pconfig['authentication_method'] == 'eap-radius') && $pconfig['mobile']) {
+		if (!empty($config['ipsec']['client']['user_source'])) {
+			$auth_server_list  = explode(',', $config['ipsec']['client']['user_source']);
+			foreach ($auth_server_list as $auth_server_name) {
+				$auth_server       = auth_get_authserver($auth_server_name);
+				if (!is_array($auth_server) || ($auth_server['type'] != 'radius')) {
+					$input_errors[] = gettext("A valid RADIUS server must be selected for user authentication on the Mobile Clients tab in order to set EAP-RADIUS as the authentication method.");
+				}
+			}
+		}
+	}
+	if (is_array($old_ph1ent) && ipsec_vti($old_ph1ent, false, false) && $pconfig['disabled']) {
+		$input_errors[] = gettext("Cannot disable a Phase 1 with a child Phase 2 while the interface is assigned. Remove the interface assignment before disabling this P2.");
+	}
+
+	if (!empty($pconfig['certref'])) {
+		$errchkcert =& lookup_cert($pconfig['certref']);
+		if (is_array($errchkcert) && !cert_check_pkey_compatibility($errchkcert['prv'], 'IPsec')) {
+			$input_errors[] = gettext("The selected ECDSA certificate does not use a curve compatible with IKEv2");
+		}
+	}
+
+	if (!$input_errors) {
+		$ph1ent['ikeid'] = $pconfig['ikeid'];
+		$ph1ent['iketype'] = $pconfig['iketype'];
+		if ($pconfig['iketype'] == 'ikev2') {
+			unset($ph1ent['mode']);
+		} else {
+			$ph1ent['mode'] = $pconfig['mode'];
+		}
+
+		// re-create vtimaps after IKE version switching
+		$vtisubnet_spec = ipsec_vti($ph1ent, true);
+		if ((($pconfig['iketype'] != $a_phase1[$p1index]['iketype']) ||
+		    (isset($pconfig['splitconn']) != isset($a_phase1[$p1index]['splitconn']))) &&
+		    ($vtisubnet_spec || is_array($vtisubnet_spec))) {
+			foreach ($a_vtimaps as $id => $vtimap) {
+				if ($vtimap['reqid'] == $ph1ent['ikeid']) {
+					unset($a_vtimaps[$id]);
+				}
+			}
+			if (($pconfig['iketype'] == 'ikev1') ||
+			    isset($pconfig['splitconn'])) {
+				foreach ($vtisubnet_spec as $idx => $vtisub) {
+					$a_vtimaps[] = ipsec_create_vtimap(
+					    $ph1ent['ikeid'], $idx);
+				}
+			} else {
+				$a_vtimaps[] = ipsec_create_vtimap(
+				    $ph1ent['ikeid'], 0);
+			}
+		}
+
+		$ph1ent['disabled'] = $pconfig['disabled'] ? true : false;
+		$ph1ent['interface'] = $pconfig['interface'];
+		/* if the remote gateway changed and the interface is not WAN then remove route */
+		/* the ipsec_configure() handles adding the route */
+		if ($pconfig['interface'] <> "wan") {
+			if ($old_ph1ent['remote-gateway'] <> $pconfig['remotegw']) {
+				route_del($old_ph1ent['remote-gateway']);
+			}
+		}
+
+		if ($pconfig['mobile']) {
+			$ph1ent['mobile'] = true;
+		} else {
+			$ph1ent['remote-gateway'] = $pconfig['remotegw'];
+			if ( !empty($pconfig['ikeport']) ) {
+				$ph1ent['ikeport'] = $pconfig['ikeport'];
+			} else {
+				unset($ph1ent['ikeport']);
+			}
+			if ( !empty($pconfig['nattport']) ) {
+				$ph1ent['nattport'] = $pconfig['nattport'];
+			} else {
+				unset($ph1ent['nattport']);
+			}
+		}
+
+		$ph1ent['protocol'] = $pconfig['protocol'];
+
+		$ph1ent['myid_type'] = $pconfig['myid_type'];
+		$ph1ent['myid_data'] = $pconfig['myid_data'];
+		$ph1ent['peerid_type'] = $pconfig['peerid_type'];
+		$ph1ent['peerid_data'] = $pconfig['peerid_data'];
+
+		$ph1ent['encryption'] = $pconfig['encryption'];
+		$ph1ent['rekey_time'] = $pconfig['rekey_time'];
+		$ph1ent['reauth_time'] = $pconfig['reauth_time'];
+		$ph1ent['over_time'] = $pconfig['over_time'];
+		$ph1ent['pre-shared-key'] = $pconfig['pskey'];
+		$ph1ent['private-key'] = base64_encode($pconfig['privatekey']);
+		$ph1ent['certref'] = $pconfig['certref'];
+		$ph1ent['pkcs11certref'] = $pconfig['pkcs11certref'];
+		$ph1ent['pkcs11pin'] = $pconfig['pkcs11pin'];
+		$ph1ent['caref'] = $pconfig['caref'];
+		$ph1ent['authentication_method'] = $pconfig['authentication_method'];
+		$ph1ent['descr'] = $pconfig['descr'];
+		$ph1ent['nat_traversal'] = $pconfig['nat_traversal'];
+		$ph1ent['mobike'] = $pconfig['mobike'];
+
+		if ( isset($pconfig['gw_duplicates'])) {
+			$ph1ent['gw_duplicates'] = true;
+		} else {
+			unset($ph1ent['gw_duplicates']);
+		}
+
+		$ph1ent['closeaction'] = $pconfig['closeaction'];
+
+		if (isset($pconfig['prfselect_enable'])) {
+			$ph1ent['prfselect_enable'] = 'yes';
+		} else {
+			unset($ph1ent['prfselect_enable']);
+		}
+
+		if (isset($pconfig['responderonly'])) {
+			$ph1ent['responderonly'] = true;
+		} else {
+			unset($ph1ent['responderonly']);
+		}
+
+		if (isset($pconfig['dpd_enable'])) {
+			$ph1ent['dpd_delay'] = $pconfig['dpd_delay'];
+			$ph1ent['dpd_maxfail'] = $pconfig['dpd_maxfail'];
+		}
+
+		if (isset($pconfig['splitconn'])) {
+			$ph1ent['splitconn'] = true;
+		} else {
+			unset($ph1ent['splitconn']);
+		}
+
+		if (isset($pconfig['tfc_enable'])) {
+			$ph1ent['tfc_enable'] = true;
+		}
+
+		if (isset($pconfig['tfc_bytes'])) {
+			$ph1ent['tfc_bytes'] = $pconfig['tfc_bytes'];
+		}
+
+		/* generate unique phase1 ikeid */
+		if ($ph1ent['ikeid'] == 0) {
+			$ph1ent['ikeid'] = ipsec_ikeid_next();
+		}
+
+		if (isset($p1index) && $a_phase1[$p1index]) {
+			$a_phase1[$p1index] = $ph1ent;
+		} else {
+			$a_phase1[] = $ph1ent;
+		}
+
+		write_config(gettext("Saved IPsec tunnel Phase 1 configuration."));
+		mark_subsystem_dirty('ipsec');
+
+		header("Location: vpn_ipsec.php");
+		exit;
+	}
+}
+
+function build_interface_list() {
+	$interfaces = get_configured_interface_with_descr();
+
+	$viplist = get_configured_vip_list();
+	foreach ($viplist as $vip => $address) {
+		$interfaces[$vip] = $address;
+		if (get_vip_descr($address)) {
+			$interfaces[$vip] .= " (". get_vip_descr($address) .")";
+		}
+	}
+
+	$grouplist = return_gateway_groups_array();
+
+	foreach ($grouplist as $name => $group) {
+		if ($group[0]['vip'] != "") {
+			$vipif = $group[0]['vip'];
+		} else {
+			$vipif = $group[0]['int'];
+		}
+
+		$interfaces[$name] = sprintf(gettext("GW Group %s"), $name);
+	}
+
+	return($interfaces);
+
+}
+
+function build_auth_method_list() {
+	global $p1_authentication_methods, $pconfig;
+
+	$list = array();
+
+	foreach ($p1_authentication_methods as $method_type => $method_params) {
+		if (!$pconfig['mobile'] && $method_params['mobile']) {
+			continue;
+		}
+
+		$list[$method_type] = htmlspecialchars($method_params['name']);
+	}
+
+	return($list);
+}
+
+function build_myid_list() {
+	global $my_identifier_list;
+
+	$list = array();
+
+	foreach ($my_identifier_list as $id_type => $id_params) {
+		$list[$id_type] = htmlspecialchars($id_params['desc']);
+	}
+
+	return($list);
+}
+
+function build_peerid_list() {
+	global $peer_identifier_list;
+
+	$list = array();
+
+	foreach ($peer_identifier_list as $id_type => $id_params) {
+		$list[$id_type] = htmlspecialchars($id_params['desc']);
+	}
+
+	return($list);
+}
+
+function build_pkcs11cert_list() {
+	global $config;
+
+	$list = array();
+	$p11_cn = array();
+	$p11_id = array();
+	$output = shell_exec('/usr/local/bin/pkcs15-tool -c');
+
+	preg_match_all('/X\.509\ Certificate\ \[(.*)\]/', $output, $p11_cn);
+	preg_match_all('/ID\s+: (.*)/', $output, $p11_id);
+
+	if (is_array($p11_id)) {
+		for ($i = 0; $i < count($p11_id[1]); $i++) {
+			$list[$p11_id[1][$i]] = "{$p11_cn[1][$i]} " . "({$p11_id[1][$i]})";
+		}
+	}
+	return($list);
+}
+
+function build_eal_list() {
+	global $p1_ealgos;
+
+	$list = array();
+
+	if (is_array($p1_ealgos)) {
+		foreach ($p1_ealgos as $algo => $algodata) {
+			$list[$algo] = htmlspecialchars($algodata['name']);
+		}
+	}
+
+	return($list);
+}
+
+if ($pconfig['mobile']) {
+	$pgtitle = array(gettext("VPN"), gettext("IPsec"), gettext("Mobile Clients"), gettext("Edit Phase 1"));
+	$pglinks = array("", "vpn_ipsec.php", "vpn_ipsec_mobile.php", "@self");
+} else {
+	$pgtitle = array(gettext("VPN"), gettext("IPsec"), gettext("Tunnels"), gettext("Edit Phase 1"));
+	$pglinks = array("", "vpn_ipsec.php", "vpn_ipsec.php", "@self");
+}
+
+$shortcut_section = "ipsec";
+
+include("head.inc");
+
+if ($input_errors) {
+	print_input_errors($input_errors);
+}
+
+$tab_array = array();
+$tab_array[] = array(gettext("Tunnels"), true, "vpn_ipsec.php");
+$tab_array[] = array(gettext("Mobile Clients"), false, "vpn_ipsec_mobile.php");
+$tab_array[] = array(gettext("Pre-Shared Keys"), false, "vpn_ipsec_keys.php");
+$tab_array[] = array(gettext("Advanced Settings"), false, "vpn_ipsec_settings.php");
+display_top_tabs($tab_array);
+
+$form = new Form();
+
+$section = new Form_Section('General Information');
+
+$section->addInput(new Form_Checkbox(
+	'disabled',
+	'Disabled',
+	'Set this option to disable this phase1 without removing it from the list. ',
+	$pconfig['disabled']
+));
+
+$section->addInput(new Form_Select(
+	'iketype',
+	'*Key Exchange version',
+	$pconfig['iketype'],
+	array("ikev1" => "IKEv1", "ikev2" => "IKEv2", "auto" => gettext("Auto"))
+))->setHelp('Select the Internet Key Exchange protocol version to be used. Auto uses IKEv2 when initiator, and accepts either IKEv1 or IKEv2 as responder.');
+
+$section->addInput(new Form_Select(
+	'protocol',
+	'*Internet Protocol',
+	$pconfig['protocol'],
+	array("inet" => "IPv4", "inet6" => "IPv6", "both" => "Both (Dual Stack)")
+))->setHelp('Select the Internet Protocol family.');
+
+$section->addInput(new Form_Select(
+	'interface',
+	'*Interface',
+	$pconfig['interface'],
+	build_interface_list()
+))->setHelp('Select the interface for the local endpoint of this phase1 entry.');
+
+if (!$pconfig['mobile']) {
+	$group = new Form_Group('*Remote Gateway');
+
+	$group->add(new Form_Input(
+		'remotegw',
+		'Remote Gateway',
+		'text',
+		$pconfig['remotegw']
+	))->setHelp('Enter the public IP address or host name of the remote gateway.%1$s%2$s%3$s',
+	    '<div class="infoblock">',
+	    sprint_info_box(gettext('Use \'0.0.0.0\' to allow connections from any IPv4 address or \'::\' ' . 
+	    'to allow connections from any IPv6 address.' . '<br/>' . 'Responder Only must be set and ' . 
+	    'Peer IP Address cannot be used for Remote Identifier.'), 'info', false),
+	    '</div>');
+	$group->add(new Form_Input(
+	    'ikeport',
+	    'Remote IKE Port',
+	    'number',
+	    $pconfig['ikeport'],
+	    ['min' => 1, 'max' => 65535]
+	))->setHelp('UDP port for IKE on the remote gateway. Leave empty for default automatic behavior (500/4500).');
+	$group->add(new Form_Input(
+	    'nattport',
+	    'Remote NAT-T Port',
+	    'number',
+	    $pconfig['nattport'],
+	    ['min' => 1, 'max' => 65535]
+	))->setHelp('UDP port for NAT-T on the remote gateway.%1$s%2$s%3$s',
+	    '<div class="infoblock">',
+	    sprint_info_box(gettext('If the IKE port is empty and NAT-T contains a value, the tunnel will use only NAT-T.'),
+	    'info', false),
+	    '</div>');
+	$section->add($group);
+}
+
+$section->addInput(new Form_Input(
+	'descr',
+	'Description',
+	'text',
+	$pconfig['descr']
+))->setHelp('A description may be entered here for administrative reference (not parsed).');
+
+$form->add($section);
+
+$section = new Form_Section('Phase 1 Proposal (Authentication)');
+
+$section->addInput(new Form_Select(
+	'authentication_method',
+	'*Authentication Method',
+	$pconfig['authentication_method'],
+	build_auth_method_list()
+))->setHelp('Must match the setting chosen on the remote side.');
+
+$section->addInput(new Form_Select(
+	'mode',
+	'*Negotiation mode',
+	$pconfig['mode'],
+	array("main" => gettext("Main"), "aggressive" => gettext("Aggressive"))
+))->setHelp('Aggressive is more flexible, but less secure.');
+
+$group = new Form_Group('*My identifier');
+
+$group->add(new Form_Select(
+	'myid_type',
+	null,
+	$pconfig['myid_type'],
+	build_myid_list()
+));
+
+$group->add(new Form_Input(
+	'myid_data',
+	null,
+	'text',
+	$pconfig['myid_data']
+));
+
+$section->add($group);
+
+$group = new Form_Group('*Peer identifier');
+$group->addClass('peeridgroup');
+
+$group->add(new Form_Select(
+	'peerid_type',
+	null,
+	$pconfig['peerid_type'],
+	build_peerid_list()
+));
+
+$group->add(new Form_Input(
+	'peerid_data',
+	null,
+	'text',
+	$pconfig['peerid_data']
+));
+
+if ($pconfig['mobile']) {
+	$group->setHelp('This is known as the "group" setting on some VPN client implementations');
+}
+
+$section->add($group);
+
+$section->addInput(new Form_Input(
+	'pskey',
+	'*Pre-Shared Key',
+	'text',
+	$pconfig['pskey']
+))->setHelp('Enter the Pre-Shared Key string. This key must match on both peers. %1$sThis key should be long and random to protect the tunnel and its contents. A weak Pre-Shared Key can lead to a tunnel compromise.%1$s', '<br/>');
+
+$section->addInput(new Form_Select(
+	'certref',
+	'*My Certificate',
+	$pconfig['certref'],
+	cert_build_list('cert', 'IPsec')
+))->setHelp('Select a certificate previously configured in the Certificate Manager.');
+
+$section->addInput(new Form_Select(
+	'pkcs11certref',
+	'*PKCS#11 Certificate',
+	$pconfig['pkcs11certref'],
+	build_pkcs11cert_list()
+))->setHelp('Select a Certificate from an attached PKCS#11 token device');
+
+$section->addInput(new Form_Input(
+	'pkcs11pin',
+	'*PKCS#11 PIN',
+	'text',
+	$pconfig['pkcs11pin']
+))->setHelp('Enter PKCS#11 token PIN number');
+
+$section->addInput(new Form_Select(
+	'caref',
+	'*Peer Certificate Authority',
+	$pconfig['caref'],
+	cert_build_list('ca', 'IPsec')
+))->setHelp('Select a certificate authority previously configured in the Certificate Manager.');
+
+$form->add($section);
+
+$rowcount = count($pconfig['encryption']['item']);
+$section = new Form_Section('Phase 1 Proposal (Encryption Algorithm)');
+foreach($pconfig['encryption']['item'] as $key => $p1enc) {
+	$lastrow = ($counter == $rowcount - 1);
+	$group = new Form_Group($counter == 0 ? '*Encryption Algorithm' : '');
+	$group->addClass("repeatable");
+
+	$group->add(new Form_Select(
+		'ealgo_algo'.$key,
+		null,
+		$p1enc['encryption-algorithm']['name'],
+		build_eal_list()
+	))->setHelp($lastrow ? 'Algorithm' : '')->setWidth(2);
+
+	$group->add(new Form_Select(
+		'ealgo_keylen'.$key,
+		null,
+		$p1enc['encryption-algorithm']['keylen'],
+		array()
+	))->setHelp($lastrow ? 'Key length' : '')->setWidth(2);
+
+	$group->add(new Form_Select(
+		'halgo'.$key,
+		'*Hash Algorithm',
+		$p1enc['hash-algorithm'],
+		$p1_halgos
+	))->setHelp($lastrow ? 'Hash' : '')->setWidth(2);
+
+	$group->add(new Form_Select(
+		'dhgroup'.$key,
+		'*DH Group',
+		$p1enc['dhgroup'],
+		$p1_dhgroups
+	))->setHelp($lastrow ? 'DH Group' : '')->setWidth(2);
+
+	$group->add(new Form_Button(
+		'deleterow' . $counter,
+		'Delete',
+		null,
+		'fa-trash'
+	))->addClass('btn-warning')->setWidth(2);
+
+	$group->add(new Form_StaticText(
+		null,
+		null
+	))->setWidth(6);
+
+	$group->add(new Form_Select(
+		'prfalgo'.$key,
+		'*PRF Algorithm',
+		$p1enc['prf-algorithm'],
+		$p1_halgos
+	))->setHelp($lastrow ? 'PRF' : '')->setWidth(2);
+
+	$section->add($group);
+	$counter += 1;
+}
+$section->addInput(new Form_StaticText('', ''))->setHelp('Note: Blowfish, 3DES, CAST128, MD5, SHA1, and DH groups 1, 2, 5, 22, 23, and 24 provide weak security and should be avoided.');
+
+$btnaddopt = new Form_Button(
+	'algoaddrow',
+	'Add Algorithm',
+	null,
+	'fa-plus'
+);
+$btnaddopt->removeClass('btn-primary')->addClass('btn-success btn-sm');
+$section->addInput($btnaddopt);
+
+$form->add($section);
+
+$section = new Form_Section('Expiration and Replacement');
+
+$section->addInput(new Form_Input(
+	'rekey_time',
+	'Rekey Time',
+	'number',
+	$pconfig['rekey_time'],
+	['min' => 0]
+))->setHelp('Time, in seconds, before an IKE SA establishes new keys. This works without interruption. ' .
+		'Only supported by IKEv2, and is recommended for use with IKEv2. ' .
+		'Leave blank or enter a value of 0 to disable.');
+
+$section->addInput(new Form_Input(
+	'reauth_time',
+	'Reauth Time',
+	'number',
+	$pconfig['reauth_time'],
+	['min' => 0]
+))->setHelp('Time, in seconds, before an IKE SA is torn down and recreated from scratch, including authentication. ' .
+		'This can be disruptive unless both sides support make-before-break and overlapping IKE SA entries. ' .
+		'Supported by IKEv1 and IKEv2. Leave blank or enter a value of 0 to disable.');
+
+$section->addInput(new Form_Input(
+	'over_time',
+	'Over Time',
+	'number',
+	$pconfig['over_time'],
+	['min' => 0]
+))->setHelp('Hard IKE SA life time, in seconds, after which the IKE SA will be expired. ' .
+		'This time is relative to reauthentication and rekey time. ' .
+		'If left empty, defaults to 10% of whichever timer is higher (reauth or rekey)');
+
+$form->add($section);
+
+$section = new Form_Section('Advanced Options');
+
+$section->addInput(new Form_Checkbox(
+	'responderonly',
+	'Responder Only',
+	'Enable this option to never initiate this connection from this side, only respond to incoming requests.',
+	$pconfig['responderonly']
+));
+
+$section->addInput(new Form_Select(
+	'closeaction',
+	'Child SA Close Action',
+	$pconfig['closeaction'],
+	$ipsec_closeactions
+))->setHelp('Set this option to control the behavior when the remote peer unexpectedly closes a child SA (P2)');
+
+$section->addInput(new Form_Select(
+	'nat_traversal',
+	'NAT Traversal',
+	$pconfig['nat_traversal'],
+	array('on' => gettext('Auto'), 'force' => gettext('Force'))
+))->setHelp('Set this option to enable the use of NAT-T (i.e. the encapsulation of ESP in UDP packets) if needed, ' .
+			'which can help with clients that are behind restrictive firewalls.');
+
+$section->addInput(new Form_Select(
+	'mobike',
+	'MOBIKE',
+	$pconfig['mobike'],
+	array('on' => gettext('Enable'), 'off' => gettext('Disable'))
+))->setHelp('Set this option to control the use of MOBIKE');
+
+$section->addInput(new Form_Checkbox(
+	'gw_duplicates',
+	'Gateway duplicates',
+	'Enable this to allow multiple phase 1 configurations with the same endpoint. ' .
+		'When enabled, pfSense does not manage routing to the remote gateway and traffic will follow the default route ' .
+		'without regard for the chosen interface. Static routes can override this behavior.',
+	$pconfig['gw_duplicates']
+));
+
+$section->addInput(new Form_Checkbox(
+	'splitconn',
+	'Split connections',
+	'Enable this to split connection entries with multiple phase 2 configurations. Required for remote endpoints that support only a single traffic selector per child SA.',
+	$pconfig['splitconn']
+));
+
+$section->addInput(new Form_Checkbox(
+	'prfselect_enable',
+	'PRF Selection',
+	'Enable manual Pseudo-Random Function (PRF) selection',
+	$pconfig['prfselect_enable']
+))->setHelp('Manual PRF selection is typically not required, but can be useful in combination with AEAD Encryption Algorithms such as AES-GCM');
+
+/* FreeBSD doesn't yet have TFC support. this is ready to go once it does
+https://redmine.pfsense.org/issues/4688
+
+$section->addInput(new Form_Checkbox(
+	'tfc_enable',
+	'Traffic Flow Confidentiality',
+	'Enable TFC',
+	$pconfig['tfc_enable']
+))->setHelp('Enable Traffic Flow Confidentiality');
+
+$section->addInput(new Form_Input(
+	'tfc_bytes',
+	'TFC Bytes',
+	'Bytes TFC',
+	$pconfig['tfc_bytes']
+))->setHelp('Enter the number of bytes to pad ESP data to, or leave blank to fill to MTU size');
+
+*/
+
+$section->addInput(new Form_Checkbox(
+	'dpd_enable',
+	'Dead Peer Detection',
+	'Enable DPD',
+	$pconfig['dpd_enable']
+));
+
+$section->addInput(new Form_Input(
+	'dpd_delay',
+	'Delay',
+	'number',
+	$pconfig['dpd_delay']
+))->setHelp('Delay between requesting peer acknowledgement.');
+
+$section->addInput(new Form_Input(
+	'dpd_maxfail',
+	'Max failures',
+	'number',
+	$pconfig['dpd_maxfail']
+))->setHelp('Number of consecutive failures allowed before disconnect. ');
+
+if (isset($p1index) && $a_phase1[$p1index]) {
+	$form->addGlobal(new Form_Input(
+		'p1index',
+		null,
+		'hidden',
+		$p1index
+	));
+}
+
+if ($pconfig['mobile']) {
+	$form->addGlobal(new Form_Input(
+		'mobile',
+		null,
+		'hidden',
+		'true'
+	));
+}
+
+$form->addGlobal(new Form_Input(
+	'ikeid',
+	null,
+	'hidden',
+	$pconfig['ikeid']
+));
+
+$form->add($section);
+
+print($form);
+
+?>
+
+
+<form action="vpn_ipsec_phase1.php" method="post" name="iform" id="iform">
+
+<script type="text/javascript">
+//<![CDATA[
+events.push(function() {
+
+	$('[id^=algoaddrow]').prop('type','button');
+
+	$('[id^=algoaddrow]').click(function() {
+		add_row();
+
+		var lastRepeatableGroup = $('.repeatable:last');
+		$(lastRepeatableGroup).find('[id^=ealgo_algo]select').change(function () {
+			id = getStringInt(this.id);
+			ealgosel_change(id, '');
+		});
+		$(lastRepeatableGroup).find('[id^=ealgo_algo]select').change();
+	});
+
+	function myidsel_change() {
+		hideGroupInput('myid_data', ($('#myid_type').val() == 'myaddress'));
+	}
+
+	function iketype_change() {
+
+		if ($('#iketype').val() == 'ikev2') {
+			hideInput('mode', true);
+			hideInput('mobike', false);
+			//hideCheckbox('tfc_enable', false);
+			hideInput('rekey_time', false);
+			hideCheckbox('splitconn', false);
+			hideCheckbox('prfselect_enable', false);
+		} else {
+			hideInput('mode', false);
+			hideInput('mobike', true);
+			//hideCheckbox('tfc_enable', true);
+			//hideInput('tfc_bytes', true);
+			hideInput('rekey_time', !($('#iketype').val() == 'auto'));
+			hideCheckbox('splitconn', true);
+			hideCheckbox('prfselect_enable', true);
+		}
+	}
+
+	function peeridsel_change() {
+		hideGroupInput('peerid_data', ($('#peerid_type').val() == 'peeraddress') || ($('#peerid_type').val() == 'any'));
+	}
+
+	function methodsel_change() {
+
+		switch ($('#authentication_method').val()) {
+			case 'eap-mschapv2':
+			case 'eap-radius':
+			case 'hybrid_cert_server':
+				hideInput('pskey', true);
+				hideClass('peeridgroup', false);
+				hideInput('certref', false);
+				hideInput('caref', true);
+				hideInput('pkcs11certref', true);
+				hideInput('pkcs11pin', true);
+				disableInput('certref', false);
+				disableInput('caref', true);
+				disableInput('pkcs11certref', true);
+				disableInput('pkcs11pin', true);
+				break;
+			case 'eap-tls':
+			case 'xauth_cert_server':
+			case 'cert':
+				hideInput('pskey', true);
+				hideClass('peeridgroup', false);
+				hideInput('certref', false);
+				hideInput('caref', false);
+				hideInput('pkcs11certref', true);
+				hideInput('pkcs11pin', true);
+				disableInput('certref', false);
+				disableInput('caref', false);
+				disableInput('pkcs11certref', true);
+				disableInput('pkcs11pin', true);
+				break;
+			case 'pkcs11':
+				hideInput('pskey', true);
+				hideClass('peeridgroup', false);
+				hideInput('certref', true);
+				hideInput('caref', false);
+				hideInput('pkcs11certref', false);
+				hideInput('pkcs11pin', false);
+				disableInput('certref', true);
+				disableInput('caref', false);
+				disableInput('pkcs11certref', false);
+				disableInput('pkcs11pin', false);
+				break;
+
+<?php if ($pconfig['mobile']) { ?>
+				case 'pre_shared_key':
+					hideInput('pskey', true);
+					hideClass('peeridgroup', true);
+					hideInput('certref', true);
+					hideInput('caref', true);
+					hideInput('pkcs11certref', true);
+					hideInput('pkcs11pin', true);
+					disableInput('certref', true);
+					disableInput('caref', true);
+					disableInput('pkcs11certref', true);
+					disableInput('pkcs11pin', true);
+					break;
+<?php } ?>
+			default: /* psk modes*/
+				hideInput('pskey', false);
+				hideClass('peeridgroup', false);
+				hideInput('certref', true);
+				hideInput('caref', true);
+				hideInput('pkcs11certref', true);
+				hideInput('pkcs11pin', true);
+				disableInput('certref', true);
+				disableInput('caref', true);
+				disableInput('pkcs11certref', true);
+				disableInput('pkcs11pin', true);
+				break;
+		}
+	}
+
+	/* PHP generates javascript case statements for variable length keys */
+	function ealgosel_change(id, bits) {
+
+		$("select[name='ealgo_keylen"+id+"']").find('option').remove().end();
+
+		switch ($('#ealgo_algo'+id).find(":selected").index().toString()) {
+<?php
+	$i = 0;
+	foreach ($p1_ealgos as $algo => $algodata) {
+		if (is_array($algodata['keysel'])) {
+?>
+			case '<?=$i?>':
+				invisibleGroupInput('ealgo_keylen'+id, false);
+<?php
+			$key_hi = $algodata['keysel']['hi'];
+			$key_lo = $algodata['keysel']['lo'];
+			$key_step = $algodata['keysel']['step'];
+
+			for ($keylen = $key_hi; $keylen >= $key_lo; $keylen -= $key_step) {
+?>
+				$("select[name='ealgo_keylen"+id+"']").append($('<option value="<?=$keylen?>"><?=$keylen?> bits</option>'));
+<?php
+			}
+?>
+			break;
+<?php
+		} else {
+?>
+			case '<?=$i?>':
+				invisibleGroupInput('ealgo_keylen'+id, true);
+			break;
+<?php
+		}
+		$i++;
+	}
+?>
+		}
+
+		if (bits) {
+			$('#ealgo_keylen'+id).val(bits);
+		}
+	}
+
+	function prfselectchkbox_change() {
+		hide = !$('#prfselect_enable').prop('checked');
+		var i;
+		for (i = 0; i < 50; i++) {
+			hideGroupInput('prfalgo' + i , hide);
+		}
+	}
+
+	function dpdchkbox_change() {
+		hide = !$('#dpd_enable').prop('checked');
+
+		hideInput('dpd_delay', hide);
+		hideInput('dpd_maxfail', hide);
+
+		if (!$('#dpd_delay').val()) {
+			$('#dpd_delay').val('10')
+		}
+
+		if (!$('#dpd_maxfail').val()) {
+			$('#dpd_maxfail').val('5')
+		}
+	}
+
+	//function tfcchkbox_change() {
+	//	hide = !$('#tfc_enable').prop('checked');
+	//
+	//	hideInput('tfc_bytes', hide);
+	//}
+
+	// ---------- Monitor elements for change and call the appropriate display functions ----------
+
+	 // Enable PRF
+	$('#prfselect_enable').click(function () {
+		prfselectchkbox_change();
+	});
+
+	 // Enable DPD
+	$('#dpd_enable').click(function () {
+		dpdchkbox_change();
+	});
+
+	 // TFC
+	//$('#tfc_enable').click(function () {
+	//	tfcchkbox_change();
+	//});
+
+	 // Peer identifier
+	$('#peerid_type').change(function () {
+		peeridsel_change();
+	});
+
+	 // My identifier
+	$('#myid_type').change(function () {
+		myidsel_change();
+	});
+
+	 // ike type
+	$('#iketype').change(function () {
+		iketype_change();
+	});
+
+	 // authentication method
+	$('#authentication_method').change(function () {
+		methodsel_change();
+	});
+
+	 // algorithm
+	$('[id^=ealgo_algo]select').change(function () {
+		id = getStringInt(this.id);
+		ealgosel_change(id, 0);
+	});
+
+	// On initial page load
+	myidsel_change();
+	peeridsel_change();
+	iketype_change();
+	methodsel_change();
+	dpdchkbox_change();
+	prfselectchkbox_change();
+<?php
+foreach($pconfig['encryption']['item'] as $key => $p1enc) {
+	$keylen = $p1enc['encryption-algorithm']['keylen'];
+	if (!is_numericint($keylen)) {
+		$keylen = "''";
+	}
+	echo "ealgosel_change({$key}, {$keylen});";
+}
+?>
+
+	// ---------- On initial page load ------------------------------------------------------------
+
+	var generateButton = $('<a class="btn btn-xs btn-warning"><i class="fa fa-refresh icon-embed-btn"></i><?=gettext("Generate new Pre-Shared Key");?></a>');
+	generateButton.on('click', function() {
+		$.ajax({
+			type: 'get',
+			url: 'vpn_ipsec_phase1.php?generatekey=true',
+			dataType: 'json',
+			success: function(data) {
+				$('#pskey').val(data.pskey.replace(/\\n/g, '\n'));
+			}
+		});
+	});
+	generateButton.appendTo($('#pskey + .help-block')[0]);
+});
+//]]>
+</script>
+</form>
+<?php
+
+include("foot.inc");
diff --git a/src/usr/local/www/vpn_wg_peer.php b/src/usr/local/www/vpn_wg_peer.php
new file mode 100644
index 0000000000..fa21ca4022
--- /dev/null
+++ b/src/usr/local/www/vpn_wg_peer.php
@@ -0,0 +1,1068 @@
+<?php
+/*
+ * vpn_ipsec_phase2.php
+ *
+ * part of pfSense (https://www.pfsense.org)
+ * Copyright (c) 2004-2013 BSD Perimeter
+ * Copyright (c) 2013-2016 Electric Sheep Fencing
+ * Copyright (c) 2014-2020 Rubicon Communications, LLC (Netgate)
+ * Copyright (c) 2008 Shrew Soft Inc
+ * All rights reserved.
+ *
+ * originally based on m0n0wall (http://m0n0.ch/wall)
+ * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+##|+PRIV
+##|*IDENT=page-vpn-ipsec-editphase2
+##|*NAME=VPN: IPsec: Edit Phase 2
+##|*DESCR=Allow access to the 'VPN: IPsec: Edit Phase 2' page.
+##|*MATCH=vpn_ipsec_phase2.php*
+##|-PRIV
+
+require_once("functions.inc");
+require_once("guiconfig.inc");
+require_once("ipsec.inc");
+require_once("vpn.inc");
+
+init_config_arr(array('ipsec', 'client'));
+$a_client = &$config['ipsec']['client'];
+init_config_arr(array('ipsec', 'phase1'));
+init_config_arr(array('ipsec', 'phase2'));
+init_config_arr(array('ipsec', 'vtimaps', 'item'));
+$a_phase1 = &$config['ipsec']['phase1'];
+$a_phase2 = &$config['ipsec']['phase2'];
+$a_vtimaps = &$config['ipsec']['vtimaps']['item'];
+
+if (!empty($_REQUEST['p2index'])) {
+	$uindex = $_REQUEST['p2index'];
+}
+
+if (!empty($_REQUEST['uniqid'])) {
+	$uindex = $_REQUEST['uniqid'];
+}
+
+if (!empty($_REQUEST['dup'])) {
+	$uindex = $_REQUEST['dup'];
+}
+
+$ph2found = false;
+if (isset($uindex)) {
+	foreach ($a_phase2 as $p2index => $ph2) {
+		if ($ph2['uniqid'] == $uindex) {
+			$ph2found = true;
+			break;
+		}
+	}
+}
+
+if ($ph2found === true) {
+	$pconfig['ikeid'] = $ph2['ikeid'];
+	$pconfig['disabled'] = isset($ph2['disabled']);
+	$pconfig['mode'] = $ph2['mode'];
+	$pconfig['descr'] = $ph2['descr'];
+	$pconfig['uniqid'] = $ph2['uniqid'];
+
+	if (!empty($ph2['natlocalid'])) {
+		idinfo_to_pconfig("natlocal", $ph2['natlocalid'], $pconfig);
+	}
+	idinfo_to_pconfig("local", $ph2['localid'], $pconfig);
+	idinfo_to_pconfig("remote", $ph2['remoteid'], $pconfig);
+
+	$pconfig['proto'] = $ph2['protocol'];
+	ealgos_to_pconfig($ph2['encryption-algorithm-option'], $pconfig);
+	$pconfig['halgos'] = $ph2['hash-algorithm-option'];
+	$pconfig['pfsgroup'] = $ph2['pfsgroup'];
+	$pconfig['lifetime'] = $ph2['lifetime'];
+	$pconfig['pinghost'] = $ph2['pinghost'];
+	$pconfig['reqid'] = $ph2['reqid'];
+
+	if (isset($ph2['mobile'])) {
+		$pconfig['mobile'] = true;
+		$pconfig['remoteid_type'] = "mobile";
+	}
+} else {
+	$pconfig['ikeid'] = $_REQUEST['ikeid'];
+
+	/* defaults */
+	$pconfig['localid_type'] = "lan";
+	$pconfig['remoteid_type'] = "network";
+	$pconfig['proto'] = "esp";
+	$pconfig['ealgos'] = explode(",", "aes,aes128gcm");
+	$pconfig['keylen_aes'] = 128;
+	$pconfig['keylen_aes128gcm'] = 128;
+	$pconfig['halgos'] = explode(",", "hmac_sha256");
+	$pconfig['pfsgroup'] = "14";
+	$pconfig['lifetime'] = "3600";
+	$pconfig['uniqid'] = uniqid();
+
+	/* mobile client */
+	if ($_REQUEST['mobile']) {
+		$pconfig['mobile']=true;
+		$pconfig['remoteid_type'] = "mobile";
+	}
+}
+
+unset($ph2);
+if (!empty($_REQUEST['dup'])) {
+	unset($uindex);
+	unset($p2index);
+	$pconfig['uniqid'] = uniqid();
+	$pconfig['reqid'] = ipsec_new_reqid();
+}
+
+if ($_POST['save']) {
+
+	unset($input_errors);
+
+	/* Check if the user is switching away from VTI */
+	$vti_switched = ($ph2found && ($pconfig['mode'] == "vti") && ($_POST['mode'] != "vti"));
+
+	$pconfig = $_POST;
+
+	if (!isset($_POST['ikeid'])) {
+		$input_errors[] = gettext("A valid ikeid must be specified.");
+	}
+
+	/* input validation */
+	$reqdfields = explode(" ", "localid_type uniqid");
+	$reqdfieldsn = array(gettext("Local network type"), gettext("Unique Identifier"));
+	if (!isset($pconfig['mobile'])) {
+		$reqdfields[] = "remoteid_type";
+		$reqdfieldsn[] = gettext("Remote network type");
+	}
+
+	do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
+
+	if (($pconfig['mode'] == "tunnel") || ($pconfig['mode'] == "tunnel6") || ($pconfig['mode'] == "vti")) {
+		switch ($pconfig['localid_type']) {
+			case "network":
+				if (($pconfig['localid_netbits'] != 0 && !$pconfig['localid_netbits']) || !is_numericint($pconfig['localid_netbits'])) {
+					$input_errors[] = gettext("A valid local network bit count must be specified.");
+				}
+			case "address":
+				if (!$pconfig['localid_address'] || !is_ipaddr($pconfig['localid_address'])) {
+					$input_errors[] = gettext("A valid local network IP address must be specified.");
+				} elseif ($pconfig['mode'] == "vti") {
+					if (!is_ipaddr($pconfig['localid_address'])) {
+						$input_errors[] = gettext("VTI requires a valid local address");
+					}
+				} elseif (is_ipaddrv4($pconfig['localid_address']) && ($pconfig['mode'] != "tunnel")) {
+					$input_errors[] = gettext("An IPv4 local address was specified but the mode is not set to tunnel");
+				} elseif (is_ipaddrv6($pconfig['localid_address']) && ($pconfig['mode'] != "tunnel6")) {
+					$input_errors[] = gettext("An IPv6 local address was specified but the mode is not set to tunnel6");
+				}
+				break;
+			default:
+				if (($pconfig['mode'] == "vti") && !is_ipaddr($pconfig['localid_address'])) {
+					$input_errors[] = gettext("VTI requires a valid local network or IP address for its endpoint address, it cannot use a network macro for a different interface (e.g. LAN).");
+				}
+
+		}
+		/* Check if the localid_type is an interface, to confirm if it has a valid subnet. */
+		if (is_array($config['interfaces'][$pconfig['localid_type']])) {
+			// Don't let an empty subnet into racoon.conf, it can cause parse errors. Ticket #2201.
+			$address = get_interface_ip($pconfig['localid_type']);
+			$netbits = get_interface_subnet($pconfig['localid_type']);
+
+			if (empty($address) || empty($netbits)) {
+				$input_errors[] = gettext("Invalid Local Network.") . " " . sprintf(gettext("%s has no subnet."), convert_friendly_interface_to_friendly_descr($pconfig['localid_type']));
+			}
+		}
+
+		if (!empty($pconfig['natlocalid_address'])) {
+			switch ($pconfig['natlocalid_type']) {
+				case "network":
+					if (($pconfig['natlocalid_netbits'] != 0 && !$pconfig['natlocalid_netbits']) || !is_numericint($pconfig['natlocalid_netbits'])) {
+						$input_errors[] = gettext("A valid NAT local network bit count must be specified.");
+					}
+					if ($pconfig['localid_type'] == "address") {
+						$input_errors[] = gettext("A network type address cannot be configured for NAT while only an address type is selected for local source.");
+					}
+				case "address":
+					if (!empty($pconfig['natlocalid_address']) && !is_ipaddr($pconfig['natlocalid_address'])) {
+						$input_errors[] = gettext("A valid NAT local network IP address must be specified.");
+					} elseif (is_ipaddrv4($pconfig['natlocalid_address']) && ($pconfig['mode'] != "tunnel")) {
+						$input_errors[] = gettext("A valid NAT local network IPv4 address must be specified or Mode needs to be changed to IPv6");
+					} elseif (is_ipaddrv6($pconfig['natlocalid_address']) && ($pconfig['mode'] != "tunnel6")) {
+						$input_errors[] = gettext("A valid NAT local network IPv6 address must be specified or Mode needs to be changed to IPv4");
+					}
+					break;
+			}
+
+			if (is_array($config['interfaces'][$pconfig['natlocalid_type']])) {
+				// Don't let an empty subnet into racoon.conf, it can cause parse errors. Ticket #2201.
+				$address = get_interface_ip($pconfig['natlocalid_type']);
+				$netbits = get_interface_subnet($pconfig['natlocalid_type']);
+
+				if (empty($address) || empty($netbits)) {
+					$input_errors[] = gettext("Invalid Local Network.") . " " . sprintf(gettext("%s has no subnet."), convert_friendly_interface_to_friendly_descr($pconfig['natlocalid_type']));
+				}
+			}
+		}
+
+		switch ($pconfig['remoteid_type']) {
+			case "network":
+				if (($pconfig['remoteid_netbits'] != 0 && !$pconfig['remoteid_netbits']) || !is_numericint($pconfig['remoteid_netbits'])) {
+					$input_errors[] = gettext("A valid remote network bit count must be specified.");
+				}
+			case "address":
+				if (!$pconfig['remoteid_address'] || !is_ipaddr($pconfig['remoteid_address'])) {
+					$input_errors[] = gettext("A valid remote network IP address must be specified.");
+				} elseif ($pconfig['mode'] == "vti") {
+					if (!is_ipaddr($pconfig['remoteid_address'])) {
+						$input_errors[] = gettext("VTI requires a valid remote address");
+					}
+				} elseif (is_ipaddrv4($pconfig['remoteid_address']) && ($pconfig['mode'] != "tunnel")) {
+					$input_errors[] = gettext("An IPv4 remote network was specified but the mode is not set to tunnel");
+				} elseif (is_ipaddrv6($pconfig['remoteid_address']) && ($pconfig['mode'] != "tunnel6")) {
+					$input_errors[] = gettext("An IPv6 remote network was specified but the mode is not set to tunnel6");
+				}
+				break;
+		}
+	}
+	/* Validate enabled phase2's are not duplicates */
+	if (isset($pconfig['mobile'])) {
+		/* User is adding phase 2 for mobile phase1 */
+		if ($pconfig['mode'] == "vti") {
+			$input_errors[] = gettext("VTI is not compatible with mobile IPsec.");
+		}
+
+		foreach ($a_phase2 as $key => $name) {
+			if (isset($name['mobile']) && $name['uniqid'] != $pconfig['uniqid']) {
+				/* check duplicate localids only for mobile clients */
+				$localid_data = ipsec_idinfo_to_cidr($name['localid'], false, $name['mode']);
+				$entered = array();
+				$entered['type'] = $pconfig['localid_type'];
+
+				if (isset($pconfig['localid_address'])) {
+					$entered['address'] = $pconfig['localid_address'];
+				}
+
+				if (isset($pconfig['localid_netbits'])) {
+					$entered['netbits'] = $pconfig['localid_netbits'];
+				}
+
+				$entered_localid_data = ipsec_idinfo_to_cidr($entered, false, $pconfig['mode']);
+				if ($localid_data == $entered_localid_data) {
+					/* adding new p2 entry */
+					$input_errors[] = gettext("Phase2 with this Local Network is already defined for mobile clients.");
+					break;
+				}
+			}
+		}
+	} else {
+		/* User is adding phase 2 for site-to-site phase1 */
+		$input_error = 0;
+		foreach ($a_phase2 as $key => $name) {
+			if (!isset($name['mobile']) && $pconfig['ikeid'] == $name['ikeid'] && $pconfig['uniqid'] != $name['uniqid']) {
+				/* check duplicate subnets only for given phase1 */
+				$localid_data = ipsec_idinfo_to_cidr($name['localid'], false, $name['mode']);
+				$remoteid_data = ipsec_idinfo_to_cidr($name['remoteid'], false, $name['mode']);
+				$entered_local = array();
+				$entered_local['type'] = $pconfig['localid_type'];
+				if (isset($pconfig['localid_address'])) {
+					$entered_local['address'] = $pconfig['localid_address'];
+				}
+				if (isset($pconfig['localid_netbits'])) {
+					$entered_local['netbits'] = $pconfig['localid_netbits'];
+				}
+				$entered_localid_data = ipsec_idinfo_to_cidr($entered_local, false, $pconfig['mode']);
+				$entered_remote = array();
+				$entered_remote['type'] = $pconfig['remoteid_type'];
+				if (isset($pconfig['remoteid_address'])) {
+					$entered_remote['address'] = $pconfig['remoteid_address'];
+				}
+				if (isset($pconfig['remoteid_netbits'])) {
+					$entered_remote['netbits'] = $pconfig['remoteid_netbits'];
+				}
+				$entered_remoteid_data = ipsec_idinfo_to_cidr($entered_remote, false, $pconfig['mode']);
+				if ($localid_data == $entered_localid_data && $remoteid_data == $entered_remoteid_data) {
+					/* adding new p2 entry */
+					$input_errors[] = gettext("Phase2 with this Local/Remote networks combination is already defined for this Phase1.");
+					break;
+				}
+			}
+		}
+		foreach ($a_phase1 as $phase1) {
+			if ($phase1['ikeid'] == $pconfig['ikeid']) {
+				/* This is the P1 for this entry */
+				if ($vti_switched) {
+					/* Determine what this P2 interface would be */
+					if (is_interface_ipsec_vti_assigned($a_phase2[$p2index])) {
+						$input_errors[] = gettext("Cannot switch away from VTI while the interface is assigned. Remove the interface assignment before switching away from VTI.");
+					}
+				}
+				/* validate its remote-gateway and local interface isn't within tunnel */
+				$entered_local = array();
+				$entered_local['type'] = $pconfig['localid_type'];
+				if (isset($pconfig['localid_address'])) {
+					$entered_local['address'] = $pconfig['localid_address'];
+				}
+				if (isset($pconfig['localid_netbits'])) {
+					$entered_local['netbits'] = $pconfig['localid_netbits'];
+				}
+				$entered_localid_data = ipsec_idinfo_to_cidr($entered_local, false, $pconfig['mode']);
+				list($entered_local_network, $entered_local_mask) = explode('/', $entered_localid_data);
+				$entered_remote = array();
+				$entered_remote['type'] = $pconfig['remoteid_type'];
+				if (isset($pconfig['remoteid_address'])) {
+					$entered_remote['address'] = $pconfig['remoteid_address'];
+				}
+				if (isset($pconfig['remoteid_netbits'])) {
+					$entered_remote['netbits'] = $pconfig['remoteid_netbits'];
+				}
+				$entered_remoteid_data = ipsec_idinfo_to_cidr($entered_remote, false, $pconfig['mode']);
+				list($entered_remote_network, $entered_remote_mask) = explode('/', $entered_remoteid_data);
+				if ($phase1['protocol'] == "inet6") {
+					$if = get_failover_interface($phase1['interface'], "inet6");
+					$interfaceip = get_interface_ipv6($if);
+				} else {
+					$if = get_failover_interface($phase1['interface']);
+					$interfaceip = get_interface_ip($if);
+				}
+				/* skip validation for hostnames, they're subject to change anyway */
+				if (is_ipaddr($phase1['remote-gateway'])) {
+					if ($pconfig['mode'] == "tunnel") {
+						if (check_subnets_overlap($interfaceip, 32, $entered_local_network, $entered_local_mask) && check_subnets_overlap($phase1['remote-gateway'], 32, $entered_remote_network, $entered_remote_mask)) {
+							$input_errors[] = gettext("The local and remote networks of a phase 2 entry cannot overlap the outside of the tunnel (interface and remote gateway) configured in its phase 1.");
+							break;
+						}
+					} else if ($pconfig['mode'] == "tunnel6") {
+						if (check_subnetsv6_overlap($interfaceip, 128, $entered_local_network, $entered_local_mask) && check_subnets_overlap($phase1['remote-gateway'], 128, $entered_remote_network, $entered_remote_mask)) {
+							$input_errors[] = gettext("The local and remote networks of a phase 2 entry cannot overlap the outside of the tunnel (interface and remote gateway) configured in its phase 1.");
+							break;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	/* For ESP protocol, handle encryption algorithms */
+	if ($pconfig['proto'] == "esp") {
+		$ealgos = pconfig_to_ealgos($pconfig);
+
+		if (!count($ealgos)) {
+			$input_errors[] = gettext("At least one encryption algorithm must be selected.");
+		} else {
+			foreach ($ealgos as $ealgo) {
+				if (empty($pconfig['halgos'])) {
+					if (!strpos($ealgo['name'], "gcm")) {
+						$input_errors[] = gettext("At least one hashing algorithm needs to be selected.");
+						break;
+					}
+				}
+			}
+		}
+	}
+	if (($_POST['lifetime'] && !is_numericint($_POST['lifetime']))) {
+		$input_errors[] = gettext("The P2 lifetime must be an integer.");
+	}
+	if (($pconfig['mode'] == "vti") && $pconfig['disabled']) {
+		$input_errors[] = gettext("Cannot disable a VTI Phase 2 while the interface is assigned. Remove the interface assignment before disabling this P2.");
+	}
+
+	if (!$input_errors) {
+
+		$ph2ent = array();
+		$ph2ent['ikeid'] = $pconfig['ikeid'];
+		$ph2ent['uniqid'] = $pconfig['uniqid'];
+		$ph2ent['mode'] = $pconfig['mode'];
+		$ph2ent['disabled'] = $pconfig['disabled'] ? true : false;
+		if (!isset($pconfig['reqid'])) {
+			$ph2ent['reqid'] = ipsec_new_reqid();
+		} else {
+			$ph2ent['reqid'] = $pconfig['reqid'];
+		}
+
+		if (($ph2ent['mode'] == "tunnel") || ($ph2ent['mode'] == "tunnel6") || ($ph2ent['mode'] == "vti")) {
+			if (!empty($pconfig['natlocalid_address'])) {
+				$ph2ent['natlocalid'] = pconfig_to_idinfo("natlocal", $pconfig);
+			}
+			$ph2ent['localid'] = pconfig_to_idinfo("local", $pconfig);
+			$ph2ent['remoteid'] = pconfig_to_idinfo("remote", $pconfig);
+		}
+
+		// create/delete vtimaps on P2 add/mod
+		$need_vtimap = true;
+		$ph1ent = ipsec_get_phase1($ph2ent['ikeid']);
+		$vtisubnet_spec = ipsec_vti($ph1ent, true);
+		foreach ($a_phase2 as $ph2) {
+			if (($ph2['uniqid'] == $ph2ent['uniqid']) &&
+			    ($ph2['mode'] == 'vti')) {
+				// config P2 mode is VTI and not changed
+				$need_vtimap = false;
+			}
+		}
+		if (($ph2ent['mode'] == "vti") && $need_vtimap) {
+			// add new vtimap if needed
+			if (($ph1ent['iketype'] == 'ikev1') ||
+			    isset($ph1ent['splitconn'])) {
+				if ($vtisubnet_spec ||
+				    is_array($vtisubnet_spec)) {
+					$a_vtimaps[] = ipsec_create_vtimap(
+					    $ph1ent['ikeid'],
+					    count($vtisubnet_spec));
+				} else {
+					$a_vtimaps[] = ipsec_create_vtimap(
+					    $ph1ent['ikeid'], 0);
+				}
+			} else {
+				if (!$vtisubnet_spec &&
+				    !is_array($vtisubnet_spec)) {
+					$a_vtimaps[] = ipsec_create_vtimap(
+					    $ph1ent['ikeid'], 0);
+				}
+			}
+		} else if (($ph2ent['mode'] != "vti") && !$need_vtimap &&
+		    (count($vtisubnet_spec) > 0)) {
+			// del vtimap on P2 mode change
+			ipsec_del_vtimap($ph2ent);
+		}
+
+		$ph2ent['protocol'] = $pconfig['proto'];
+		$ph2ent['encryption-algorithm-option'] = $ealgos;
+		if (!empty($pconfig['halgos'])) {
+			$ph2ent['hash-algorithm-option'] = $pconfig['halgos'];
+		} else {
+			unset($ph2ent['hash-algorithm-option']);
+		}
+		$ph2ent['pfsgroup'] = $pconfig['pfsgroup'];
+		$ph2ent['lifetime'] = $pconfig['lifetime'];
+		$ph2ent['pinghost'] = $pconfig['pinghost'];
+		$ph2ent['descr'] = $pconfig['descr'];
+
+		if (isset($pconfig['mobile'])) {
+			$ph2ent['mobile'] = true;
+		}
+
+		if ($ph2found === true && $a_phase2[$p2index]) {
+			$a_phase2[$p2index] = $ph2ent;
+		} else {
+			$a_phase2[] = $ph2ent;
+		}
+
+		write_config(gettext("Saved IPsec tunnel Phase 2 configuration."));
+		mark_subsystem_dirty('ipsec');
+
+		header("Location: vpn_ipsec.php");
+		exit;
+	}
+}
+
+$localid_help_tunnel  = "Local network component of this IPsec security association.";
+$localid_help_vti     = "Local point-to-point IPsec interface tunnel network address.";
+$localid_help_mobile  = "Network reachable by mobile IPsec clients.";
+$remoteid_help_tunnel = "Remote network component of this IPsec security association.";
+$remoteid_help_vti    = "Remote point-to-point IPsec interface tunnel network address.";
+
+if ($pconfig['mobile']) {
+	$pgtitle = array(gettext("VPN"), gettext("IPsec"), gettext("Mobile Clients"), gettext("Edit Phase 2"));
+	$pglinks = array("", "vpn_ipsec.php", "vpn_ipsec_mobile.php", "@self");
+	$editing_mobile = true;
+} else {
+	$pgtitle = array(gettext("VPN"), gettext("IPsec"), gettext("Tunnels"), gettext("Edit Phase 2"));
+	$pglinks = array("", "vpn_ipsec.php", "vpn_ipsec.php", "@self");
+	$editing_mobile = false;
+}
+$shortcut_section = "ipsec";
+
+include("head.inc");
+
+function pconfig_to_ealgos(& $pconfig) {
+	global $p2_ealgos;
+
+	$ealgos = array();
+	if (is_array($pconfig['ealgos'])) {
+		foreach ($p2_ealgos as $algo_name => $algo_data) {
+			if (in_array($algo_name, $pconfig['ealgos'])) {
+				$ealg = array();
+				$ealg['name'] = $algo_name;
+				if (is_array($algo_data['keysel'])) {
+					$ealg['keylen'] = $_POST["keylen_".$algo_name];
+				}
+				$ealgos[] = $ealg;
+			}
+		}
+	}
+
+	return $ealgos;
+}
+
+function ealgos_to_pconfig(& $ealgos, & $pconfig) {
+
+	$pconfig['ealgos'] = array();
+	foreach ($ealgos as $algo_data) {
+		$pconfig['ealgos'][] = $algo_data['name'];
+		if (isset($algo_data['keylen'])) {
+			$pconfig["keylen_".$algo_data['name']] = $algo_data['keylen'];
+		}
+	}
+
+	return $ealgos;
+}
+
+function pconfig_to_idinfo($prefix, & $pconfig) {
+
+	$type = $pconfig[$prefix."id_type"];
+	$address = $pconfig[$prefix."id_address"];
+	$netbits = $pconfig[$prefix."id_netbits"];
+
+	switch ($type) {
+		case "address":
+			return array('type' => $type, 'address' => $address);
+		case "network":
+			return array('type' => $type, 'address' => $address, 'netbits' => $netbits);
+		default:
+			return array('type' => $type);
+	}
+}
+
+function idinfo_to_pconfig($prefix, & $idinfo, & $pconfig) {
+
+	switch ($idinfo['type']) {
+		case "address":
+			$pconfig[$prefix."id_type"] = $idinfo['type'];
+			$pconfig[$prefix."id_address"] = $idinfo['address'];
+			break;
+		case "network":
+			$pconfig[$prefix."id_type"] = $idinfo['type'];
+			$pconfig[$prefix."id_address"] = $idinfo['address'];
+			$pconfig[$prefix."id_netbits"] = $idinfo['netbits'];
+			break;
+		default:
+			$pconfig[$prefix."id_type"] = $idinfo['type'];
+			break;
+	}
+}
+
+if ($input_errors) {
+	print_input_errors($input_errors);
+}
+
+$tab_array = array();
+$tab_array[0] = array(gettext("Tunnels"), !$editing_mobile, "vpn_ipsec.php");
+$tab_array[1] = array(gettext("Mobile Clients"), $editing_mobile, "vpn_ipsec_mobile.php");
+$tab_array[2] = array(gettext("Pre-Shared Keys"), false, "vpn_ipsec_keys.php");
+$tab_array[3] = array(gettext("Advanced Settings"), false, "vpn_ipsec_settings.php");
+display_top_tabs($tab_array);
+
+$form = new Form();
+
+$section = new Form_Section('General Information');
+
+$section->addInput(new Form_Checkbox(
+	'disabled',
+	'Disabled',
+	'Disable this phase 2 entry without removing it from the list. ',
+	$pconfig['disabled']
+));
+
+$section->addInput(new Form_Select(
+	'mode',
+	'*Mode',
+	$pconfig['mode'],
+	$p2_modes
+));
+
+$group = new Form_Group('*Local Network');
+$group->addClass('opt_localid');
+
+$subnetarray = get_configured_interface_with_descr();
+foreach ($subnetarray as $ifname => $ifdescr) {
+	$subnetarray[$ifname] = $ifdescr . ' subnet';
+}
+
+$group->add(new Form_Select(
+	'localid_type',
+	null,
+	$pconfig['localid_type'],
+	['address' => gettext('Address'), 'network' => gettext('Network')] + $subnetarray
+))->setHelp('Type');
+
+$group->add(new Form_IpAddress(
+	'localid_address',
+	null,
+	$pconfig['localid_address']
+))->setHelp('Address')->addMask('localid_netbits', $pconfig['localid_netbits'], 128, 0);
+
+$group->setHelp('%s', '<span id="opt_localid_help"></span>');
+$section->add($group);
+
+$group = new Form_Group('NAT/BINAT translation');
+$group->addClass('opt_natid');
+
+$subnetarray = get_configured_interface_with_descr();
+foreach ($subnetarray as $ifname => $ifdescr) {
+	$subnetarray[$ifname] = $ifdescr . ' subnet';
+}
+
+// Tack none, address & network on the beginning
+$subnetarray = array('none' => gettext('None'), 'address' => gettext('Address'), 'network' => gettext('Network')) + $subnetarray;
+
+$group->add(new Form_Select(
+	'natlocalid_type',
+	null,
+	$pconfig['natlocalid_type'],
+	$subnetarray
+))->setHelp('Type');
+
+$group->add(new Form_IpAddress(
+	'natlocalid_address',
+	null,
+	$pconfig['natlocalid_address']
+))->setHelp('Address')->addMask('natlocalid_netbits', $pconfig['natlocalid_netbits'], 128, 0);
+
+$group->setHelp('If NAT/BINAT is required on this network specify the address to be translated');
+$section->add($group);
+
+if (!isset($pconfig['mobile'])) {
+	$group = new Form_Group('*Remote Network');
+	$group->addClass('opt_remoteid');
+
+	$group->add(new Form_Select(
+		'remoteid_type',
+		null,
+		$pconfig['remoteid_type'],
+		array('address' => gettext('Address'), 'network' => gettext('Network'))
+	))->setHelp('Type');
+
+	$group->add(new Form_IpAddress(
+		'remoteid_address',
+		null,
+		$pconfig['remoteid_address']
+	))->setHelp('Address')->addMask('remoteid_netbits', $pconfig['remoteid_netbits'], 128, 0);
+
+	$group->setHelp('%s', '<span id="opt_remoteid_help"></span>');
+	$section->add($group);
+}
+
+$section->addInput(new Form_Input(
+	'descr',
+	'Description',
+	'text',
+	$pconfig['descr']
+))->setHelp('A description may be entered here for administrative reference (not parsed).');
+
+$form->add($section);
+
+$section = new Form_Section('Phase 2 Proposal (SA/Key Exchange)');
+
+$section->addInput(new Form_Select(
+	'proto',
+	'*Protocol',
+	$pconfig['proto'],
+	$p2_protos
+))->setHelp('Encapsulating Security Payload (ESP) is encryption, Authentication Header (AH) is authentication only.');
+
+$i = 0;
+$rows = count($p2_ealgos) - 1;
+
+foreach ($p2_ealgos as $algo => $algodata) {
+	$group = new Form_Group($i == 0 ? '*Encryption Algorithms':'');
+	$group->addClass('encalg');
+
+	// Note: ID attribute of each element created is to be unique.  Not being used, suppressing it.
+	$group->add(new Form_Checkbox(
+		'ealgos[]',
+		null,
+		$algodata['name'],
+		(is_array($pconfig['ealgos']) && in_array($algo, $pconfig['ealgos'])),
+		$algo
+	))->addClass('multi ealgoschk')->setAttribute('id', $algodata['name']);
+
+	if (is_array($algodata['keysel'])) {
+		$list = array();
+		$key_hi = $algodata['keysel']['hi'];
+		$key_lo = $algodata['keysel']['lo'];
+		$key_step = $algodata['keysel']['step'];
+		for ($keylen = $key_hi; $keylen >= $key_lo; $keylen -= $key_step) {
+			$list[$keylen] = $keylen . ' bits';
+		}
+
+		$group->add(new Form_Select(
+			'keylen_' . $algo,
+			null,
+			$pconfig["keylen_".$algo],
+			['auto' => gettext('Auto')] + $list
+		));
+	}
+
+
+	if ($i == $rows) {
+		$group->setHelp('Note: Blowfish, 3DES, and CAST128 provide weak security and should be avoided.');
+	}
+
+	$i++;
+	$section->add($group);
+}
+
+$group = new Form_Group('*Hash Algorithms');
+
+foreach ($p2_halgos as $algo => $algoname) {
+	// Note: ID attribute of each element created is to be unique.  Not being used, suppressing it.
+	$group->add(new Form_Checkbox(
+		'halgos[]',
+		null,
+		$algoname,
+		(empty($pconfig['halgos']) ? '' : in_array($algo, $pconfig['halgos'])),
+		$algo
+	))->addClass('multi')->setAttribute('id');
+
+	$group->setHelp('Note: Hash is ignored with GCM algorithms. MD5 and SHA1 provide weak security and should be avoided.');
+}
+
+$section->add($group);
+
+$sm = (!isset($pconfig['mobile']) || !isset($a_client['pfs_group']));
+$helpstr = $sm ? '':'Set globally in mobile client options. ';
+$helpstr .= 'Note: Groups 1, 2, 5, 22, 23, and 24 provide weak security and should be avoided.';
+
+$section->addInput(new Form_Select(
+	'pfsgroup',
+	'PFS key group',
+	$pconfig['pfsgroup'],
+	$sm ? $p2_pfskeygroups:array()
+))->setHelp($helpstr);
+
+$section->addInput(new Form_Input(
+	'lifetime',
+	'Lifetime',
+	'number',
+	$pconfig['lifetime']
+))->setHelp('Specifies how often the connection must be rekeyed, in seconds');
+
+$form->add($section);
+
+$section = new Form_Section('Advanced Configuration');
+
+$section->addInput(new Form_IpAddress(
+	'pinghost',
+	'Automatically ping host',
+	$pconfig['pinghost']
+))->setHelp('IP Address');
+
+// Hidden inputs
+if ($pconfig['mobile']) {
+	$form->addGlobal(new Form_Input(
+		'mobile',
+		null,
+		'hidden',
+		'true'
+	));
+}
+
+$form->addGlobal(new Form_Input(
+	'ikeid',
+	null,
+	'hidden',
+	$pconfig['ikeid']
+));
+
+if (!empty($pconfig['reqid'])) {
+	$form->addGlobal(new Form_Input(
+		'reqid',
+		null,
+		'hidden',
+		$pconfig['reqid']
+	));
+}
+
+$form->addGlobal(new Form_Input(
+	'uniqid',
+	null,
+	'hidden',
+	$pconfig['uniqid']
+));
+
+$form->add($section);
+
+print($form);
+
+?>
+
+<script type="text/javascript">
+//<![CDATA[
+events.push(function() {
+	$("form").submit(function() {
+		disableInput('localid_type', false);
+		disableInput('remoteid_type', false);
+	});
+
+	// ---------- On changing "Mode" ----------------------------------------------------------------------------------
+	function change_mode() {
+
+		value = $('#mode').val();
+
+		disableInput('localid_type', false);
+		disableInput('remoteid_type', false);
+		if ((value == 'tunnel') || (value == 'tunnel6')) {
+			hideClass('opt_localid', false);
+			hideClass('opt_natid', false);
+			$('#opt_localid_help').html("<?=$localid_help_mobile?>");
+
+<?php	if (!isset($pconfig['mobile'])): ?>
+			hideClass('opt_remoteid', false);
+			hideClass('opt_natid', false);
+			$('#opt_localid_help').html("<?=$localid_help_tunnel?>");
+			$('#opt_remoteid_help').html("<?=$remoteid_help_tunnel?>");
+<?php	endif; ?>
+		} else if (value == 'vti') {
+			hideClass('opt_localid', false);
+			hideClass('opt_natid', true);
+			hideClass('opt_remoteid', false);
+			$('#localid_type').val('address');
+			disableInput('localid_type', false);
+			typesel_change_local(30);
+			$('#remoteid_type').val('address');
+			disableInput('remoteid_type', false);
+			typesel_change_remote(30);
+			$('#opt_localid_help').html("<?=$localid_help_vti?>");
+			$('#opt_remoteid_help').html("<?=$remoteid_help_vti?>");
+		} else {
+			hideClass('opt_localid', true);
+			hideClass('opt_natid', true);
+<?php	if (!isset($pconfig['mobile'])): ?>
+			hideClass('opt_remoteid', true);
+<?php	endif; ?>
+		}
+	}
+
+	// ---------- On changing "NAT/BINAT" -----------------------------------------------------------------------------
+	function typesel_change_natlocal(bits) {
+		var value = $('#mode').val();
+
+		if (typeof(bits) === "undefined") {
+			if (value === "tunnel") {
+				bits = 24;
+			} else if (value === "tunnel6") {
+				bits = 64;
+			}
+		}
+
+		var address_is_blank = !/\S/.test($('#natlocalid_address').val());
+
+		switch ($("#natlocalid_type option:selected").index()) {
+			case 0: /* none */
+				disableInput('natlocalid_address', true);
+
+				if (address_is_blank) {
+					$('#natlocalid_netbits').val(0);
+				}
+
+				disableInput('natlocalid_netbits', true);
+				break;
+			case 1: /* address */
+				disableInput('natlocalid_address', false);
+
+				if (address_is_blank) {
+					$('#natlocalid_netbits').val(bits);
+				}
+
+				disableInput('natlocalid_netbits', true);
+				break;
+			case 2: /* network */
+				disableInput('natlocalid_address', false);
+				disableInput('natlocalid_netbits', false);
+				break;
+			default:
+				$('#natlocalid_address').val("");
+				disableInput('natlocalid_address', true);
+
+				if (address_is_blank) {
+					$('#natlocalid_netbits').val(0);
+				}
+
+				disableInput('natlocalid_netbits', true);
+				break;
+		}
+	}
+
+	// ---------- On changing "Local Network" -------------------------------------------------------------------------
+	function typesel_change_local(bits) {
+		var value = $('#mode').val();
+
+		if (typeof(bits) === "undefined") {
+			if (value === "tunnel") {
+				bits = 24;
+			} else if (value === "tunnel6") {
+				bits = 64;
+			}
+		}
+
+		var address_is_blank = !/\S/.test($('#localid_address').val());
+
+		switch ($("#localid_type option:selected").index()) {
+			case 0: /* single */
+				disableInput('localid_address', false);
+
+				if (address_is_blank) {
+					$('#localid_netbits').val(0);
+				}
+
+				disableInput('localid_netbits', true);
+				break;
+			case 1: /* network */
+				disableInput('localid_address', false);
+
+				if (address_is_blank) {
+					$('#localid_netbits').val(bits);
+				}
+
+				disableInput('localid_netbits', false);
+				break;
+			case 3: /* none */
+				disableInput('localid_address', true);
+				disableInput('localid_netbits', true);
+				break;
+			default:
+				$('#localid_address').val("");
+				disableInput('localid_address', true);
+
+				if (address_is_blank) {
+					$('#localid_netbits').val(0);
+				}
+
+				disableInput('localid_netbits', true);
+				break;
+		}
+	}
+
+<?php
+
+	// ---------- On changing "Remote Network" ------------------------------------------------------------------------
+	if (!isset($pconfig['mobile'])): ?>
+
+		function typesel_change_remote(bits) {
+
+			var value = $('#mode').val();
+
+			if (typeof(bits) === "undefined") {
+				if (value === "tunnel") {
+					bits = 24;
+				} else if (value === "tunnel6") {
+					bits = 64;
+				}
+			}
+
+			var address_is_blank = !/\S/.test($('#remoteid_address').val());
+
+			switch ($("#remoteid_type option:selected").index()) {
+				case 0: /* single */
+					disableInput('remoteid_address', false);
+
+					if (address_is_blank) {
+						$('#remoteid_netbits').val(0);
+					}
+
+					disableInput('remoteid_netbits', true);
+					break;
+				case 1: /* network */
+					disableInput('remoteid_address', false);
+
+					if (address_is_blank) {
+						$('#remoteid_netbits').val(bits);
+					}
+
+					disableInput('remoteid_netbits', false);
+					break;
+				case 3: /* none */
+					disableInput('remoteid_address', true);
+					disableInput('remoteid_netbits', true);
+					break;
+				default:
+					$('#remoteid_address').val("");
+					disableInput('remoteid_address', true);
+
+					if (address_is_blank) {
+						$('#remoteid_netbits').val(0);
+					}
+
+					disableInput('remoteid_netbits', true);
+					break;
+			}
+		}
+
+	<?php endif; ?>
+
+	function change_protocol() {
+		hideClass('encalg', ($('#proto').val() != 'esp'));
+	}
+
+	function change_aead() {
+		var notaead = ['AES', 'Blowfish', '3DES', 'CAST128'];
+		var arrayLength = notaead.length;
+		for (var i = 0; i < arrayLength; i++) {
+			if ($('#' + notaead[i]).prop('checked')) {
+				$("input[name='halgos[]']").prop("disabled", false);
+				return;
+			} 
+		}
+		$("input[name='halgos[]']").prop("disabled", true);
+	}
+
+	// ---------- Monitor elements for change and call the appropriate display functions ----------
+
+	 // Protocol
+	$('#proto').change(function () {
+		change_protocol();
+	});
+
+	// AEAD
+	$(".ealgoschk").click(function () {
+		change_aead();
+	});
+
+	 // Localid
+	$('#localid_type').change(function () {
+		typesel_change_local(<?=htmlspecialchars($pconfig['localid_netbits'])?>);
+	});
+
+	 // Remoteid
+	$('#remoteid_type').change(function () {
+		typesel_change_remote(<?=htmlspecialchars($pconfig['remoteid_netbits'])?>);
+	});
+
+	 // NATLocalid
+	$('#natlocalid_type').change(function () {
+		typesel_change_natlocal(<?=htmlspecialchars($pconfig['natlocalid_netbits'])?>);
+	});
+
+	 // Mode
+	$('#mode').change(function () {
+		change_mode();
+	});
+
+	// ---------- On initial page load ------------------------------------------------------------
+
+	change_mode();
+	change_protocol();
+	change_aead();
+	typesel_change_local(<?=htmlspecialchars($pconfig['localid_netbits'])?>);
+	typesel_change_natlocal(<?=htmlspecialchars($pconfig['natlocalid_netbits'])?>);
+<?php
+	if (!isset($pconfig['mobile'])):
+?>
+		typesel_change_remote(<?=htmlspecialchars($pconfig['remoteid_netbits'])?>);
+<?php
+endif;
+?>
+});
+//]]>
+</script>
+<?php
+include("foot.inc");
