diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/VlanSettingsController.php b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/VlanSettingsController.php new file mode 100644 index 000000000..94ecb967b --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/VlanSettingsController.php @@ -0,0 +1,133 @@ +request->getPost("vlan"); + return "{$tmp['if']}_vlan{$tmp['tag']}"; + } + + private function interfaceAssigned($if) + { + $configHandle = Config::getInstance()->object(); + if (!empty($configHandle->interfaces)) { + foreach ($configHandle->interfaces->children() as $ifname => $node) { + if ((string)$node->if == $if) { + return true; + } + } + } + return false; + } + + public function searchItemAction() + { + return $this->searchBase("vlan", ['vlanif','if','tag','pcp','descr'], "vlanif"); + } + + public function setItemAction($uuid) + { + $node = $this->getModel()->getNodeByReference('vlan.' . $uuid); + $old_vlanif = $node != null ? (string)$node->vlanif : null; + $new_vlanif = $this->generateVlanIfName(); + if ($old_vlanif != null && $old_vlanif != $new_vlanif && $this->interfaceAssigned($old_vlanif)) { + $tmp = $this->request->getPost("vlan"); + if ($tmp['tag'] != (string)$node->tag) { + $result = [ + "result" => "failed", + "validations" => [ + "vlan.tag" => gettext("Interface is assigned and you cannot change the VLAN tag while assigned.") + ] + ]; + } else { + $result = [ + "result" => "failed", + "validations" => [ + "vlan.if" => gettext("Interface is assigned and you cannot change the parent while assigned.") + ] + ]; + } + } else { + $result = $this->setBase("vlan", "vlan", $uuid, ["vlanif" => $new_vlanif]); + // store interface name for apply action + if ($result['result'] != 'failed' && $old_vlanif != $new_vlanif) { + file_put_contents("/tmp/.vlans.removed", "{$old_vlanif}\n", FILE_APPEND|LOCK_EX); + } + } + return $result; + } + + public function addItemAction() + { + return $this->addBase("vlan", "vlan", [ + "vlanif" => $this->generateVlanIfName() + ]); + } + + public function getItemAction($uuid = null) + { + return $this->getBase("vlan", "vlan", $uuid); + } + + public function delItemAction($uuid) + { + $node = $this->getModel()->getNodeByReference('vlan.' . $uuid); + $old_vlanif = $node != null ? (string)$node->vlanif : null; + if ($old_vlanif != null && $this->interfaceAssigned($old_vlanif)) { + throw new UserException(gettext("This VLAN cannot be deleted because it is assigned as an interface.")); + } else { + $result = $this->delBase("vlan", $uuid); + // store interface name for apply action + if ($result['result'] != 'failed') { + file_put_contents("/tmp/.vlans.removed", "{$old_vlanif}\n", FILE_APPEND|LOCK_EX); + } + return $result; + } + } + + public function reconfigureAction() + { + $result = array("status" => "failed"); + if ($this->request->isPost()) { + $result['status'] = strtolower(trim((new Backend())->configdRun('interface vlan configure'))); + } + return $result; + } +} diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/VlanController.php b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/VlanController.php new file mode 100644 index 000000000..963271ff8 --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/VlanController.php @@ -0,0 +1,38 @@ +view->pick('OPNsense/Interface/vlan'); + $this->view->formDialogVlan = $this->getForm("dialogVlan"); + } +} diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVlan.xml b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVlan.xml new file mode 100644 index 000000000..903f914a5 --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVlan.xml @@ -0,0 +1,31 @@ +
+ + vlan.vlanif + + info + + + vlan.if + + dropdown + Only VLAN capable interfaces will be shown. + + + vlan.tag + + text + 802.1Q VLAN tag (between 1 and 4094) + + + vlan.pcp + + dropdown + 802.1Q VLAN PCP (priority code point) + + + vlan.descr + + text + You may enter a description here for your reference (not parsed). + +
diff --git a/src/opnsense/mvc/app/models/OPNsense/Core/ACL/ACL.xml b/src/opnsense/mvc/app/models/OPNsense/Core/ACL/ACL.xml index 81440c49f..ea9cb50cf 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Core/ACL/ACL.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Core/ACL/ACL.xml @@ -410,18 +410,6 @@ interfaces_ppps_edit.php* - - Interfaces: VLAN - - interfaces_vlan.php* - - - - Interfaces: VLAN: Edit - - interfaces_vlan_edit.php* - - Interfaces: WAN diff --git a/src/opnsense/mvc/app/models/OPNsense/Core/Menu/Menu.xml b/src/opnsense/mvc/app/models/OPNsense/Core/Menu/Menu.xml index ef69891db..6404f5050 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Core/Menu/Menu.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Core/Menu/Menu.xml @@ -138,9 +138,6 @@ - - - diff --git a/src/opnsense/mvc/app/models/OPNsense/Interfaces/ACL/ACL.xml b/src/opnsense/mvc/app/models/OPNsense/Interfaces/ACL/ACL.xml index 56f6dd849..9eaeb2378 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Interfaces/ACL/ACL.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Interfaces/ACL/ACL.xml @@ -13,4 +13,11 @@ api/interfaces/loopback + + Interfaces: VLAN + + ui/interfaces/vlan + api/interfaces/vlan + + diff --git a/src/opnsense/mvc/app/models/OPNsense/Interfaces/FieldTypes/VlanInterfaceField.php b/src/opnsense/mvc/app/models/OPNsense/Interfaces/FieldTypes/VlanInterfaceField.php new file mode 100644 index 000000000..498706fc8 --- /dev/null +++ b/src/opnsense/mvc/app/models/OPNsense/Interfaces/FieldTypes/VlanInterfaceField.php @@ -0,0 +1,75 @@ +object(); + $ifnames = []; + if (!empty($configHandle->interfaces)) { + foreach ($configHandle->interfaces->children() as $ifname => $node) { + $ifnames[(string)$node->if] = !empty((string)$node->descr) ? (string)$node->descr : $key; + } + } + self::$interface_devices = []; + $ifconfig = json_decode((new Backend())->configdRun('interface list ifconfig'), true); + if (!empty($ifconfig)) { + foreach ($ifconfig as $ifname => $details) { + // XXX: skip same interface types as legacy, may need to revise later + if (strpos($ifname, "_vlan") > 1 || strpos($ifname, "lo") === 0 || strpos($ifname, "enc") === 0 || + strpos($ifname, "pflog") === 0 || strpos($ifname, "pfsync") === 0 || + strpos($ifname, "bridge") === 0 || + strpos($ifname, "gre") === 0 || strpos($ifname, "gif") === 0 || strpos($ifname, "ipsec") === 0 + ) { + continue; + } + self::$interface_devices[$ifname] = sprintf( + "%s (%s) [%s]", + $ifname, + $details['macaddr'], + !empty($ifnames[$ifname]) ? $ifnames[$ifname] : "" + ); + } + } + } + $this->internalOptionList = self::$interface_devices; + return parent::actionPostLoadingEvent(); + } +} diff --git a/src/opnsense/mvc/app/models/OPNsense/Interfaces/Menu/Menu.xml b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Menu/Menu.xml index b75e87299..e83e32e3f 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Interfaces/Menu/Menu.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Menu/Menu.xml @@ -3,6 +3,7 @@ + diff --git a/src/opnsense/mvc/app/models/OPNsense/Interfaces/Vlan.php b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Vlan.php new file mode 100644 index 000000000..83709376f --- /dev/null +++ b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Vlan.php @@ -0,0 +1,35 @@ + + //vlans + 1.0.0 + + VLAN configuration + + + + + Y + + + 1 + 4094 + Y + + + Y + 0 + + Background (1, lowest) + Best Effort (0, default) + Excellent Effort (2) + Critical Applications (3) + Video (4) + Voice (5) + Internetwork Control (6) + Network Control (7, highest) + + + + N + + + Y + + + The interface name should be unique. + UniqueConstraint + + + + + + diff --git a/src/opnsense/mvc/app/views/OPNsense/Interface/vlan.volt b/src/opnsense/mvc/app/views/OPNsense/Interface/vlan.volt new file mode 100644 index 000000000..0e7eff5cf --- /dev/null +++ b/src/opnsense/mvc/app/views/OPNsense/Interface/vlan.volt @@ -0,0 +1,55 @@ + +
+ + + + + + + + + + + + + + + + + + + + +
{{ lang._('ID') }}{{ lang._('Parent Interface') }}{{ lang._('Interface') }}{{ lang._('Tag') }}{{ lang._('PCP') }}{{ lang._('Description') }}{{ lang._('Commands') }}
+ + +
+
+ +
+ +

+
+
+ + +{{ partial("layout_partials/base_dialog",['fields':formDialogVlan,'id':'DialogVlan','label':lang._('Edit Vlan')])}} diff --git a/src/opnsense/scripts/interfaces/reconfigure_vlans.php b/src/opnsense/scripts/interfaces/reconfigure_vlans.php new file mode 100755 index 000000000..295ad290b --- /dev/null +++ b/src/opnsense/scripts/interfaces/reconfigure_vlans.php @@ -0,0 +1,90 @@ +#!/usr/local/bin/php + 0) { + $handle = fopen($vfilename, "r+"); + if (flock($handle, LOCK_EX)) { + fseek($handle, 0); + foreach (explode("\n", fread($handle, filesize($vfilename))) as $line) { + if (!isset($all_vlans[$line]) && trim($line) != "") { + $all_vlans[$line] = []; + } + } + fseek($handle, 0); + ftruncate($handle, 0); + flock($handle, LOCK_UN); + } +} +// merge configured vlans +if (!empty($config['vlans']['vlan'])) { + foreach ($config['vlans']['vlan'] as $vlan) { + $all_vlans[$vlan['vlanif']] = $vlan; + } +} + +// handle existing vlan's +foreach (legacy_interfaces_details() as $ifname => $ifdetails) { + if (empty($ifdetails['vlan'])) { + continue; + } + if (isset($all_vlans[$ifname])){ + $vlan = $all_vlans[$ifname]; + $cvlan = $ifdetails['vlan']; + if (empty($vlan)) { + // option 1: removed vlan + legacy_interface_destroy($ifname); + } elseif ($vlan['pcp'] != $cvlan['pcp']) { + // option 2: pcp changed, which can be altered instantly + exec('/sbin/ifconfig ' . escapeshellarg($vlan['vlanif']) . ' vlanpcp ' . escapeshellarg($vlan['pcp']). ' 2>&1', $out, $ret); + } elseif ($vlan['tag'] != $cvlan['tag'] || $vlan['if'] != $cvlan['parent']){ + // option 3: changed vlan, recreate with appropriate settings + // XXX: legacy code used interface_configure() in these cases, but since you can't change a tag or a parent + // for an assigned interface. At the moment that doesn't seem to make much sense + interface_vlan_configure($vlan); + } + unset($all_vlans[$ifname]); + } +} + +// configure new +foreach ($all_vlans as $ifname => $vlan) { + if (!empty($vlan)) { + interface_vlan_configure($vlan); + } +} + +ifgroup_setup(); diff --git a/src/opnsense/service/conf/actions.d/actions_interface.conf b/src/opnsense/service/conf/actions.d/actions_interface.conf index c481e5b24..f2940e0c0 100644 --- a/src/opnsense/service/conf/actions.d/actions_interface.conf +++ b/src/opnsense/service/conf/actions.d/actions_interface.conf @@ -115,6 +115,11 @@ command: /usr/local/sbin/pluginctl -c vxlan_prepare message: Reconfiguring vxlan type: script +[vlan.configure] +command: /usr/local/opnsense/scripts/interfaces/reconfigure_vlans.php +message: Reconfiguring vlan +type: script + [loopback.configure] command: /usr/local/sbin/pluginctl -c loopback_prepare message: Reconfiguring loopbacks diff --git a/src/www/interfaces_vlan.php b/src/www/interfaces_vlan.php deleted file mode 100644 index 991b20f99..000000000 --- a/src/www/interfaces_vlan.php +++ /dev/null @@ -1,145 +0,0 @@ - - * 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. - */ - -require_once("guiconfig.inc"); -require_once("interfaces.inc"); - -$a_vlans = &config_read_array('vlans', 'vlan'); - -if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $input_errors = array(); - if (!empty($a_vlans[$_POST['id']])) { - $id = $_POST['id']; - } - - if (!empty($_POST['action']) && $_POST['action'] == "del" && isset($id)) { - if (is_interface_assigned($a_vlans[$id]['vlanif'])) { - $input_errors[] = gettext("This VLAN cannot be deleted because it is assigned as an interface."); - } else { - if (does_interface_exist($a_vlans[$id]['vlanif'])) { - legacy_interface_destroy($a_vlans[$id]['vlanif']); - } - unset($a_vlans[$id]); - write_config(); - header(url_safe('Location: /interfaces_vlan.php')); - exit; - } - } -} - -include("head.inc"); - -legacy_html_escape_form_data($a_vlans); - -?> - - - -
-
-
- 0) print_input_errors($input_errors); ?> -
-
-
- - -
- - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - -
-
-
-
-
-
-
-
- diff --git a/src/www/interfaces_vlan_edit.php b/src/www/interfaces_vlan_edit.php deleted file mode 100644 index d82325381..000000000 --- a/src/www/interfaces_vlan_edit.php +++ /dev/null @@ -1,251 +0,0 @@ - - * 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. - */ - -require_once("guiconfig.inc"); -require_once("system.inc"); -require_once("interfaces.inc"); -require_once("filter.inc"); - -$a_vlans = &config_read_array('vlans', 'vlan'); - -if ($_SERVER['REQUEST_METHOD'] === 'GET') { - // read form data - if (!empty($a_vlans[$_GET['id']])) { - $id = $_GET['id']; - } - $pconfig['if'] = isset($a_vlans[$id]['if']) ? $a_vlans[$id]['if'] : null; - $pconfig['vlanif'] = isset($a_vlans[$id]['vlanif']) ? $a_vlans[$id]['vlanif'] : null; - $pconfig['tag'] = isset($a_vlans[$id]['tag']) ? $a_vlans[$id]['tag'] : null; - $pconfig['pcp'] = isset($a_vlans[$id]['pcp']) ? $a_vlans[$id]['pcp'] : 0; - $pconfig['descr'] = isset($a_vlans[$id]['descr']) ? $a_vlans[$id]['descr'] : null; -} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { - // validate / save form data - if (!empty($a_vlans[$_POST['id']])) { - $id = $_POST['id']; - } - - $input_errors = []; - $pconfig = $_POST; - - /* input validation */ - $reqdfields = explode(" ", "if tag"); - $reqdfieldsn = [gettext('Parent interface'), gettext('VLAN tag')]; - - do_input_validation($pconfig, $reqdfields, $reqdfieldsn, $input_errors); - - if ($pconfig['tag'] && (!is_numericint($pconfig['tag']) || ($pconfig['tag'] < '1') || ($pconfig['tag'] > '4094'))) { - $input_errors[] = gettext("The VLAN tag must be an integer between 1 and 4094."); - } - - if (isset($pconfig['pcp']) && (!is_numericint($pconfig['pcp']) || $pconfig['pcp'] < 0 || $pconfig['pcp'] > 7)) { - $input_errors[] = gettext("The VLAN priority must be an integer between 0 and 7."); - } - - if (!does_interface_exist($pconfig['if'])) { - $input_errors[] = gettext("Interface supplied as parent is invalid"); - } - - if (isset($id) && $pconfig['tag'] && $pconfig['tag'] != $a_vlans[$id]['tag']) { - if (!empty($a_vlans[$id]['vlanif']) && convert_real_interface_to_friendly_interface_name($a_vlans[$id]['vlanif']) != NULL) { - $input_errors[] = gettext("Interface is assigned and you cannot change the VLAN tag while assigned."); - } - } - - foreach ($a_vlans as $vlan) { - if (isset($id) && $a_vlans[$id] === $vlan) { - continue; - } - if (($vlan['if'] == $pconfig['if']) && ($vlan['tag'] == $pconfig['tag'])) { - $input_errors[] = sprintf(gettext("A VLAN with the tag %s is already defined on this interface."), $vlan['tag']); - break; - } - } - - if (count($input_errors) == 0) { - $confif = ""; - $vlan = []; - $vlan['if'] = $pconfig['if']; - $vlan['tag'] = $pconfig['tag']; - $vlan['pcp'] = $pconfig['pcp']; - $vlan['descr'] = $pconfig['descr']; - $vlan['vlanif'] = "{$pconfig['if']}_vlan{$pconfig['tag']}"; - if (isset($id)) { - if (($a_vlans[$id]['if'] != $pconfig['if']) || ($a_vlans[$id]['tag'] != $pconfig['tag']) || ($a_vlans[$id]['pcp'] != $pconfig['pcp'])) { - $confif = convert_real_interface_to_friendly_interface_name($a_vlans[$id]['vlanif']); - legacy_interface_destroy($a_vlans[$id]['vlanif']); - if ($confif != '') { - $config['interfaces'][$confif]['if'] = $vlan['vlanif']; - } - } - } else { - /* - * Since VLAN name is calculated we do not need to fetch one from the - * system. However, we would still like to know if the system can create - * another VLAN if it is being added like is done for other devices. - * Eventually we want to change VLAN device names to a simpler "vlanX" style. - */ - $vlan['vlanif'] = legacy_interface_create('vlan', $vlan['vlanif']); /* XXX find another strategy */ - } - - if (empty($vlan['vlanif']) || strpos($vlan['vlanif'], '_vlan') === false) { - $input_errors[] = gettext("Error occurred creating interface, please retry."); - } else { - if (isset($id)) { - $a_vlans[$id] = $vlan; - } else { - $a_vlans[] = $vlan; - } - write_config(); - interface_vlan_configure($vlan); - ifgroup_setup(); - if ($confif != '') { - interface_configure(false, $confif); - } - header(url_safe('Location: /interfaces_vlan.php')); - exit; - } - } -} - -include("head.inc"); -legacy_html_escape_form_data($pconfig); -?> - - - - -
-
-
- 0) print_input_errors($input_errors); ?> -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -   -
- - -
- - -
- - -
- - -
  - - - - - - -
-
-
-
-
-
-
-
-