From 237d2df7e0f3081cb228138a7b3cf6edd9052ac9 Mon Sep 17 00:00:00 2001 From: Marcin Matlag Date: Tue, 30 Jun 2026 12:34:07 +0200 Subject: [PATCH] Fixes bug #5362, use local initiator IP for BGP neighbor as update-source value * Added validation - options are mutually exclusive * FRR does not complain if given address is not on the host at the moment --- .../Quagga/forms/dialogEditBGPNeighbor.xml | 4 ++-- .../mvc/app/models/OPNsense/Quagga/BGP.php | 20 +++++++++++++++++++ .../mvc/app/views/OPNsense/Quagga/bgp.volt | 10 ++++++++++ .../templates/OPNsense/Quagga/bgpd.conf | 4 +++- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/dialogEditBGPNeighbor.xml b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/dialogEditBGPNeighbor.xml index 245b6505c5..ffdf92f345 100644 --- a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/dialogEditBGPNeighbor.xml +++ b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/dialogEditBGPNeighbor.xml @@ -70,7 +70,7 @@ text true - Specify the local IP address used to establish connections with the neighbor. Only relevant for MD5 authentication. + Specify the local IP address (IPv4 or IPv6) used as the source to initiate BGP/BFD sessions, including MD5 authentication. Mutually exclusive with Update-Source Interface. false @@ -79,7 +79,7 @@ neighbor.updatesource dropdown - Interface (IPv4) where BGP sessions are sourced from, typically required when using loopback addresses. + Interface (IPv4) where BGP sessions are sourced from, typically required when using loopback addresses. Mutually exclusive with Local Initiator IP. false diff --git a/net/frr/src/opnsense/mvc/app/models/OPNsense/Quagga/BGP.php b/net/frr/src/opnsense/mvc/app/models/OPNsense/Quagga/BGP.php index 6af510a0c2..58f75e3bd4 100644 --- a/net/frr/src/opnsense/mvc/app/models/OPNsense/Quagga/BGP.php +++ b/net/frr/src/opnsense/mvc/app/models/OPNsense/Quagga/BGP.php @@ -2,6 +2,7 @@ namespace OPNsense\Quagga; +use OPNsense\Base\Messages\Message; use OPNsense\Base\BaseModel; /* @@ -28,4 +29,23 @@ */ class BGP extends BaseModel { + /** + * {@inheritdoc} + */ + public function performValidation($validateFullModel = false) + { + $messages = parent::performValidation($validateFullModel); + foreach ($this->neighbors->neighbor->iterateItems() as $neighbor) { + if (!$validateFullModel && !$neighbor->isFieldChanged()) { + continue; + } + if (!$neighbor->updatesource->isEmpty() && !$neighbor->localip->isEmpty()) { + $messages->appendMessage(new Message( + gettext("Update-Source Interface and Local Initiator IP are mutually exclusive."), + $neighbor->updatesource->__reference + )); + } + } + return $messages; + } } diff --git a/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/bgp.volt b/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/bgp.volt index 9cac76ec23..fb591d4cbb 100644 --- a/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/bgp.volt +++ b/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/bgp.volt @@ -38,6 +38,16 @@ POSSIBILITY OF SUCH DAMAGE. } }); + function checkNeighborSourceConflict() { + const $block = $('#help_block_neighbor\\.updatesource'); + if ($('#neighbor\\.updatesource').val() && $('#neighbor\\.localip').val()) { + $block.text('{{ lang._("Update-Source Interface and Local Initiator IP are mutually exclusive.") }}'); + } else { + $block.text(''); + } + } + $(document).on('change', '#neighbor\\.updatesource, #neighbor\\.localip', checkNeighborSourceConflict); + mapDataToFormUI({'frm_bgp_settings':"/api/quagga/bgp/get"}).done(function(data){ formatTokenizersUI(); $('.selectpicker').selectpicker('refresh'); diff --git a/net/frr/src/opnsense/service/templates/OPNsense/Quagga/bgpd.conf b/net/frr/src/opnsense/service/templates/OPNsense/Quagga/bgpd.conf index d1e5b4073f..58a42c58b4 100644 --- a/net/frr/src/opnsense/service/templates/OPNsense/Quagga/bgpd.conf +++ b/net/frr/src/opnsense/service/templates/OPNsense/Quagga/bgpd.conf @@ -138,7 +138,9 @@ router bgp {{ OPNsense.quagga.bgp.asnumber }} {% if 'disable_connected_check' in neighbor and neighbor.disable_connected_check == '1' %} neighbor {{ neighbor.address }} disable-connected-check {% endif %} -{% if ':' not in neighbor.address and 'updatesource' in neighbor and neighbor.updatesource != '' %} +{% if 'localip' in neighbor and neighbor.localip != '' %} + neighbor {{ neighbor.address }} update-source {{ neighbor.localip }} +{% elif ':' not in neighbor.address and 'updatesource' in neighbor and neighbor.updatesource != '' %} neighbor {{ neighbor.address }} update-source {{ physical_interface(neighbor.updatesource) }} {% endif %} {% if ':' in neighbor.address and 'linklocalinterface' in neighbor and neighbor.linklocalinterface != '' %}