From d0d9a7ffb23d2660696f187d5d652aaecc6ff53f Mon Sep 17 00:00:00 2001
From: Maxfield Allison <42394355+maxfield-allison@users.noreply.github.com>
Date: Fri, 27 Mar 2026 03:55:15 -0500
Subject: [PATCH] net/frr: add BGP maximum-paths support for ECMP (#5340)
---
net/frr/Makefile | 2 +-
net/frr/pkg-descr | 4 ++++
.../app/controllers/OPNsense/Quagga/forms/bgp.xml | 14 ++++++++++++++
.../mvc/app/models/OPNsense/Quagga/BGP.xml | 10 ++++++++++
.../service/templates/OPNsense/Quagga/bgpd.conf | 6 ++++++
5 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/net/frr/Makefile b/net/frr/Makefile
index b5a3cb8bd..c7de56a07 100644
--- a/net/frr/Makefile
+++ b/net/frr/Makefile
@@ -1,5 +1,5 @@
PLUGIN_NAME= frr
-PLUGIN_VERSION= 1.51
+PLUGIN_VERSION= 1.52
PLUGIN_COMMENT= The FRRouting Protocol Suite
PLUGIN_DEPENDS= frr10-pythontools
PLUGIN_MAINTAINER= ad@opnsense.org
diff --git a/net/frr/pkg-descr b/net/frr/pkg-descr
index 03a39c947..f29324599 100644
--- a/net/frr/pkg-descr
+++ b/net/frr/pkg-descr
@@ -12,6 +12,10 @@ WWW: https://frrouting.org/
Plugin Changelog
================
+1.52
+
+* Add BGP maximum-paths support for ECMP multipath (contributed by maxfield-allison) (opnsense/plugins#4878)
+
1.51
* Add per-neighbor local-as option for BGP (contributed by danohn) (opnsense/plugins/pull/5308)
diff --git a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/bgp.xml b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/bgp.xml
index 5b1e02975..5be7fb2e2 100644
--- a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/bgp.xml
+++ b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/bgp.xml
@@ -67,4 +67,18 @@
checkbox
Enable extended logging of BGP neighbor changes.
+
+ bgp.maximumpaths
+
+ text
+ true
+ Maximum number of equal-cost paths for EBGP multipath (ECMP). Leave empty to use FRR default (1).
+
+
+ bgp.maximumpathsibgp
+
+ text
+ true
+ Maximum number of equal-cost paths for IBGP multipath (ECMP). Leave empty to use FRR default (1).
+
diff --git a/net/frr/src/opnsense/mvc/app/models/OPNsense/Quagga/BGP.xml b/net/frr/src/opnsense/mvc/app/models/OPNsense/Quagga/BGP.xml
index 6767a387a..788dd67b7 100644
--- a/net/frr/src/opnsense/mvc/app/models/OPNsense/Quagga/BGP.xml
+++ b/net/frr/src/opnsense/mvc/app/models/OPNsense/Quagga/BGP.xml
@@ -48,6 +48,16 @@
Y
+
+ 1
+ 128
+ The value shall be between 1 and 128 or left empty to use the default.
+
+
+ 1
+ 128
+ The value shall be between 1 and 128 or left empty to use the default.
+
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 1681660ee..7f151cf92 100644
--- a/net/frr/src/opnsense/service/templates/OPNsense/Quagga/bgpd.conf
+++ b/net/frr/src/opnsense/service/templates/OPNsense/Quagga/bgpd.conf
@@ -170,6 +170,12 @@ router bgp {{ OPNsense.quagga.bgp.asnumber }}
{% for addressFamily in addressFamilies %}
address-family {{ addressFamily }} unicast
+{% if helpers.exists('OPNsense.quagga.bgp.maximumpaths') and OPNsense.quagga.bgp.maximumpaths != '' %}
+ maximum-paths {{ OPNsense.quagga.bgp.maximumpaths }}
+{% endif %}
+{% if helpers.exists('OPNsense.quagga.bgp.maximumpathsibgp') and OPNsense.quagga.bgp.maximumpathsibgp != '' %}
+ maximum-paths ibgp {{ OPNsense.quagga.bgp.maximumpathsibgp }}
+{% endif %}
{% for redistribution in helpers.toList('OPNsense.quagga.bgp.redistributions.redistribution') %}
{% if redistribution.enabled == '1' %}
redistribute {{ redistribution.redistribute }}{% if redistribution.linkedRoutemap %} route-map {{ helpers.getUUID(redistribution.linkedRoutemap).name }}{% endif +%}