Interfaces: LAGG: migrate to MVC closes https://github.com/opnsense/core/issues/6384

Migrate ui to MVC, wrap model around existing configuration area to remain backward compatibility.
To avoid laggs configured via console not being reachable from the gui, add a uuid to it.
This commit is contained in:
Ad Schellevis 2023-07-17 21:34:31 +02:00
parent 1ccb2c9430
commit 57077b83f5
18 changed files with 701 additions and 553 deletions

10
plist
View File

@ -378,14 +378,17 @@
/usr/local/opnsense/mvc/app/controllers/OPNsense/IPsec/forms/dialogRemote.xml
/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/Interfaces/Api/LaggSettingsController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/LoopbackSettingsController.php
/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/LaggController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/LoopbackController.php
/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/dialogLagg.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogLoopback.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVip.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVlan.xml
@ -657,9 +660,12 @@
/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/FieldTypes/LaggInterfaceField.php
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/FieldTypes/VipInterfaceField.php
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/FieldTypes/VipNetworkField.php
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/FieldTypes/VlanInterfaceField.php
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/Lagg.php
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/Lagg.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/Loopback.php
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/Loopback.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/Menu/Menu.xml
@ -764,6 +770,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/lagg.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Interface/loopback.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Interface/vip.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Interface/vlan.volt
@ -959,6 +966,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_laggs.php
/usr/local/opnsense/scripts/interfaces/reconfigure_vips.php
/usr/local/opnsense/scripts/interfaces/reconfigure_vlans.php
/usr/local/opnsense/scripts/interfaces/rtsold_resolvconf.sh
@ -2002,8 +2010,6 @@
/usr/local/www/interfaces_gif_edit.php
/usr/local/www/interfaces_gre.php
/usr/local/www/interfaces_gre_edit.php
/usr/local/www/interfaces_lagg.php
/usr/local/www/interfaces_lagg_edit.php
/usr/local/www/interfaces_ppps.php
/usr/local/www/interfaces_ppps_edit.php
/usr/local/www/interfaces_wireless.php

View File

@ -538,6 +538,7 @@ EOD;
while (1) {
$lagg = [];
$lagg['@attributes'] = ['uuid' => generate_uuid()];
echo "\nLAGG-capable interfaces:\n\n";

View File

@ -0,0 +1,173 @@
<?php
/**
* Copyright (C) 2023 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.
*
*/
namespace OPNsense\Interfaces\Api;
use OPNsense\Base\ApiMutableModelControllerBase;
use OPNsense\Base\UserException;
use OPNsense\Core\Backend;
use OPNsense\Core\Config;
/**
* @package OPNsense\Interfaces
*/
class LaggSettingsController extends ApiMutableModelControllerBase
{
protected static $internalModelName = 'lagg';
protected static $internalModelClass = 'OPNsense\Interfaces\Lagg';
/**
* write updated or removed laggif to temp
*/
private function stashUpdate($laggif)
{
file_put_contents("/tmp/.lagg.todo", "{$laggif}\n", FILE_APPEND | LOCK_EX);
chmod("/tmp/.lagg.todo", 0750);
}
/**
* search laggs
* @return array search results
*/
public function searchItemAction()
{
return $this->searchBase("lagg", ['laggif', 'descr', 'members', 'proto'], "descr");
}
/**
* Update lagg with given properties
* @param string $uuid internal id
* @return array save result + validation output
*/
public function setItemAction($uuid)
{
$node = $this->getModel()->getNodeByReference('lagg.' . $uuid);
$overlay = null;
if (!empty($node)) {
// not allowed to change lagg interface name
$overlay['laggif'] = (string)$node->laggif;
}
$result = $this->setBase("lagg", "lagg", $uuid, $overlay);
if ($result['result'] != 'failed') {
$this->stashUpdate($overlay !== null ? $overlay['laggif'] : $this->request->get('lagg')['laggif']);
}
return $result;
}
/**
* Add new lagg and set with attributes from post
* @return array save result + validation output
*/
public function addItemAction()
{
Config::getInstance()->lock();
$overlay = [];
$ifnames = [];
foreach ($this->getModel()->lagg->iterateItems() as $node) {
$ifnames[] = (string)$node->laggif;
}
for ($i=0; true ; ++$i) {
$laggif = sprintf('lagg%d', $i);
if (!in_array($laggif, $ifnames)) {
$overlay['laggif'] = $laggif;
break;
}
}
$result = $this->addBase("lagg", "lagg", $overlay);
if ($result['result'] != 'failed') {
$this->stashUpdate($overlay['laggif']);
}
return $result;
}
/**
* Retrieve lagg settings or return defaults for new one
* @param $uuid item unique id
* @return array lagg content
*/
public function getItemAction($uuid = null)
{
return $this->getBase("lagg", "lagg", $uuid);
}
/**
* Delete lagg by uuid
* @param string $uuid internal id
* @return array save status
*/
public function delItemAction($uuid)
{
Config::getInstance()->lock();
$node = $this->getModel()->getNodeByReference('lagg.' . $uuid);
$laggif = $node != null ? (string)$node->laggif : null;
$uses = [];
$cfg = Config::getInstance()->object();
foreach ($cfg->interfaces->children() as $key => $value) {
if ((string)$value->if == $laggif) {
$uses['interfaces.'.$key] = !empty($value->descr) ? (string)$value->descr : $key;
}
}
if (isset($cfg->vlans) && isset($cfg->vlans->vlan)) {
foreach ($cfg->vlans->children() as $vlan) {
if ((string)$vlan->if == $laggif) {
$uses[(string)$vlan->vlanif] = !empty($vlan->descr) ? (string)$vlan->descr : $key;
}
}
}
if (!empty($uses)) {
$message = "";
foreach ($uses as $key => $value) {
$message .= htmlspecialchars(sprintf("\n[%s] %s", $key, $value), ENT_NOQUOTES | ENT_HTML401);
}
$message = sprintf(gettext("Cannot delete lagg. Currently in use by %s"), $message);
throw new \OPNsense\Base\UserException($message, gettext("Lagg in use"));
}
$result = $this->delBase("lagg", $uuid);
if ($result['result'] != 'failed' && !empty($laggif)) {
$this->stashUpdate($laggif);
}
return $result;
}
/**
* reconfigure laggs
*/
public function reconfigureAction()
{
if ($this->request->isPost()) {
(new Backend())->configdRun("interface lagg configure");
return array("status" => "ok");
} else {
return array("status" => "failed");
}
}
}

View File

@ -0,0 +1,43 @@
<?php
/*
* Copyright (C) 2023 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.
*/
namespace OPNsense\Interfaces;
use OPNsense\Base\IndexController;
/**
* @package OPNsense\Interfaces
*/
class LaggController extends IndexController
{
public function indexAction($selected = null)
{
$this->view->formDialogEdit = $this->getForm("dialogLagg");
$this->view->pick('OPNsense/Interface/lagg');
}
}

View File

@ -0,0 +1,60 @@
<form>
<field>
<id>lagg.laggif</id>
<label>Device</label>
<type>info</type>
<help></help>
</field>
<field>
<id>lagg.members</id>
<label>Parent</label>
<type>select_multiple</type>
<help>Choose the members that will be used for the link aggregation</help>
</field>
<field>
<id>lagg.proto</id>
<label>Proto</label>
<type>dropdown</type>
<help>The protocol to use, please refer to the documentation for a detailed explanation of the various types available</help>
</field>
<field>
<id>lagg.lacp_fast_timeout</id>
<label>Fast timeout</label>
<type>checkbox</type>
<style>proto proto_lacp</style>
<help>Enable lacp fast-timeout on the interface.</help>
</field>
<field>
<id>lagg.use_flowid</id>
<label>Use flowid</label>
<type>dropdown</type>
<style>selectpicker proto proto_lacp proto_loadbalance</style>
<help>Use the RSS hash from the network card if available, otherwise a hash is locally calculated. The default depends on the system tunable in net.link.lagg.default_use_flowid.</help>
</field>
<field>
<id>lagg.lagghash</id>
<label>Hash Layers</label>
<type>select_multiple</type>
<style>selectpicker proto proto_lacp proto_loadbalance</style>
<help>Set the packet layers to hash for aggregation protocols which load balance.</help>
</field>
<field>
<id>lagg.lacp_strict</id>
<label>Use strict</label>
<type>dropdown</type>
<style>selectpicker proto proto_lacp</style>
<help>Enable lacp strict compliance on the interface. The default depends on the system tunable in net.link.lagg.lacp.default_strict_mode.</help>
</field>
<field>
<id>lagg.mtu</id>
<label>MTU</label>
<type>text</type>
<help>If you leave this field blank, the smallest mtu of this laggs children will be used.</help>
</field>
<field>
<id>lagg.descr</id>
<label>Description</label>
<type>text</type>
<help>You may enter a description here for your reference (not parsed).</help>
</field>
</form>

View File

@ -389,18 +389,6 @@
<pattern>api/firewall/group/*</pattern>
</patterns>
</page-interfaces-groups-edit>
<page-interfaces-lagg-edit>
<name>Interfaces: LAGG: Edit</name>
<patterns>
<pattern>interfaces_lagg_edit.php*</pattern>
</patterns>
</page-interfaces-lagg-edit>
<page-interfaces-lagg>
<name>Interfaces: LAGG</name>
<patterns>
<pattern>interfaces_lagg.php*</pattern>
</patterns>
</page-interfaces-lagg>
<page-interfaces-ppps>
<name>Interfaces: PPPs</name>
<patterns>

View File

@ -127,9 +127,6 @@
<GRE url="/interfaces_gre.php">
<Edit url="/interfaces_gre_edit.php*" visibility="hidden"/>
</GRE>
<LAGG url="/interfaces_lagg.php">
<Edit url="/interfaces_lagg_edit.php*" visibility="hidden"/>
</LAGG>
</Types>
<Diagnostics order="970" cssClass="fa fa-medkit fa-fw">
<DNSLookup VisibleName="DNS Lookup" url="/ui/diagnostics/dns_diagnostics"/>

View File

@ -36,4 +36,11 @@
<pattern>api/interfaces/vlan_settings/*</pattern>
</patterns>
</page-interfaces-vlan-edit>
<page-interfaces-lagg-edit>
<name>Interfaces: LAGG: Edit</name>
<patterns>
<pattern>ui/interfaces/lagg</pattern>
<pattern>api/interfaces/lagg_settings/*</pattern>
</patterns>
</page-interfaces-lagg-edit>
</acl>

View File

@ -0,0 +1,61 @@
<?php
/*
* Copyright (C) 2023 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.
*/
namespace OPNsense\Interfaces\FieldTypes;
use OPNsense\Base\FieldTypes\BaseListField;
use OPNsense\Core\Config;
use OPNsense\Core\Backend;
class LaggInterfaceField extends BaseListField
{
private static $parent_interfaces = null;
protected function actionPostLoadingEvent()
{
if (self::$parent_interfaces === null) {
self::$parent_interfaces = [];
$skip = [];
foreach (Config::getInstance()->object()->interfaces->children() as $ifname => $node) {
if (!empty((string)$node->if)) {
$skip[] = (string)$node->if;
}
}
$itfs = json_decode((new Backend())->configdRun("interface list ifconfig") ?? '', true) ?? [];
foreach ($itfs as $ifname => $ifinfo) {
if (in_array($ifname, $skip) || !$ifinfo['is_physical']) {
continue;
}
self::$parent_interfaces[$ifname] = sprintf("%s (%s)", $ifname, $ifinfo['macaddr'] ?? '');
}
}
$this->internalOptionList = self::$parent_interfaces;
return parent::actionPostLoadingEvent();
}
}

View File

@ -0,0 +1,86 @@
<?php
/**
* Copyright (C) 2023 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.
*
*/
namespace OPNsense\Interfaces;
use OPNsense\Base\BaseModel;
use Phalcon\Messages\Message;
/**
* Class Lagg
* @package OPNsense\Interfaces
*/
class Lagg extends BaseModel
{
/**
* {@inheritdoc}
*/
public function performValidation($validateFullModel = false)
{
$messages = parent::performValidation($validateFullModel);
// validate members are uniquely assigned to a single lagg
$members = [];
foreach ($this->getFlatNodes() as $key => $node) {
if ($node->getInternalXMLTagName() == 'members') {
foreach (explode(',', (string)$node) as $intf) {
if (!isset($members[$intf])) {
$members[$intf] = [];
}
$members[$intf][] = $node->getParentNode()->getAttributes()['uuid'];
}
}
}
foreach ($this->getFlatNodes() as $key => $node) {
if ($validateFullModel || $node->isFieldChanged()) {
if ($node->getParentNode()->getInternalXMLTagName() === 'lagg') {
$uuid = $node->getParentNode()->getAttributes()['uuid'];
if ($node->getInternalXMLTagName() == 'members') {
$tmp = [];
foreach (explode(',', (string)$node) as $intf) {
if (!empty($members[$intf]) && count($members[$intf]) > 1) {
$tmp[] = $intf;
}
}
if (!empty($tmp)) {
$messages->appendMessage(
new Message(
sprintf(gettext('Members %s are already used in other laggs.'), implode(',', $tmp)),
$key
)
);
}
}
}
}
}
return $messages;
}
}

View File

@ -0,0 +1,71 @@
<model>
<mount>/laggs</mount>
<version>1.0.0</version>
<description>Lagg interfaces</description>
<items>
<lagg type="ArrayField">
<laggif type="TextField">
<Required>Y</Required>
<Constraints>
<check001>
<ValidationMessage>Lagg already exists!</ValidationMessage>
<type>UniqueConstraint</type>
</check001>
</Constraints>
<Mask>/^lagg(\d)*$/</Mask>
</laggif>
<members type=".\LaggInterfaceField">
<Required>Y</Required>
<Multiple>Y</Multiple>
<ValidationMessage>At least one valid member is required</ValidationMessage>
</members>
<proto type="OptionField">
<Required>Y</Required>
<default>lacp</default>
<OptionValues>
<none>None</none>
<lacp>lacp</lacp>
<failover>failover</failover>
<fec>fec</fec>
<loadbalance>loadbalance</loadbalance>
<roundrobin>roundrobin</roundrobin>
</OptionValues>
</proto>
<lacp_fast_timeout type="BooleanField">
<default>0</default>
<Required>Y</Required>
</lacp_fast_timeout>
<use_flowid type="OptionField">
<OptionValues>
<default value="">Default</default>
<yes value="1">Yes</yes>
<no value="0">No</no>
</OptionValues>
</use_flowid>
<lagghash type="OptionField">
<Multiple>Y</Multiple>
<OptionValues>
<l2>L2: src/dst MAC address and optional VLAN number.</l2>
<l3>L3: src/dst address for IPv4 or IPv6.</l3>
<l4>L4: src/dst port for TCP/UDP/SCTP.</l4>
</OptionValues>
</lagghash>
<lacp_strict type="OptionField">
<OptionValues>
<default value="">Default</default>
<yes value="1">Yes</yes>
<no value="0">No</no>
</OptionValues>
</lacp_strict>
<mtu type="IntegerField">
<Required>N</Required>
<MinimumValue>576</MinimumValue>
<MaximumValue>65535</MaximumValue>
</mtu>
<descr type="TextField">
<mask>/^([\t\n\v\f\r 0-9a-zA-Z.\-,_\x{00A0}-\x{FFFF}]){0,255}$/u</mask>
<ValidationMessage>Description should be a string between 1 and 255 characters</ValidationMessage>
</descr>
</lagg>
</items>
</model>

View File

@ -8,6 +8,7 @@
<VXLAN url="/ui/interfaces/vxlan"/>
<Loopback url="/ui/interfaces/loopback"/>
<VLAN url="/ui/interfaces/vlan"/>
<LAGG url="/ui/interfaces/lagg"/>
</Types>
</Interfaces>
</menu>

View File

@ -0,0 +1,69 @@
<script>
$( document ).ready(function() {
$("#grid-laggs").UIBootgrid(
{ search:'/api/interfaces/lagg_settings/searchItem',
get:'/api/interfaces/lagg_settings/getItem/',
set:'/api/interfaces/lagg_settings/setItem/',
add:'/api/interfaces/lagg_settings/addItem/',
del:'/api/interfaces/lagg_settings/delItem/',
options: {
formatters: {
members: function (column, row) {
return row[column.id].replace(',', '<br/>');
}
}
}
}
);
$("#lagg\\.proto").change(function(){
$(".proto").closest("tr").hide();
$(".proto_"+$(this).val()).each(function(){
$(this).closest("tr").show();
});
});
$("#reconfigureAct").SimpleActionButton();
});
</script>
<div class="tab-content content-box">
<table id="grid-laggs" class="table table-condensed table-hover table-striped" data-editDialog="DialogEdit" data-editAlert="LaggChangeMessage">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="laggif" data-width="5em" data-type="string">{{ lang._('Device') }}</th>
<th data-column-id="members" data-type="string" data-width="12em" data-formatter="members">{{ lang._('Members') }}</th>
<th data-column-id="proto" data-width="10em" data-type="string">{{ lang._('Protocol') }}</th>
<th data-column-id="descr" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-fw fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-fw fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
<div class="col-md-12">
<div id="LaggChangeMessage" class="alert alert-info" style="display: none" role="alert">
{{ lang._('After changing settings, please remember to apply them with the button below') }}
</div>
<hr/>
<button class="btn btn-primary" id="reconfigureAct"
data-endpoint='/api/interfaces/lagg_settings/reconfigure'
data-label="{{ lang._('Apply') }}"
data-error-title="{{ lang._('Error reconfiguring laggs') }}"
type="button"
></button>
<br/><br/>
</div>
</div>
{{ partial("layout_partials/base_dialog",['fields':formDialogEdit,'id':'DialogEdit','label':lang._('Edit Lagg')])}}

View File

@ -28,5 +28,43 @@
*/
require_once("interfaces.inc");
require_once("interfaces.lib.inc");
echo json_encode(legacy_interfaces_details());
$vfaces = [
'_stf',
'_vlan',
'_wlan',
'bridge',
'carp',
'enc',
'gif',
'gre',
'ipfw', /* ipfw logging device, not enabled by default */
'ipsec',
'l2tp',
'lagg',
'lo',
'ng',
'ovpnc',
'ovpns',
'pflog',
'pfsync',
'plip',
'ppp',
'pppoe',
'pptp',
'qinq',
'tap',
'tun',
'vlan',
'vxlan',
];
$response = legacy_interfaces_details();
foreach ($response as $ifname => &$intf) {
$tmp_ifnames = preg_split('/\d+/', $ifname);
$intf['is_physical'] = !count(array_intersect($tmp_ifnames, $vfaces));
}
echo json_encode($response);

View File

@ -0,0 +1,77 @@
#!/usr/local/bin/php
<?php
/*
* Copyright (C) 2023 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.
*/
require_once 'config.inc';
require_once 'filter.inc';
require_once 'interfaces.inc';
require_once 'util.inc';
/* gather all relevant lagg interfaces*/
$laggs_todo = [];
$vfilename = '/tmp/.lagg.todo';
if (file_exists($vfilename) && filesize($vfilename) > 0) {
$handle = fopen($vfilename, 'r+');
if (flock($handle, LOCK_EX)) {
fseek($handle, 0);
foreach (explode("\n", fread($handle, filesize($vfilename))) as $line) {
if (!isset($laggs_todo[$line]) && trim($line) != '') {
$laggs_todo[$line] = [];
}
}
fseek($handle, 0);
ftruncate($handle, 0);
flock($handle, LOCK_UN);
}
}
/* collect changed laggs to reconfigure */
$lagg_configure = [];
foreach ((new OPNsense\Interfaces\Lagg())->lagg->iterateItems() as $item) {
$lagg = [];
foreach ($item->iterateItems() as $node) {
$lagg[$node->getInternalXMLTagName()] = (string)$node;
}
if (isset($laggs_todo[$lagg['laggif']])) {
$lagg_configure[] = $lagg;
unset($laggs_todo[$lagg['laggif']]);
}
}
/* remove non existing interfaces */
foreach (array_keys($laggs_todo) as $laggif) {
legacy_interface_destroy($laggif);
}
/*
reconfigure still existing laggs,
removal should happen first as it may free parent interfaces.
*/
foreach ($lagg_configure as $lagg) {
_interfaces_lagg_configure($lagg);
}

View File

@ -145,6 +145,11 @@ command: /usr/local/opnsense/scripts/interfaces/reconfigure_vlans.php
message: Reconfiguring vlan
type: script
[lagg.configure]
command: /usr/local/opnsense/scripts/interfaces/reconfigure_laggs.php
message: Reconfiguring laggs
type: script
[loopback.configure]
command: /usr/local/sbin/pluginctl -c loopback
message: Reconfiguring loopbacks

View File

@ -1,142 +0,0 @@
<?php
/*
* Copyright (C) 2014-2015 Deciso B.V.
* Copyright (C) 2008 Ermal Luçi
* 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");
$a_laggs = &config_read_array('laggs', 'lagg');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$input_errors = array();
if (!empty($a_laggs[$_POST['id']])) {
$id = $_POST['id'];
}
if (!empty($_POST['action']) && $_POST['action'] == "del" && isset($id)) {
if (is_interface_assigned($a_laggs[$id]['laggif'])) {
$input_errors[] = gettext("This LAGG interface cannot be deleted because it is still being used.");
} else {
mwexecf('/sbin/ifconfig %s destroy', $a_laggs[$id]['laggif']);
unset($a_laggs[$id]);
write_config();
header(url_safe('Location: /interfaces_lagg.php'));
exit;
}
}
}
include("head.inc");
legacy_html_escape_form_data($a_laggs);
?>
<body>
<script>
$( document ).ready(function() {
// link delete buttons
$(".act_delete").click(function(event){
event.preventDefault();
var id = $(this).data("id");
// delete single
BootstrapDialog.show({
type:BootstrapDialog.TYPE_DANGER,
title: "<?= gettext("LAGG");?>",
message: "<?=gettext("Do you really want to delete this LAGG interface?");?>",
buttons: [{
label: "<?= gettext("No");?>",
action: function(dialogRef) {
dialogRef.close();
}}, {
label: "<?= gettext("Yes");?>",
action: function(dialogRef) {
$("#id").val(id);
$("#action").val("del");
$("#iform").submit()
}
}]
});
});
});
</script>
<?php include("fbegin.inc"); ?>
<section class="page-content-main">
<div class="container-fluid">
<div class="row">
<?php if (isset($input_errors) && count($input_errors) > 0) print_input_errors($input_errors); ?>
<section class="col-xs-12">
<div class="tab-content content-box col-xs-12">
<form method="post" name="iform" id="iform">
<input type="hidden" id="action" name="action" value="">
<input type="hidden" id="id" name="id" value="">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th><?=gettext("Device");?></th>
<th><?=gettext("Members");?></th>
<th><?=gettext("Protocol");?></th>
<th><?=gettext("Description");?></th>
<th class="text-nowrap">
<a href="interfaces_lagg_edit.php" class="btn btn-primary btn-xs" data-toggle="tooltip" title="<?= html_safe(gettext('Add')) ?>">
<i class="fa fa-plus fa-fw"></i>
</a>
</th>
</tr>
</thead>
<tbody>
<?php
$i = 0;
foreach ($a_laggs as $lagg): ?>
<tr>
<td><?=$lagg['laggif'];?></td>
<td><?=$lagg['members'];?></td>
<td><?=strtoupper($lagg['proto']);?></td>
<td><?=$lagg['descr'];?></td>
<td class="text-nowrap">
<a href="interfaces_lagg_edit.php?id=<?=$i;?>" class="btn btn-xs btn-default" data-toggle="tooltip" title="<?= html_safe(gettext('Edit')) ?>">
<i class="fa fa-pencil fa-fw"></i>
</a>
<button title="<?= html_safe(gettext('Delete')) ?>" data-toggle="tooltip" data-id="<?=$i;?>" class="btn btn-default btn-xs act_delete" type="submit">
<i class="fa fa-trash fa-fw"></i>
</button>
</td>
</tr>
<?php
$i++;
endforeach; ?>
</tbody>
</table>
</div>
</form>
</div>
</section>
</div>
</div>
</section>
<?php include("foot.inc"); ?>

View File

@ -1,393 +0,0 @@
<?php
/*
* Copyright (C) 2014-2016 Deciso B.V.
* Copyright (C) 2008 Ermal Luçi
* 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");
/**
* list available interfaces for lagg
* @param null $selected_id selected item index
* @return array
*/
function available_interfaces($selected_id = null)
{
global $config;
// configured interfaces
$configured_interfaces = array();
foreach (array_keys(legacy_config_get_interfaces(['virtual' => false])) as $intf) {
$configured_interfaces[] = get_real_interface($intf);
}
// lagg members from other lagg interfaces
$lagg_member_interfaces = array();
foreach ($config['laggs']['lagg'] as $lagg_idx => $lagg) {
if ($lagg_idx == $selected_id) {
continue;
}
foreach (explode(",", $lagg['members']) as $lagg_member) {
$lagg_member_interfaces[] = get_real_interface($lagg_member);
}
}
$interfaces = array();
foreach (get_interface_list() as $intf => $intf_info) {
if (in_array($intf, $lagg_member_interfaces)) {
// skip members of other lagg interfaces
continue;
} elseif (in_array($intf, $configured_interfaces)) {
// skip configured interfaces
continue;
}
$interfaces[$intf] = $intf_info;
}
return $interfaces;
}
$laggprotos = array("none", "lacp", "failover", "fec", "loadbalance", "roundrobin");
$a_laggs = &config_read_array('laggs', 'lagg');
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
// read form data
if (!empty($a_laggs[$_GET['id']])) {
$id = $_GET['id'];
}
$pconfig = array();
$pconfig['laggif'] = isset($a_laggs[$id]['laggif']) ? $a_laggs[$id]['laggif'] : null;
$pconfig['members'] = isset($a_laggs[$id]['members']) ? explode(",", $a_laggs[$id]['members']) : array();
$pconfig['proto'] = isset($a_laggs[$id]['proto']) ? $a_laggs[$id]['proto'] : null;
$pconfig['descr'] = isset($a_laggs[$id]['descr']) ? $a_laggs[$id]['descr'] : null;
$pconfig['lacp_fast_timeout'] = !empty($a_laggs[$id]['lacp_fast_timeout']);
$pconfig['use_flowid'] = isset($a_laggs[$id]['use_flowid']) ? $a_laggs[$id]['use_flowid'] : null;
$pconfig['lagghash'] = isset($a_laggs[$id]['lagghash']) ? explode(',', $a_laggs[$id]['lagghash']) : [];
$pconfig['lacp_strict'] = isset($a_laggs[$id]['lacp_strict']) ? $a_laggs[$id]['lacp_strict'] : null;
$pconfig['mtu'] = isset($a_laggs[$id]['mtu']) ? $a_laggs[$id]['mtu'] : null;
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
// validate and save form data
if (!empty($a_laggs[$_POST['id']])) {
$id = $_POST['id'];
}
$input_errors = array();
$pconfig = $_POST;
/* input validation */
$reqdfields = explode(" ", "members proto");
$reqdfieldsn = array(gettext("Member interfaces"), gettext("Lagg protocol"));
do_input_validation($pconfig, $reqdfields, $reqdfieldsn, $input_errors);
if (isset($pconfig['members'])) {
foreach ($pconfig['members'] as $member) {
if (!does_interface_exist($member)) {
$input_errors[] = sprintf(gettext('Interface \'%s\' supplied as member does not exist'), $member);
}
}
}
if (!empty($pconfig['lagghash'])){
foreach ((array)$pconfig['lagghash'] as $item) {
if (!in_array($item, ['l2', 'l3', 'l4'])) {
$input_errors[] = sprintf(gettext('Invalid lagghash value \'%s\''), $item);
}
}
}
if (!in_array($pconfig['proto'], $laggprotos)) {
$input_errors[] = gettext("Protocol supplied is invalid");
}
if (!empty($pconfig['mtu'])) {
$mtu_low = 576;
$mtu_high = 65535;
if ($pconfig['mtu'] < $mtu_low || $pconfig['mtu'] > $mtu_high || (string)((int)$pconfig['mtu']) != $pconfig['mtu'] ) {
$input_errors[] = sprintf(gettext('The MTU must be greater than %s bytes and less than %s.'), $mtu_low, $mtu_high);
}
}
if (count($input_errors) == 0) {
$lagg = array();
$lagg['members'] = implode(',', $pconfig['members']);
$lagg['descr'] = $pconfig['descr'];
$lagg['laggif'] = $pconfig['laggif'];
$lagg['proto'] = $pconfig['proto'];
$lagg['mtu'] = $pconfig['mtu'];
$lagg['lacp_fast_timeout'] = !empty($pconfig['lacp_fast_timeout']);
if (in_array($pconfig['use_flowid'] ?? '', ['0', '1'])) {
$lagg['use_flowid'] = $pconfig['use_flowid'];
}
if (in_array($pconfig['lacp_strict'] ?? '', ['0', '1'])) {
$lagg['lacp_strict'] = $pconfig['lacp_strict'];
}
if (!empty($pconfig['lagghash'])) {
$lagg['lagghash'] = implode(',', $pconfig['lagghash']);
}
if (isset($id)) {
$lagg['laggif'] = $a_laggs[$id]['laggif'];
}
if (empty($lagg['laggif'])) {
$lagg['laggif'] = legacy_interface_create('lagg'); /* XXX find another strategy */
}
if (empty($lagg['laggif']) || strpos($lagg['laggif'], 'lagg') !== 0) {
$input_errors[] = gettext("Error occurred creating interface, please retry.");
} else {
if (isset($id)) {
$a_laggs[$id] = $lagg;
} else {
$a_laggs[] = $lagg;
}
write_config();
_interfaces_lagg_configure($lagg);
$confif = convert_real_interface_to_friendly_interface_name($lagg['laggif']);
if ($confif != '') {
interface_configure(false, $confif);
}
header(url_safe('Location: /interfaces_lagg.php'));
exit;
}
}
}
include("head.inc");
legacy_html_escape_form_data($pconfig);
?>
<body>
<script>
$( document ).ready(function() {
$("#proto").change(function(){
let this_proto = $("#proto").val();
$(".proto").closest('tr').hide();
$(".proto").prop( "disabled", true );
$(".proto_"+this_proto).closest('tr').show();
$(".proto_"+this_proto).prop( "disabled", false );
$('.selectpicker').selectpicker('refresh');
});
$("#proto").change();
});
</script>
<?php include("fbegin.inc"); ?>
<section class="page-content-main">
<div class="container-fluid">
<div class="row">
<?php if (isset($input_errors) && count($input_errors) > 0) print_input_errors($input_errors); ?>
<section class="col-xs-12">
<div class="content-box">
<div class="table-responsive">
<form method="post" name="iform" id="iform">
<table class="table table-striped opnsense_standard_table_form">
<thead>
<tr>
<td style="width:22%"><strong><?=gettext("LAGG configuration");?></strong></td>
<td style="width:78%; text-align:right">
<small><?=gettext("full help"); ?> </small>
<i class="fa fa-toggle-off text-danger" style="cursor: pointer;" id="show_all_help_page"></i>
&nbsp;
</td>
</tr>
</thead>
<tbody>
<tr>
<td><a id="help_for_members" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Parent interface"); ?></td>
<td>
<select name="members[]" multiple="multiple" class="selectpicker">
<?php
foreach (available_interfaces(isset($id) ? $id : null) as $ifn => $ifinfo):?>
<option value="<?=$ifn;?>" <?=!empty($pconfig['members']) && in_array($ifn, $pconfig['members']) ? "selected=\"selected\"" : "";?>>
<?=$ifn;?> (<?=$ifinfo['mac']?>)
</option>
<?php
endforeach;?>
</select>
<div class="hidden" data-for="help_for_members">
<?=gettext("Choose the members that will be used for the link aggregation"); ?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_proto" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Lag proto"); ?></td>
<td>
<select name="proto" class="selectpicker" id="proto">
<?php
foreach ($laggprotos as $proto):?>
<option value="<?=$proto;?>" <?=$proto == $pconfig['proto'] ? "selected=\"selected\"": "";?>>
<?=strtoupper($proto);?>
</option>
<?php
endforeach;?>
</select>
<div class="hidden" data-for="help_for_proto">
<ul>
<li><b><?=gettext("failover"); ?></b></li>
<?=gettext("Sends and receives traffic only through the master port. " .
"If the master port becomes unavailable, the next active port is " .
"used. The first interface added is the master port; any " .
"interfaces added after that are used as failover devices."); ?>
<li><b><?=gettext("fec"); ?></b><br /></li>
<?=gettext("Supports Cisco EtherChannel. This is a static setup and " .
"does not negotiate aggregation with the peer or exchange " .
"frames to monitor the link."); ?>
<li>
<b><?=gettext("lacp"); ?></b></li>
<?=gettext("Supports the IEEE 802.3ad Link Aggregation Control Protocol " .
"(LACP) and the Marker Protocol. LACP will negotiate a set " .
"of aggregable links with the peer in to one or more Link " .
"Aggregated Groups. Each LAG is composed of ports of the " .
"same speed, set to full-duplex operation. The traffic will " .
"be balanced across the ports in the LAG with the greatest " .
"total speed, in most cases there will only be one LAG which " .
"contains all ports. In the event of changes in physical " .
"connectivity, Link Aggregation will quickly converge to a " .
"new configuration."); ?>
<li><b><?=gettext("loadbalance"); ?></b></li>
<?=gettext("Balances outgoing traffic across the active ports based on " .
"hashed protocol header information and accepts incoming " .
"traffic from any active port. This is a static setup and " .
"does not negotiate aggregation with the peer or exchange " .
"frames to monitor the link. The hash includes the Ethernet " .
"source and destination address, and, if available, the VLAN " .
"tag, and the IP source and destination address.") ?>
<li><b><?=gettext("roundrobin"); ?></b></li>
<?=gettext("Distributes outgoing traffic using a round-robin scheduler " .
"through all active ports and accepts incoming traffic from " .
"any active port."); ?>
<li><b><?=gettext("none"); ?></b></li>
<?=gettext("This protocol is intended to do nothing: It disables any " .
"traffic without disabling the lagg interface itself."); ?>
</ul>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_descr" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Description"); ?></td>
<td>
<input name="descr" type="text" value="<?=$pconfig['descr'];?>" />
<div class="hidden" data-for="help_for_descr">
<?=gettext("You may enter a description here for your reference (not parsed)."); ?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_lacp_fast_timeout" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Fast timeout"); ?></td>
<td>
<input name="lacp_fast_timeout" id="lacp_fast_timeout" class="proto proto_lacp" type="checkbox" value="yes" <?=!empty($pconfig['lacp_fast_timeout']) ? "checked=\"checked\"" : "" ;?>/>
<div class="hidden" data-for="help_for_lacp_fast_timeout">
<?=gettext("Enable lacp fast-timeout on the interface."); ?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_use_flowid" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Use flowid"); ?></td>
<td>
<select name="use_flowid" class="selectpicker proto proto_lacp proto_loadbalance" id="use_flowid">
<option value=""><?=gettext("Default");?></option>
<option value="1" <?=$pconfig['use_flowid'] === "1" ? "selected=\"selected\"": ""?>>
<?=gettext("Yes");?>
</option>
<option value="0" <?=$pconfig['use_flowid'] === "0" ? "selected=\"selected\"": ""?>>
<?=gettext("No");?>
</option>
</select>
<div class="hidden" data-for="help_for_use_flowid">
<?=gettext(
"Use the RSS hash from the network card if available, otherwise a hash is locally calculated. ".
"The default depends on the system tunable in net.link.lagg.default_use_flowid."
)?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_lagghash" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Hash Layers"); ?>
<td>
<select name="lagghash[]" title="<?=gettext("Default");?>" multiple="multiple" class="selectpicker proto proto_lacp proto_loadbalance">
<option value="l2" <?=in_array('l2', $pconfig['lagghash']) ? "selected=\"selected\"": ""?> ><?=gettext("L2: src/dst MAC address and optional VLAN number."); ?></option>
<option value="l3" <?=in_array('l3', $pconfig['lagghash']) ? "selected=\"selected\"": ""?>><?=gettext("L3: src/dst address for IPv4 or IPv6."); ?></option>
<option value="l4" <?=in_array('l4', $pconfig['lagghash']) ? "selected=\"selected\"": ""?>><?=gettext("L4: src/dst port for TCP/UDP/SCTP."); ?></option>
</select>
<div class="hidden" data-for="help_for_lagghash">
<?=gettext("Set the packet layers to hash for aggregation protocols which load balance."); ?><br/>
</div>
</td>
</td>
</tr>
<tr>
<td><a id="help_for_lacp_strict" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Use strict"); ?></td>
<td>
<select name="lacp_strict" class="selectpicker proto proto_lacp" id="lacp_strict">
<option value=""><?=gettext("Default");?></option>
<option value="1" <?=$pconfig['lacp_strict'] === "1" ? "selected=\"selected\"": ""?>>
<?=gettext("Yes");?>
</option>
<option value="0" <?=$pconfig['lacp_strict'] === "0" ? "selected=\"selected\"": ""?>>
<?=gettext("No");?>
</option>
</select>
<div class="hidden" data-for="help_for_lacp_strict">
<?=gettext(
"Enable lacp strict compliance on the interface. ".
"The default depends on the system tunable in net.link.lagg.lacp.default_strict_mode."
)?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_mtu" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("MTU"); ?></td>
<td>
<input name="mtu" id="mtu" type="text" value="<?=$pconfig['mtu'];?>" />
<div class="hidden" data-for="help_for_mtu">
<?= gettext("If you leave this field blank, the smallest mtu of this laggs children will be used.");?>
</div>
</td>
</tr>
<tr>
<td style="width:22%">&nbsp;</td>
<td style="width:78%">
<input type="hidden" name="laggif" value="<?=$pconfig['laggif']; ?>" />
<input name="Submit" type="submit" class="btn btn-primary" value="<?=html_safe(gettext('Save')); ?>" />
<input type="button" class="btn btn-default" value="<?=html_safe(gettext('Cancel'));?>" onclick="window.location.href='/interfaces_lagg.php'" />
<?php if (isset($id)): ?>
<input name="id" type="hidden" value="<?=$id;?>" />
<?php endif; ?>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</div>
</section>
</div>
</div>
</section>
<?php include("foot.inc"); ?>