diff --git a/src/etc/inc/interfaces.inc b/src/etc/inc/interfaces.inc
index f73c8dd8f..f28dc2d4f 100644
--- a/src/etc/inc/interfaces.inc
+++ b/src/etc/inc/interfaces.inc
@@ -411,6 +411,10 @@ function _interfaces_lagg_configure($lagg)
$interface_stats = legacy_interfaces_details();
$members = explode(',', $lagg['members']);
+ if (!empty($lagg['primary_member'])) {
+ /* place primary member as first member */
+ $members = array_unique(array_merge([$lagg['primary_member']], $members));
+ }
if (empty($interface_stats[$lagg['laggif']])) {
legacy_interface_create($lagg['laggif']);
diff --git a/src/etc/inc/interfaces.lib.inc b/src/etc/inc/interfaces.lib.inc
index 0a165369d..e56bba260 100644
--- a/src/etc/inc/interfaces.lib.inc
+++ b/src/etc/inc/interfaces.lib.inc
@@ -416,13 +416,16 @@ function legacy_interfaces_details($intf = null)
$result[$current_interface]['laggstatistics']['active ports'] = trim(explode(":", $next_line)[1]);
$next_line = $ifconfig_data[$lineid + 2];
$result[$current_interface]['laggstatistics']['flapping'] = trim(explode(":", $next_line)[1]);
- } elseif (preg_match("/laggport: (.*)\Wflags=\d+<(.*)> state=\d+<(.*)>$/", $line, $matches)) {
+ } elseif (
+ preg_match("/laggport: (.*)\Wflags=\d+<(.*)> state=\d+<(.*)>$/", $line, $matches) ||
+ preg_match("/laggport: (.*)\Wflags=\d+<(.*)>.*/", $line, $matches)
+ ) {
if (empty($result[$current_interface]['laggport'])) {
$result[$current_interface]['laggport'] = [];
}
$result[$current_interface]['laggport'][trim($matches[1])] = [
"flags" => explode(",", strtolower($matches[2])),
- "state" => explode(",", strtolower($matches[3])),
+ "state" => isset($matches[3]) ? explode(",", strtolower($matches[3])) : [],
];
} elseif (strpos($line, "\tgroups: ") !== false) {
array_shift($line_parts);
diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogLagg.xml b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogLagg.xml
index 141f28f99..de90f954d 100644
--- a/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogLagg.xml
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogLagg.xml
@@ -17,6 +17,13 @@
dropdown
The protocol to use, please refer to the documentation for a detailed explanation of the various types available
+
+ lagg.primary_member
+
+ dropdown
+
+ This interface will be added first in the lagg making it the primary one.
+
lagg.lacp_fast_timeout
diff --git a/src/opnsense/mvc/app/models/OPNsense/Interfaces/Lagg.php b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Lagg.php
index bd8e397dc..ea242b391 100644
--- a/src/opnsense/mvc/app/models/OPNsense/Interfaces/Lagg.php
+++ b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Lagg.php
@@ -55,28 +55,37 @@ class Lagg extends BaseModel
}
}
}
- 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
- )
- );
- }
- }
+
+ foreach ($this->lagg->iterateItems() as $node) {
+ if (!$validateFullModel && !$node->isFieldChanged()) {
+ continue;
+ }
+ $uuid = $node->getAttributes()['uuid'];
+ $key = $node->__reference;
+ $members = explode(',', (string)$node->members);
+
+ $tmp = [];
+ foreach ($members 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 . '.members'
+ )
+ );
+ }
+ if (!empty((string)$node->primary_member) && !in_array((string)$node->primary_member, $members)) {
+ $messages->appendMessage(
+ new Message(
+ sprintf(gettext('Primary member %s not in member list.'), (string)$node->primary_member),
+ $key . '.primary_member'
+ )
+ );
+ }
}
return $messages;
}
diff --git a/src/opnsense/mvc/app/models/OPNsense/Interfaces/Lagg.xml b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Lagg.xml
index 251f86adb..5dce29295 100644
--- a/src/opnsense/mvc/app/models/OPNsense/Interfaces/Lagg.xml
+++ b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Lagg.xml
@@ -19,6 +19,8 @@
Y
At least one valid member is required
+
+
Y
lacp