From 983a0663b068f699e41532184ec1d2f164b00d0a Mon Sep 17 00:00:00 2001 From: Ad Schellevis Date: Wed, 9 Apr 2025 13:09:09 +0200 Subject: [PATCH] Interfaces: Devices: Bridge - refactor to MVC closes https://github.com/opnsense/core/issues/8353 (#8534) * Interfaces: Devices: Bridge - refactor to MVC for https://github.com/opnsense/core/issues/8353 * move existing properties to model which overlays existing config path * add a simple wrapper script for [re]configuration which diffs and applies using the new _interfaces_bridge_configure() implementation * Update src/opnsense/mvc/app/models/OPNsense/Interfaces/Bridge.xml Co-authored-by: Franco Fichtner --------- Co-authored-by: Franco Fichtner --- plist | 10 +- .../Api/BridgeSettingsController.php | 138 +++++ .../OPNsense/Interfaces/BridgeController.php | 40 ++ .../Interfaces/forms/dialogBridge.xml | 194 ++++++ .../mvc/app/models/OPNsense/Core/ACL/ACL.xml | 11 +- .../app/models/OPNsense/Interfaces/Bridge.php | 76 +++ .../app/models/OPNsense/Interfaces/Bridge.xml | 74 +++ .../FieldTypes/BridgeMemberField.php | 58 ++ .../models/OPNsense/Interfaces/Menu/Menu.xml | 4 +- .../app/views/OPNsense/Interface/bridge.volt | 45 ++ .../interfaces/reconfigure_bridges.php | 53 ++ .../conf/actions.d/actions_interface.conf | 5 + src/www/interfaces_bridge.php | 164 ----- src/www/interfaces_bridge_edit.php | 567 ------------------ 14 files changed, 695 insertions(+), 744 deletions(-) create mode 100644 src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/BridgeSettingsController.php create mode 100644 src/opnsense/mvc/app/controllers/OPNsense/Interfaces/BridgeController.php create mode 100644 src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogBridge.xml create mode 100644 src/opnsense/mvc/app/models/OPNsense/Interfaces/Bridge.php create mode 100644 src/opnsense/mvc/app/models/OPNsense/Interfaces/Bridge.xml create mode 100644 src/opnsense/mvc/app/models/OPNsense/Interfaces/FieldTypes/BridgeMemberField.php create mode 100644 src/opnsense/mvc/app/views/OPNsense/Interface/bridge.volt create mode 100755 src/opnsense/scripts/interfaces/reconfigure_bridges.php delete mode 100644 src/www/interfaces_bridge.php delete mode 100644 src/www/interfaces_bridge_edit.php diff --git a/plist b/plist index 1cc03e991..11ab1c638 100644 --- a/plist +++ b/plist @@ -360,6 +360,7 @@ /usr/local/opnsense/mvc/app/controllers/OPNsense/IPsec/forms/dialogSPD.xml /usr/local/opnsense/mvc/app/controllers/OPNsense/IPsec/forms/dialogVTI.xml /usr/local/opnsense/mvc/app/controllers/OPNsense/IPsec/forms/settings.xml +/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/BridgeSettingsController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/GifSettingsController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/GreSettingsController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/LaggSettingsController.php @@ -369,6 +370,7 @@ /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/VipSettingsController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/VlanSettingsController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/VxlanSettingsController.php +/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/BridgeController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/GifController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/GreController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/LaggController.php @@ -378,6 +380,7 @@ /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/VipController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/VlanController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/VxlanController.php +/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogBridge.xml /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogGif.xml /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogGre.xml /usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogLagg.xml @@ -752,6 +755,9 @@ /usr/local/opnsense/mvc/app/models/OPNsense/IPsec/Swanctl.php /usr/local/opnsense/mvc/app/models/OPNsense/IPsec/Swanctl.xml /usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/ACL/ACL.xml +/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/Bridge.php +/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/Bridge.xml +/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/FieldTypes/BridgeMemberField.php /usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/FieldTypes/LaggInterfaceField.php /usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/FieldTypes/LinkAddressField.php /usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/FieldTypes/NeighborField.php @@ -915,6 +921,7 @@ /usr/local/opnsense/mvc/app/views/OPNsense/IPsec/spd.volt /usr/local/opnsense/mvc/app/views/OPNsense/IPsec/tunnels.volt /usr/local/opnsense/mvc/app/views/OPNsense/IPsec/vti.volt +/usr/local/opnsense/mvc/app/views/OPNsense/Interface/bridge.volt /usr/local/opnsense/mvc/app/views/OPNsense/Interface/gif.volt /usr/local/opnsense/mvc/app/views/OPNsense/Interface/gre.volt /usr/local/opnsense/mvc/app/views/OPNsense/Interface/lagg.volt @@ -1175,6 +1182,7 @@ /usr/local/opnsense/scripts/interfaces/ppp-linkup.sh /usr/local/opnsense/scripts/interfaces/ppp-rename.sh /usr/local/opnsense/scripts/interfaces/ppp-uptime.sh +/usr/local/opnsense/scripts/interfaces/reconfigure_bridges.php /usr/local/opnsense/scripts/interfaces/reconfigure_gifs.php /usr/local/opnsense/scripts/interfaces/reconfigure_gres.php /usr/local/opnsense/scripts/interfaces/reconfigure_laggs.php @@ -2404,8 +2412,6 @@ /usr/local/www/index.php /usr/local/www/interfaces.php /usr/local/www/interfaces_assign.php -/usr/local/www/interfaces_bridge.php -/usr/local/www/interfaces_bridge_edit.php /usr/local/www/interfaces_ppps.php /usr/local/www/interfaces_ppps_edit.php /usr/local/www/interfaces_wireless.php diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/BridgeSettingsController.php b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/BridgeSettingsController.php new file mode 100644 index 000000000..941cc7b6a --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/BridgeSettingsController.php @@ -0,0 +1,138 @@ +searchBase("bridged", null, "descr"); + } + + /** + * Update bridge with given properties + * @param string $uuid internal id + * @return array save result + validation output + */ + public function setItemAction($uuid) + { + Config::getInstance()->lock(); + $node = $this->getModel()->getNodeByReference('bridged.' . $uuid); + $overlay = null; + if (!empty($node)) { + // not allowed to change bridge interface name + $overlay['bridgeif'] = (string)$node->bridgeif; + } + return $this->setBase("bridge", "bridged", $uuid, $overlay); + } + + /** + * Add new bridge and set with attributes from post + * @return array save result + validation output + */ + public function addItemAction() + { + Config::getInstance()->lock(); + $overlay = []; + $ifnames = []; + foreach ($this->getModel()->bridged->iterateItems() as $node) { + $ifnames[] = (string)$node->bridgeif; + } + for ($i = 0; true; ++$i) { + $gifif = sprintf('bridge%d', $i); + if (!in_array($gifif, $ifnames)) { + $overlay['bridgeif'] = $gifif; + break; + } + } + + return $this->addBase("bridge", "bridged", $overlay); + } + + /** + * Retrieve bridge settings or return defaults for new one + * @param $uuid item unique id + * @return array bridge content + */ + public function getItemAction($uuid = null) + { + return $this->getBase("bridge", "bridged", $uuid); + } + + /** + * Delete bridge by uuid + * @param string $uuid internal id + * @return array save status + */ + public function delItemAction($uuid) + { + Config::getInstance()->lock(); + $node = $this->getModel()->getNodeByReference('bridged.' . $uuid); + if ($node != null) { + $cfg = Config::getInstance()->object(); + foreach ($cfg->interfaces->children() as $key => $value) { + if ((string)$value->if == (string)$node->bridgeif) { + throw new \OPNsense\Base\UserException( + sprintf(gettext("Cannot delete bridge. Currently in use by [%s] %s"), $key, $value), + gettext("bridge in use") + ); + } + } + } + return $this->delBase("bridged", $uuid); + } + + /** + * reconfigure bridges + */ + public function reconfigureAction() + { + if ($this->request->isPost()) { + (new Backend())->configdRun("interface bridge configure"); + return ["status" => "ok"]; + } else { + return ["status" => "failed"]; + } + } +} diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/BridgeController.php b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/BridgeController.php new file mode 100644 index 000000000..9b3e78109 --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/BridgeController.php @@ -0,0 +1,40 @@ +view->pick('OPNsense/Interface/bridge'); + + $this->view->formDialogBridge = $this->getForm("dialogBridge"); + $this->view->formGridBridge = $this->getFormGrid("dialogBridge"); + } +} diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogBridge.xml b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogBridge.xml new file mode 100644 index 000000000..a8ddf119a --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogBridge.xml @@ -0,0 +1,194 @@ +
+ + bridge.bridgeif + + info + + 12em + + + + bridge.members + + select_multiple + Interfaces participating in the bridge. + + + bridge.descr + + text + You may enter a description here for your reference (not parsed). + + + bridge.linklocal + + checkbox + By default, link-local addresses for bridges are disabled. You can enable them manually using this option. However, when a bridge interface has IPv6 addresses, IPv6 addresses on a member interface will be automatically removed before the interface is added. + + boolean + boolean + + + + header + + true + + + bridge.enablestp + + checkbox + Enable spanning tree options for this bridge. + true + + boolean + boolean + false + + + + bridge.proto + + dropdown + Protocol used for spanning tree. + true + + false + + + + bridge.stp + + select_multiple + Enable Spanning Tree Protocol on interface. The if_bridge(4) driver has support for the IEEE 802.1D Spanning Tree Protocol (STP). STP is used to detect and remove loops in a network topology. + true + + false + + + + bridge.maxage + + text + Set the time that a Spanning Tree Protocol configuration is valid. The default is 20 seconds. The minimum is 6 seconds and the maximum is 40 seconds. + true + + false + + + + bridge.fwdelay + + text + Set the time that must pass before an interface begins forwarding packets when Spanning Tree is enabled. The default is 15 seconds. The minimum is 4 seconds and the maximum is 30 seconds. + true + + false + + + + bridge.holdcnt + + text + Set the transmit hold count for Spanning Tree. This is the number of packets transmitted before being rate limited. The default is 6. The minimum is 1 and the maximum is 10. + true + + false + + + + header + + true + + + bridge.maxaddr + + text + Set the size of the bridge address cache to size. The default is 2000 entries. + true + + false + + + + bridge.timeout + + text + Set the timeout of address cache entries to this number of seconds. If seconds is zero, then address cache entries will not be expired. The default is 1200 seconds. + true + + false + + + + bridge.span + + dropdown + Add the interface named by interface as a span port on the bridge. Span ports transmit a copy of every frame received by the bridge. This is most useful for snooping a bridged network passively on another host connected to one of the span ports of the bridge. + true + + false + + + + bridge.edge + + select_multiple + Set interface as an edge port. An edge port connects directly to end stations and cannot create bridging loops in the network; this allows it to transition straight to forwarding. + true + + false + + + + bridge.autoedge + + select_multiple + Allow interface to automatically detect edge status. This is the default for all interfaces added to a bridge. (This will disable the autoedge status of interfaces.) + true + + false + + + + bridge.ptp + + select_multiple + Set the interface as a point-to-point link. This is required for straight transitions to forwarding and should be enabled on a direct link to another RSTP-capable switch. + true + + false + + + + bridge.autoptp + + select_multiple + Automatically detect the point-to-point status on interface by checking the full duplex link status. This is the default for interfaces added to the bridge. (The interfaces selected here will be removed from default autoedge status.) + true + + false + + + + bridge.static + + select_multiple + Mark an interface as a "sticky" interface. Dynamically learned address entries are treated as static once entered into the cache. Sticky entries are never aged out of the cache or replaced, even if the address is seen on a different interface. + true + + false + + + + bridge.private + + select_multiple + Mark an interface as a "private" interface. A private interface does not forward any traffic to any other port that is also a private interface. + true + + false + + +
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 0c0cf7768..9ed304f45 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Core/ACL/ACL.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Core/ACL/ACL.xml @@ -299,17 +299,12 @@ - Interfaces: Bridge edit - - interfaces_bridge_edit.php* - - - Interfaces: Bridge - interfaces_bridge.php* + ui/interfaces/bridge + api/interfaces/bridge_settings/* - + Interfaces: GIF diff --git a/src/opnsense/mvc/app/models/OPNsense/Interfaces/Bridge.php b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Bridge.php new file mode 100644 index 000000000..d3b67c039 --- /dev/null +++ b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Bridge.php @@ -0,0 +1,76 @@ +bridged->iterateItems() as $bridge) { + if (!$validateFullModel && !$bridge->isFieldChanged()) { + continue; + } + $key = $bridge->__reference; + $members = explode(',', $bridge->members->getCurrentValue()); + if (!$bridge->span->isEmpty() && in_array($bridge->span->getCurrentValue(), $members)) { + $messages->appendMessage( + new Message( + gettext("Span interface cannot be part of the bridge."), + $key . ".span" + ) + ); + } + foreach (['stp', 'edge', 'autoedge', 'ptp', 'autoptp', 'static', 'private'] as $section) { + if ($bridge->$section->isEmpty()) { + continue; + } + foreach (explode(',', $bridge->$section->getCurrentValue()) as $if) { + if (!in_array($if, $members)) { + $messages->appendMessage( + new Message( + gettext("Contains non bridge members."), + $key . "." . $section + ) + ); + break; + } + } + } + } + return $messages; + } +} diff --git a/src/opnsense/mvc/app/models/OPNsense/Interfaces/Bridge.xml b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Bridge.xml new file mode 100644 index 000000000..ec3adcf27 --- /dev/null +++ b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Bridge.xml @@ -0,0 +1,74 @@ + + /bridges + 1.0.0 + Bridge interfaces + + + + Y + + + Bridge already exists! + UniqueConstraint + + + /^bridge[\d]+$/ + + + Y + Y + + + + + Y + rstp + + RSTP + STP + + + + Y + + + 6 + 40 + + + 4 + 30 + + + 1 + 10 + + + 1 + + + 0 + + + + Y + + + Y + + + Y + + + Y + + + Y + + + Y + + + + + diff --git a/src/opnsense/mvc/app/models/OPNsense/Interfaces/FieldTypes/BridgeMemberField.php b/src/opnsense/mvc/app/models/OPNsense/Interfaces/FieldTypes/BridgeMemberField.php new file mode 100644 index 000000000..968f9bf7a --- /dev/null +++ b/src/opnsense/mvc/app/models/OPNsense/Interfaces/FieldTypes/BridgeMemberField.php @@ -0,0 +1,58 @@ +object(); + if (!empty($configHandle->interfaces)) { + foreach ($configHandle->interfaces->children() as $ifname => $node) { + if (!empty((string)$node->virtual)) { + continue; + } elseif (!empty((string)$node->if) && str_starts_with('gre', $node->if)) { + continue; + } elseif (!empty((string)$node->if) && str_starts_with('lo', $node->if)) { + continue; + } + $descr = !empty((string)$node->descr) ? (string)$node->descr : strtoupper($ifname); + self::$interfaces[$ifname] = $descr; + } + } + } + $this->internalOptionList = self::$interfaces; + 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 b838bb307..c3d63c74c 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Interfaces/Menu/Menu.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Menu/Menu.xml @@ -2,9 +2,7 @@ - - - + diff --git a/src/opnsense/mvc/app/views/OPNsense/Interface/bridge.volt b/src/opnsense/mvc/app/views/OPNsense/Interface/bridge.volt new file mode 100644 index 000000000..c337fbcd5 --- /dev/null +++ b/src/opnsense/mvc/app/views/OPNsense/Interface/bridge.volt @@ -0,0 +1,45 @@ +{# + # Copyright (c) 2025 Deciso B.V. + # 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. + #} + + +
+ {{ partial('layout_partials/base_bootgrid_table', formGridBridge)}} +
+{{ partial('layout_partials/base_apply_button', {'data_endpoint': '/api/interfaces/bridge_settings/reconfigure'}) }} +{{ partial('layout_partials/base_dialog',['fields':formDialogBridge,'id':formGridBridge['edit_dialog_id'],'label':lang._('Edit Bridge')])}} diff --git a/src/opnsense/scripts/interfaces/reconfigure_bridges.php b/src/opnsense/scripts/interfaces/reconfigure_bridges.php new file mode 100755 index 000000000..a0a9d5b67 --- /dev/null +++ b/src/opnsense/scripts/interfaces/reconfigure_bridges.php @@ -0,0 +1,53 @@ +#!/usr/local/bin/php + - - - - -
-
-
- 0) print_input_errors($input_errors); ?> -
-
-
- - -
- - - - - - - - - - - - false]) as $intf => $intfdata) { - if (substr($intfdata['if'], 0, 3) != 'gre' && substr($intfdata['if'], 0, 2) != 'lo') { - $ifdescrs[$intf] = $intfdata['descr']; - } - } - foreach ($a_bridges as $bridge): ?> - - - - - - - - - -
- - - -
- $member) { - if (!isset($ifdescrs[$member])) { - unset($members[$key]); - } else { - $members[$key] = $ifdescrs[$member]; - } - } - echo implode(', ', $members); ?> - - - - - -
-
-
-
-
-
-
-
- - false]) as $intf => $intfdata) { - if (substr($intfdata['if'], 0, 3) != 'gre' && substr($intfdata['if'], 0, 2) != 'lo') { - $ifacelist[$intf] = $intfdata['descr']; - } -} - -if ($_SERVER['REQUEST_METHOD'] === 'GET') { - // read form data - if (isset($_GET['id']) && !empty($a_bridges[$_GET['id']])) { - $id = $_GET['id']; - } - // copy fields 1-on-1 - $copy_fields = ['descr', 'bridgeif', 'maxaddr', 'timeout', 'maxage','fwdelay', 'proto', 'holdcnt', 'span']; - foreach ($copy_fields as $fieldname) { - if (isset($a_bridges[$id][$fieldname])) { - $pconfig[$fieldname] = $a_bridges[$id][$fieldname]; - } else { - $pconfig[$fieldname] = null; - } - } - - // bool fields - $pconfig['enablestp'] = !empty($a_bridges[$id]['enablestp']); - $pconfig['linklocal'] = !empty($a_bridges[$id]['linklocal']); - - // simple array fields - $array_fields = ['members', 'stp', 'edge', 'autoedge', 'ptp', 'autoptp', 'static', 'private']; - foreach ($array_fields as $fieldname) { - if (!empty($a_bridges[$id][$fieldname])) { - $pconfig[$fieldname] = explode(',', $a_bridges[$id][$fieldname]); - } else { - $pconfig[$fieldname] = []; - } - } -} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { - // save / validate formdata - if (isset($_POST['id']) && !empty($a_bridges[$_POST['id']])) { - $id = $_POST['id']; - } - - $input_errors = []; - $pconfig = $_POST; - - $not_between_int = function($val, $min, $max) { - return !is_numeric($val) || $val < $min || $val > $max; - }; - - if (!empty($pconfig['maxage']) && $not_between_int($pconfig['maxage'], 6, 40)) { - $input_errors[] = gettext("Maxage needs to be an integer between 6 and 40."); - } - if ($pconfig['maxaddr'] != "" && $not_between_int($pconfig['maxaddr'], 0, PHP_INT_MAX)) { - $input_errors[] = gettext("Maxaddr needs to be an integer."); - } - if ($pconfig['timeout'] != "" && $not_between_int($pconfig['timeout'], 0, PHP_INT_MAX)) { - $input_errors[] = gettext("Timeout needs to be an integer."); - } - if (!empty($pconfig['fwdelay']) && $not_between_int($pconfig['fwdelay'], 4, 30)) { - $input_errors[] = gettext("Forward Delay needs to be an integer between 4 and 30."); - } - if (!empty($pconfig['holdcnt']) && $not_between_int($pconfig['holdcnt'], 1, 10)) { - $input_errors[] = gettext("Transmit Hold Count for STP needs to be an integer between 1 and 10."); - } - - $members = !empty($pconfig['members']) ? $pconfig['members'] : []; - if (!empty($members)) { - foreach($members as $ifmembers) { - if (empty($config['interfaces'][$ifmembers])) { - $input_errors[] = gettext("A member interface passed does not exist in configuration"); - } - if (!empty($config['interfaces'][$ifmembers]['wireless']['mode']) && $config['interfaces'][$ifmembers]['wireless']['mode'] != "hostap") { - $input_errors[] = gettext("Bridging a wireless interface is only possible in hostap mode."); - } - if ($pconfig['span'] != "none" && $pconfig['span'] == $ifmembers) { - $input_errors[] = gettext("Span interface cannot be part of the bridge. Remove the span interface from bridge members to continue."); - } - } - } - - foreach (['stp', 'edge', 'autoedge', 'ptp', 'autoptp', 'static', 'private'] as $section) { - if (!empty($pconfig[$section])) { - foreach ($pconfig[$section] as $if) { - if (!in_array($if, $members)) { - $ifname = !empty($ifacelist[$if]) ? $ifacelist[$if] : $if; - $input_errors[] = gettext(sprintf("Option %s contains non bridge member interface %s", $section, $ifname)); - } - } - } - } - - if (count($input_errors) == 0) { - $bridge = []; - - // booleans - foreach (['enablestp', 'linklocal'] as $fieldname) { - if (!empty($pconfig[$fieldname])) { - $bridge[$fieldname] = true; - } - } - - // 1 on 1 copy - $copy_fields = ['descr', 'maxaddr', 'timeout', 'bridgeif', 'maxage','fwdelay', 'proto', 'holdcnt']; - foreach ($copy_fields as $fieldname) { - if (isset($pconfig[$fieldname]) && $pconfig[$fieldname] != '') { - $bridge[$fieldname] = $pconfig[$fieldname]; - } else { - $bridge[$fieldname] = null; - } - } - if ($pconfig['span'] != 'none') { - $bridge['span'] = $pconfig['span']; - } - - // simple array fields - $array_fields = ['members', 'stp', 'edge', 'autoedge', 'ptp', 'autoptp', 'static', 'private']; - foreach ($array_fields as $fieldname) { - if(!empty($pconfig[$fieldname])) { - $bridge[$fieldname] = implode(',', $pconfig[$fieldname]); - } - } - - if (empty($bridge['bridgeif'])) { - $bridge['bridgeif'] = legacy_interface_create('bridge'); /* XXX find another strategy */ - } - - if (empty($bridge['bridgeif']) || strpos($bridge['bridgeif'], 'bridge') !== 0) { - $input_errors[] = gettext("Error occurred creating interface, please retry."); - } else { - if (isset($id)) { - $a_bridges[$id] = $bridge; - } else { - $a_bridges[] = $bridge; - } - write_config(); - interfaces_bridge_configure($bridge['bridgeif']); - ifgroup_setup(); - interfaces_restart_by_device(false, [$bridge['bridgeif']]); - header(url_safe('Location: /interfaces_bridge.php')); - exit; - } - } -} - -legacy_html_escape_form_data($pconfig); -include("head.inc"); -?> - - - - - -
-
-
- 0) print_input_errors($input_errors); ?> -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - -   -
- - -
- - -
- /> - - -
- -
-
-
- - - - - -
-
- - - - - - - -
  - - - - - - -
-
-
-
-
-
-
-
- -