diff options
Diffstat (limited to 'src/usr/local/www/firewall_virtual_ip_edit.php')
-rw-r--r-- | src/usr/local/www/firewall_virtual_ip_edit.php | 572 |
1 files changed, 572 insertions, 0 deletions
diff --git a/src/usr/local/www/firewall_virtual_ip_edit.php b/src/usr/local/www/firewall_virtual_ip_edit.php new file mode 100644 index 0000000..6021115 --- /dev/null +++ b/src/usr/local/www/firewall_virtual_ip_edit.php @@ -0,0 +1,572 @@ +<?php +/* $Id$ */ +/* + + firewall_virtual_ip_edit.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Copyright (C) 2005 Bill Marquette <bill.marquette@gmail.com>. + All rights reserved. + + Includes code from m0n0wall which is: + Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Includes code from pfSense which is: + Copyright (C) 2004-2005 Scott Ullrich <geekgod@pfsense.com>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-firewall-virtualipaddress-edit +##|*NAME=Firewall: Virtual IP Address: Edit page +##|*DESCR=Allow access to the 'Firewall: Virtual IP Address: Edit' page. +##|*MATCH=firewall_virtual_ip_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("filter.inc"); +require("shaper.inc"); + +if (isset($_POST['referer'])) { + $referer = $_POST['referer']; +} else { + $referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/firewall_virtual_ip.php'); +} + +if (!is_array($config['virtualip']['vip'])) { + $config['virtualip']['vip'] = array(); +} +$a_vip = &$config['virtualip']['vip']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +function return_first_two_octets($ip) { + $ip_split = explode(".", $ip); + return $ip_split[0] . "." . $ip_split[1]; +} + +function find_last_used_vhid() { + global $config, $g; + $vhid = 0; + foreach ($config['virtualip']['vip'] as $vip) { + if ($vip['vhid'] > $vhid) { + $vhid = $vip['vhid']; + } + } + return $vhid; +} + +if (isset($id) && $a_vip[$id]) { + $pconfig['mode'] = $a_vip[$id]['mode']; + $pconfig['vhid'] = $a_vip[$id]['vhid']; + $pconfig['advskew'] = $a_vip[$id]['advskew']; + $pconfig['advbase'] = $a_vip[$id]['advbase']; + $pconfig['password'] = $a_vip[$id]['password']; + $pconfig['range'] = $a_vip[$id]['range']; + $pconfig['subnet'] = $a_vip[$id]['subnet']; + $pconfig['subnet_bits'] = $a_vip[$id]['subnet_bits']; + $pconfig['noexpand'] = $a_vip[$id]['noexpand']; + $pconfig['descr'] = $a_vip[$id]['descr']; + $pconfig['type'] = $a_vip[$id]['type']; + $pconfig['interface'] = $a_vip[$id]['interface']; + $pconfig['uniqid'] = $a_vip[$id]['interface']; +} else { + $lastvhid = find_last_used_vhid(); + $lastvhid++; + $pconfig['vhid'] = $lastvhid; + $pconfig['uniqid'] = uniqid(); +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "mode"); + $reqdfieldsn = array(gettext("Type")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if ($_POST['subnet']) { + $_POST['subnet'] = trim($_POST['subnet']); + } + + if ($_POST['subnet']) { + if (!is_ipaddr($_POST['subnet'])) { + $input_errors[] = gettext("A valid IP address must be specified."); + } else { + if (isset($id) && isset($a_vip[$id])) { + $ignore_if = $a_vip[$id]['interface']; + $ignore_mode = $a_vip[$id]['mode']; + if (isset($a_vip[$id]['vhid'])) { + $ignore_vhid = $a_vip[$id]['vhid']; + } + } else { + $ignore_if = $_POST['interface']; + $ignore_mode = $_POST['mode']; + } + + if (!isset($ignore_vhid)) { + $ignore_vhid = $_POST['vhid']; + } + + if ($ignore_mode == 'carp') { + $ignore_if .= "_vip{$ignore_vhid}"; + } else { + $ignore_if .= "_virtualip{$id}"; + } + + if (is_ipaddr_configured($_POST['subnet'], $ignore_if)) { + $input_errors[] = gettext("This IP address is being used by another interface or VIP."); + } + + unset($ignore_if, $ignore_mode); + } + } + + $natiflist = get_configured_interface_with_descr(); + foreach ($natiflist as $natif => $natdescr) { + if ($_POST['interface'] == $natif && (empty($config['interfaces'][$natif]['ipaddr']) && empty($config['interfaces'][$natif]['ipaddrv6']))) { + $input_errors[] = gettext("The interface chosen for the VIP has no IPv4 or IPv6 address configured so it cannot be used as a parent for the VIP."); + } + } + + /* ipalias and carp should not use network or broadcast address */ + if ($_POST['mode'] == "ipalias" || $_POST['mode'] == "carp") { + if (is_ipaddrv4($_POST['subnet']) && $_POST['subnet_bits'] != "32") { + $network_addr = gen_subnet($_POST['subnet'], $_POST['subnet_bits']); + $broadcast_addr = gen_subnet_max($_POST['subnet'], $_POST['subnet_bits']); + } else if (is_ipaddrv6($_POST['subnet']) && $_POST['subnet_bits'] != "128") { + $network_addr = gen_subnetv6($_POST['subnet'], $_POST['subnet_bits']); + $broadcast_addr = gen_subnetv6_max($_POST['subnet'], $_POST['subnet_bits']); + } + + if (isset($network_addr) && $_POST['subnet'] == $network_addr) { + $input_errors[] = gettext("You cannot use the network address for this VIP"); + } else if (isset($broadcast_addr) && $_POST['subnet'] == $broadcast_addr) { + $input_errors[] = gettext("You cannot use the broadcast address for this VIP"); + } + } + + /* make sure new ip is within the subnet of a valid ip + * on one of our interfaces (wan, lan optX) + */ + switch ($_POST['mode']) { + case 'carp': + /* verify against reusage of vhids */ + $idtracker = 0; + foreach ($config['virtualip']['vip'] as $vip) { + if ($vip['vhid'] == $_POST['vhid'] && $vip['interface'] == $_POST['interface'] && $idtracker <> $id) { + $input_errors[] = sprintf(gettext("VHID %s is already in use on interface %s. Pick a unique number on this interface."), $_POST['vhid'], convert_friendly_interface_to_friendly_descr($_POST['interface'])); + } + $idtracker++; + } + if (empty($_POST['password'])) { + $input_errors[] = gettext("You must specify a CARP password that is shared between the two VHID members."); + } + + if ($_POST['interface'] == 'lo0') { + $input_errors[] = gettext("For this type of vip localhost is not allowed."); + } else if (strpos($_POST['interface'], '_vip')) { + $input_errors[] = gettext("A CARP parent interface can only be used with IP Alias type Virtual IPs."); + } + break; + case 'ipalias': + if (strstr($_POST['interface'], "_vip")) { + if (is_ipaddrv4($_POST['subnet'])) { + $parent_ip = get_interface_ip($_POST['interface']); + $parent_sn = get_interface_subnet($_POST['interface']); + $subnet = gen_subnet($parent_ip, $parent_sn); + } else if (is_ipaddrv6($_POST['subnet'])) { + $parent_ip = get_interface_ipv6($_POST['interface']); + $parent_sn = get_interface_subnetv6($_POST['interface']); + $subnet = gen_subnetv6($parent_ip, $parent_sn); + } + if (isset($parent_ip) && !ip_in_subnet($_POST['subnet'], "{$subnet}/{$parent_sn}") && + !ip_in_interface_alias_subnet(link_carp_interface_to_parent($_POST['interface']), $_POST['subnet'])) { + $cannot_find = $_POST['subnet'] . "/" . $_POST['subnet_bits'] ; + $input_errors[] = sprintf(gettext("Sorry, we could not locate an interface with a matching subnet for %s. Please add an IP alias in this subnet on this interface."), $cannot_find); + } + unset($parent_ip, $parent_sn, $subnet); + } + break; + default: + if ($_POST['interface'] == 'lo0') { + $input_errors[] = gettext("For this type of vip localhost is not allowed."); + } else if (strpos($_POST['interface'], '_vip')) { + $input_errors[] = gettext("A CARP parent interface can only be used with IP Alias type Virtual IPs."); + } + break; + } + + if (!$input_errors) { + $vipent = array(); + + $vipent['mode'] = $_POST['mode']; + $vipent['interface'] = $_POST['interface']; + + /* ProxyARP specific fields */ + if ($_POST['mode'] === "proxyarp") { + if ($_POST['type'] == "range") { + $vipent['range']['from'] = $_POST['range_from']; + $vipent['range']['to'] = $_POST['range_to']; + + } + $vipent['noexpand'] = isset($_POST['noexpand']); + } + + /* CARP specific fields */ + if ($_POST['mode'] === "carp") { + $vipent['vhid'] = $_POST['vhid']; + $vipent['vhid'] = $_POST['uinqid']; + $vipent['advskew'] = $_POST['advskew']; + $vipent['advbase'] = $_POST['advbase']; + $vipent['password'] = $_POST['password']; + } + + /* Common fields */ + $vipent['descr'] = $_POST['descr']; + if (isset($_POST['type'])) { + $vipent['type'] = $_POST['type']; + } else { + $vipent['type'] = "single"; + } + + if ($vipent['type'] == "single" || $vipent['type'] == "network") { + if (!isset($_POST['subnet_bits'])) { + $vipent['subnet_bits'] = "32"; + } else { + $vipent['subnet_bits'] = $_POST['subnet_bits']; + } + $vipent['subnet'] = $_POST['subnet']; + } + + if (!isset($id)) { + $id = count($a_vip); + } + if (file_exists("{$g['tmp_path']}/.firewall_virtual_ip.apply")) { + $toapplylist = unserialize(file_get_contents("{$g['tmp_path']}/.firewall_virtual_ip.apply")); + } else { + $toapplylist = array(); + } + + $toapplylist[$id] = $a_vip[$id]; + if (!empty($a_vip[$id])) { + /* modify all virtual IP rules with this address */ + for ($i = 0; isset($config['nat']['rule'][$i]); $i++) { + if ($config['nat']['rule'][$i]['destination']['address'] == $a_vip[$id]['subnet']) { + $config['nat']['rule'][$i]['destination']['address'] = $vipent['subnet']; + } + } + } + $a_vip[$id] = $vipent; + + if (write_config()) { + mark_subsystem_dirty('vip'); + file_put_contents("{$g['tmp_path']}/.firewall_virtual_ip.apply", serialize($toapplylist)); + } + header("Location: firewall_virtual_ip.php"); + exit; + } +} + +$pgtitle = array(gettext("Firewall"), gettext("Virtual IP Address"), gettext("Edit")); +include("head.inc"); + +?> + +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<script type="text/javascript" src="/javascript/jquery.ipv4v6ify.js"></script> +<?php include("fbegin.inc"); ?> +<script type="text/javascript"> +//<![CDATA[ +function get_radio_value(obj) { + for (i = 0; i < obj.length; i++) { + if (obj[i].checked) { + return obj[i].value; + } + } + return null; +} +function set_note(noteMessage) { + var note = document.getElementById("typenote"); + if (note.firstChild != null) { + note.removeChild(note.firstChild); + } + if (noteMessage) { + note.appendChild(noteMessage); + } +} +function enable_change() { + var carpnote = document.createTextNode("<?=gettext("This must be the network's subnet mask. It does not specify a CIDR range.");?>"); + var proxyarpnote = document.createTextNode("<?=gettext("This is a CIDR block of proxy ARP addresses.");?>"); + var ipaliasnote = document.createTextNode("<?=gettext("This must be the network's subnet mask. It does not specify a CIDR range.");?>"); + + $mode = get_radio_value(document.iform.mode); + + document.iform.password.disabled = $mode != "carp"; + document.iform.vhid.disabled = $mode != "carp"; + document.iform.advskew.disabled = $mode != "carp"; + document.iform.advbase.disabled = $mode != "carp"; + document.iform.type.disabled = $mode in {"carp":1, "ipalias":1}; + + if ($mode in {"carp":1, "ipalias":1}) { + document.iform.type.selectedIndex = 0;// single-adress + } + switch ($mode) { + case "carp" : set_note(carpnote); break; + case "ipalias" : set_note(ipaliasnote); break; + case "proxyarp": set_note(proxyarpnote); break; + default: set_note(undefined); + } + typesel_change(); +} + +function typesel_change() { + switch (document.iform.type.selectedIndex) { + case 0: // single + document.iform.subnet.disabled = 0; + document.iform.subnet_bits.disabled = (get_radio_value(document.iform.mode) == "proxyarp") || (get_radio_value(document.iform.mode) == "other"); + document.iform.noexpand.disabled = 1; + jQuery('#noexpandrow').css('display', 'none'); + break; + case 1: // network + document.iform.subnet.disabled = 0; + document.iform.subnet_bits.disabled = 0; + document.iform.noexpand.disabled = 0; + jQuery('#noexpandrow').css('display', ''); + //document.iform.range_from.disabled = 1; + //document.iform.range_to.disabled = 1; + break; + case 2: // range + document.iform.subnet.disabled = 1; + document.iform.subnet_bits.disabled = 1; + document.iform.noexpand.disabled = 1; + jQuery('#noexpandrow').css('display', 'none'); + //document.iform.range_from.disabled = 0; + //document.iform.range_to.disabled = 0; + break; + case 3: // IP alias + document.iform.subnet.disabled = 1; + document.iform.subnet_bits.disabled = 0; + document.iform.noexpand.disabled = 1; + jQuery('#noexpandrow').css('display', 'none'); + //document.iform.range_from.disabled = 0; + //document.iform.range_to.disabled = 0; + break; + } +} +//]]> +</script> + +<?php if ($input_errors) print_input_errors($input_errors); ?> +<form action="firewall_virtual_ip_edit.php" method="post" name="iform" id="iform"> + <table width="100%" border="0" cellpadding="6" cellspacing="0" summary="virtual IP edit"> + <tr> + <td colspan="2" valign="top" class="listtopic"><?=gettext("Edit Virtual IP");?></td> + </tr> + <tr> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Type");?></td> + <td width="78%" class="vtable"> + <input name="mode" type="radio" onclick="enable_change()" value="ipalias" + <?php if ($pconfig['mode'] == "ipalias") echo "checked=\"checked\"";?> /> <?=gettext("IP Alias");?> + <input name="mode" type="radio" onclick="enable_change()" value="carp" + <?php if ($pconfig['mode'] == "carp") echo "checked=\"checked\"";?> /> <?=gettext("CARP"); ?> + <input name="mode" type="radio" onclick="enable_change()" value="proxyarp" + <?php if ($pconfig['mode'] == "proxyarp") echo "checked=\"checked\"";?> /> <?=gettext("Proxy ARP"); ?> + <input name="mode" type="radio" onclick="enable_change()" value="other" + <?php if ($pconfig['mode'] == "other") echo "checked=\"checked\"";?> /> <?=gettext("Other");?> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Interface");?></td> + <td width="78%" class="vtable"> + <select name="interface" class="formselect"> + <?php + $interfaces = get_configured_interface_with_descr(false, true); + $carplist = get_configured_carp_interface_list(); + foreach ($carplist as $cif => $carpip) { + $interfaces[$cif] = $carpip . ' (' . get_vip_descr($carpip) . ')'; + } + $interfaces['lo0'] = 'Localhost'; + foreach ($interfaces as $iface => $ifacename): ?> + <option value="<?=$iface;?>" <?php if ($iface == $pconfig['interface']) echo "selected=\"selected\""; ?>> + <?=htmlspecialchars($ifacename);?> + </option> + <?php + endforeach; + ?> + </select> + </td> + </tr> + <tr> + <td valign="top" class="vncellreq"><?=gettext("IP Address(es)");?></td> + <td class="vtable"> + <table border="0" cellspacing="0" cellpadding="0" summary="ip addresses"> + <tr> + <td><?=gettext("Type:");?> </td> + <td> + <select name="type" class="formselect" onchange="typesel_change()"> + <option value="single" <?php if ((!$pconfig['range'] && $pconfig['subnet_bits'] == 32) || (!isset($pconfig['subnet']))) echo "selected=\"selected\""; ?>> + <?=gettext("Single address");?> + </option> + <option value="network" <?php if (!$pconfig['range'] && $pconfig['subnet_bits'] != 32 && isset($pconfig['subnet'])) echo "selected=\"selected\""; ?>> + <?=gettext("Network");?> + </option> + <!-- XXX: Billm, don't let anyone choose this until NAT configuration screens are ready for it <option value="range" <?php if ($pconfig['range']) echo "selected=\"selected\""; ?>> + Range</option> --> + </select> + </td> + </tr> + <tr> + <td><?=gettext("Address:");?> </td> + <td> + <input name="subnet" type="text" class="formfld unknown ipv4v6" id="subnet" size="28" value="<?=htmlspecialchars($pconfig['subnet']);?>" /> + / + <select name="subnet_bits" class="formselect ipv4v6" id="select"> + <?php for ($i = 128; $i >= 1; $i--): ?> + <option value="<?=$i;?>" <?php if ($i == $pconfig['subnet_bits']) echo "selected=\"selected\""; ?>> + <?=$i;?> + </option> + <?php endfor; ?> + </select> + <i id="typenote"></i> + </td> + </tr> + <tr id="noexpandrow"> + <td><?=gettext("Expansion:");?> </td> + <td> + <input name="noexpand" type="checkbox" class="formfld unknown" id="noexpand" <?php echo (isset($pconfig['noexpand'])) ? "checked=\"checked\"" : "" ; ?> /> + Disable expansion of this entry into IPs on NAT lists (e.g. 192.168.1.0/24 expands to 256 entries.) + </td> + </tr> + <?php + /* + <tr> + <td>Range: </td> + <td><input name="range_from" type="text" class="formfld unknown" id="range_from" size="28" value="<?=htmlspecialchars($pconfig['range']['from']);?>" /> +- + <input name="range_to" type="text" class="formfld unknown" id="range_to" size="28" value="<?=htmlspecialchars($pconfig['range']['to']);?>" /> + </td> + </tr> + */ +?> + </table> + </td> + </tr> + <tr valign="top"> + <td width="22%" class="vncellreq"><?=gettext("Virtual IP Password");?></td> + <td class="vtable"><input type='password' name='password' value="<?=htmlspecialchars($pconfig['password']);?>" /> + <br /><?=gettext("Enter the VHID group password.");?> + </td> + </tr> + <tr valign="top"> + <td width="22%" class="vncellreq"><?=gettext("VHID Group");?></td> + <td class="vtable"> + <select id='vhid' name='vhid'> + <?php for ($i = 1; $i <= 255; $i++): ?> + <option value="<?=$i;?>" <?php if ($i == $pconfig['vhid']) echo "selected=\"selected\""; ?>> + <?=$i;?> + </option> + <?php endfor; ?> + </select> + <br /><?=gettext("Enter the VHID group that the machines will share");?> + </td> + </tr> + <tr valign="top"> + <td width="22%" class="vncellreq"><?=gettext("Advertising Frequency");?></td> + <td class="vtable"> + Base: + <select id='advbase' name='advbase'> + <?php for ($i = 1; $i <= 254; $i++): ?> + <option value="<?=$i;?>" <?php if ($i == $pconfig['advbase']) echo "selected=\"selected\""; ?>> + <?=$i;?> + </option> + <?php endfor; ?> + </select> + Skew: + <select id='advskew' name='advskew'> + <?php for ($i = 0; $i <= 254; $i++): ?> + <option value="<?=$i;?>" <?php if ($i == $pconfig['advskew']) echo "selected=\"selected\""; ?>> + <?=$i;?> + </option> + <?php endfor; ?> + </select> + <br /><br /> + <?=gettext("The frequency that this machine will advertise. 0 means usually master. Otherwise the lowest combination of both values in the cluster determines the master.");?> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Description");?></td> + <td width="78%" class="vtable"> + <input name="descr" type="text" class="formfld unknown" id="descr" size="40" value="<?=htmlspecialchars($pconfig['descr']);?>" /> + <br /> + <span class="vexpl"><?=gettext("You may enter a description here for your reference (not parsed).");?></span> + </td> + </tr> + <tr> + <td width="22%" valign="top"> </td> + <td width="78%"> + <input name="Submit" type="submit" class="formbtn" value="<?=gettext("Save"); ?>" /> + <input type="button" class="formbtn" value="<?=gettext("Cancel");?>" onclick="window.location.href='<?=$referer;?>'" /> + <input name="referer" type="hidden" value="<?=$referer;?>" /> + <?php if (isset($id) && $a_vip[$id]): ?> + <input name="id" type="hidden" value="<?=htmlspecialchars($id);?>" /> + <?php endif; ?> + <input name="uniqid" type="hidden" value="<?=$pconfig['uniqid'];?>" /> + </td> + </tr> + <tr> + <td colspan="4"> + <span class="vexpl"> + <span class="red"> + <b><?=gettext("Note:");?><br /></b> + </span> + <?=gettext("Proxy ARP and Other type Virtual IPs cannot be bound to by anything running on the firewall, such as IPsec, OpenVPN, etc. Use a CARP or IP Alias type address for these cases.");?> + <br /><br /> <?=gettext("For more information on CARP and the above values, visit the OpenBSD ");?><a href='http://www.openbsd.org/faq/pf/carp.html'> <?=gettext("CARP FAQ"); ?></a>. + </span> + </td> + </tr> + </table> +</form> +<script type="text/javascript"> +//<![CDATA[ +enable_change(); +//]]> +</script> +<?php include("fend.inc"); ?> +</body> +</html> |