diff --git a/net/frr/pkg-descr b/net/frr/pkg-descr
index 829df3bb5..1e52df4ba 100644
--- a/net/frr/pkg-descr
+++ b/net/frr/pkg-descr
@@ -14,6 +14,10 @@ Plugin Changelog
1.23
* Add route-reflector-client to BGP neighbor config
+* bugfix: BGP Prefix List "IP version" field appears to be ineffective (#2602)
+* feature: IPv6 link-local neighbors require an interface to be specified (#2604)
+* feature: multiprotocol BGP sessions are not supported (#2606)
+* bugfix: Multiple BGP peers causes config generation error (#2623)
1.22
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 3ae8d6830..f494060aa 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
@@ -26,7 +26,14 @@
neighbor.updatesource
select_multiple
- Physical name of the interface facing the peer
+ FRR documentation for more information.]]>
+
+
+ neighbor.linklocalinterface
+
+ dropdown
+ true
+ FRR documentation for more information.]]>
neighbor.nexthopself
@@ -38,6 +45,13 @@
checkbox
+
+ neighbor.multiprotocol
+
+ checkbox
+ true
+ RFC 2283]]>
+
neighbor.rrclient
diff --git a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/dialogEditBGPPrefixLists.xml b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/dialogEditBGPPrefixLists.xml
index 115e015de..276e4e5d4 100644
--- a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/dialogEditBGPPrefixLists.xml
+++ b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/dialogEditBGPPrefixLists.xml
@@ -20,7 +20,7 @@
prefixlist.version
- select_multiple
+ dropdown
Set the IP version to use.
@@ -32,7 +32,7 @@
prefixlist.action
- select_multiple
+ dropdown
Set permit for match or deny to negate the rule.
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 767e8d008..cc5b2782b 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
@@ -66,6 +66,15 @@
/^(?!0).*$/
+
+
+ N
+ N
+ Y
+
+ /^(?!0).*$/
+
+
0
N
@@ -74,6 +83,10 @@
0
N
+
+ 0
+ N
+
0
N
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 2505bb113..e0a9b8d5c 100644
--- a/net/frr/src/opnsense/service/templates/OPNsense/Quagga/bgpd.conf
+++ b/net/frr/src/opnsense/service/templates/OPNsense/Quagga/bgpd.conf
@@ -1,5 +1,31 @@
{% if helpers.exists('OPNsense.quagga.bgp.enabled') and OPNsense.quagga.bgp.enabled == '1' %}
-{% from 'OPNsense/Macros/interface.macro' import physical_interface %}
+{% from 'OPNsense/Macros/interface.macro' import physical_interface %}
+{% set addressFamilies = ['ipv4', 'ipv6' ] %}
+{% set neighbors = {'ipv4': [], 'ipv6': []} %}
+{% set networks = {'ipv4': [], 'ipv6': []} %}
+
+{% if helpers.exists('OPNsense.quagga.bgp.neighbors.neighbor') %}
+{% for neighbor in helpers.toList('OPNsense.quagga.bgp.neighbors.neighbor') %}
+{% if neighbor.enabled == '1' and neighbor.multiprotocol == '1' %}
+{# // the .append() method in Jinja2 returns "None", so filter through default() to suppress #}
+{{ neighbors['ipv4'].append(neighbor) | default("", True) }}
+{{ neighbors['ipv6'].append(neighbor) | default("", True) }}
+{% elif neighbor.enabled == '1' and ':' not in neighbor.address %}
+{{ neighbors['ipv4'].append(neighbor) | default("", True) }}
+{% elif neighbor.enabled == '1' and ':' in neighbor.address %}
+{{ neighbors['ipv6'].append(neighbor) | default("", True) }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if helpers.exists('OPNsense.quagga.bgp.networks') %}
+{% for network in OPNsense.quagga.bgp.networks.split(',') %}
+{% if ':' not in network %}
+{{ networks['ipv4'].append(network) | default("", True) }}
+{% elif ':' in network %}
+{{ networks['ipv6'].append(network) | default("", True) }}
+{% endif %}
+{% endfor %}
+{% endif %}
!
! Zebra configuration saved from vty
! 2017/03/03 20:21:04
@@ -31,9 +57,12 @@ router bgp {{ OPNsense.quagga.bgp.asnumber }}
{% if 'bfd' in neighbor and neighbor.bfd == '1' %}
neighbor {{ neighbor.address }} bfd
{% endif %}
- {% if 'updatesource' in neighbor and neighbor.updatesource != '' %}
+{% if ':' 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 != '' %}
+ neighbor {{ neighbor.address }} interface {{ physical_interface(neighbor.linklocalinterface) }}
+{% endif %}
{% if 'multihop' in neighbor and neighbor.multihop == '1' %}
neighbor {{ neighbor.address }} ebgp-multihop
{% endif %}
@@ -48,145 +77,72 @@ router bgp {{ OPNsense.quagga.bgp.asnumber }}
{% endif %}
{% endfor %}
{% endif %}
- address-family ipv4 unicast
-{% if helpers.exists('OPNsense.quagga.bgp.networks') %}
-{% for network in OPNsense.quagga.bgp.networks.split(',') %}
-{% if ':' not in network %}
- network {{ network }}
-{% endif %}
-{% endfor %}
-{% endif %}
+
+{% for addressFamily in addressFamilies %}
+ address-family {{ addressFamily }} unicast
{% if helpers.exists('OPNsense.quagga.bgp.redistribute') and OPNsense.quagga.bgp.redistribute != '' %}
{% for bgp_redistribute in OPNsense.quagga.bgp.redistribute.split(',') %}
redistribute {{ bgp_redistribute }}
{% endfor %}
{% endif %}
-{% if helpers.exists('OPNsense.quagga.bgp.neighbors.neighbor') and ':' not in OPNsense.quagga.bgp.neighbors.neighbor.address %}
-{% for neighbor in helpers.toList('OPNsense.quagga.bgp.neighbors.neighbor') %}
-{% if neighbor.enabled == '1' and ':' not in neighbor.address %}
-{% if 'nexthopself' in neighbor and neighbor.nexthopself == '1' %}
- neighbor {{ neighbor.address }} next-hop-self
-{% endif %}
-{% if 'rrclient' in neighbor and neighbor.rrclient == '1' %}
- neighbor {{ neighbor.address }} route-reflector-client
-{% endif %}
-{% if 'defaultoriginate' in neighbor and neighbor.defaultoriginate == '1' %}
- neighbor {{ neighbor.address }} default-originate
-{% endif %}
-{% if neighbor.linkedPrefixlistIn|default("") != "" %}
-{% for prefixlist in neighbor.linkedPrefixlistIn.split(",") %}
-{% set prefixlist2_data = helpers.getUUID(prefixlist) %}
-{% if prefixlist2_data != {} and prefixlist2_data.enabled == '1' %}
- neighbor {{ neighbor.address }} prefix-list {{ prefixlist2_data.name }} in
-{% endif %}
-{% endfor %}
-{% endif %}
-{% if neighbor.linkedPrefixlistOut|default("") != "" %}
-{% for prefixlist in neighbor.linkedPrefixlistOut.split(",") %}
-{% set prefixlist_data = helpers.getUUID(prefixlist) %}
-{% if prefixlist_data != {} and prefixlist_data.enabled == '1' %}
- neighbor {{ neighbor.address }} prefix-list {{ prefixlist_data.name }} out
-{% endif %}
-{% endfor %}
-{% endif %}
-{% if neighbor.linkedRoutemapIn|default("") != "" %}
-{% for aspath in neighbor.linkedRoutemapIn.split(",") %}
-{% set routemap2_data = helpers.getUUID(aspath) %}
-{% if routemap2_data != {} and routemap2_data.enabled == '1' %}
- neighbor {{ neighbor.address }} route-map {{ routemap2_data.name }} in
-{% endif %}
-{% endfor %}
-{% endif %}
-{% if neighbor.linkedRoutemapOut|default("") != "" %}
-{% for aspath in neighbor.linkedRoutemapOut.split(",") %}
-{% set routemap_data = helpers.getUUID(aspath) %}
-{% if routemap_data != {} and routemap_data.enabled == '1' %}
- neighbor {{ neighbor.address }} route-map {{ routemap_data.name }} out
-{% endif %}
-{% endfor %}
-{% endif %}
-{% endif %}
-{% endfor %}
-{% endif %}
-{% if helpers.exists('OPNsense.quagga.bgp.neighbors.neighbor') %}
-{% for neighbor in helpers.toList('OPNsense.quagga.bgp.neighbors.neighbor') %}
-{% if neighbor.enabled == '1' and ':' in neighbor.address %}
- no neighbor {{ neighbor.address }} activate
-{% endif %}
-{% endfor %}
-{% endif %}
- exit-address-family
-!
- address-family ipv6 unicast
-{% if helpers.exists('OPNsense.quagga.bgp.networks') and ':' in OPNsense.quagga.bgp.networks %}
-{% for network in OPNsense.quagga.bgp.networks.split(',') %}
-{% if ':' in network %}
+{% for network in networks[addressFamily] %}
network {{ network }}
-{% endif %}
-{% endfor %}
-{% endif %}
-{% if helpers.exists('OPNsense.quagga.bgp.redistribute') and OPNsense.quagga.bgp.redistribute != '' %}
-{% for bgp_redistribute in OPNsense.quagga.bgp.redistribute.split(',') %}
- redistribute {{ bgp_redistribute }}
-{% endfor %}
-{% endif %}
-{% if helpers.exists('OPNsense.quagga.bgp.neighbors.neighbor') %}
-{% for neighbor in helpers.toList('OPNsense.quagga.bgp.neighbors.neighbor') %}
-{% if neighbor.enabled == '1' and ':' in neighbor.address %}
+{% endfor %}
+{% for neighbor in neighbors[addressFamily] %}
neighbor {{ neighbor.address }} activate
-{% if 'nexthopself' in neighbor and neighbor.nexthopself == '1' %}
+{% if 'nexthopself' in neighbor and neighbor.nexthopself == '1' %}
neighbor {{ neighbor.address }} next-hop-self
-{% endif %}
-{% if 'rrclient' in neighbor and neighbor.rrclient == '1' %}
+{% endif %}
+{% if 'rrclient' in neighbor and neighbor.rrclient == '1' %}
neighbor {{ neighbor.address }} route-reflector-client
-{% endif %}
-{% if 'defaultoriginate' in neighbor and neighbor.defaultoriginate == '1' %}
+{% endif %}
+{% if 'defaultoriginate' in neighbor and neighbor.defaultoriginate == '1' %}
neighbor {{ neighbor.address }} default-originate
-{% endif %}
-{% if neighbor.linkedPrefixlistIn|default("") != "" %}
-{% for prefixlist in neighbor.linkedPrefixlistIn.split(",") %}
-{% set prefixlist2_data = helpers.getUUID(prefixlist) %}
-{% if prefixlist2_data != {} and prefixlist2_data.enabled == '1' %}
+{% endif %}
+{% if neighbor.linkedPrefixlistIn|default("") != "" %}
+{% for prefixlist in neighbor.linkedPrefixlistIn.split(",") %}
+{% set prefixlist2_data = helpers.getUUID(prefixlist) %}
+{% if prefixlist2_data != {} and prefixlist2_data.enabled == '1' %}
neighbor {{ neighbor.address }} prefix-list {{ prefixlist2_data.name }} in
-{% endif %}
-{% endfor %}
{% endif %}
-{% if neighbor.linkedPrefixlistOut|default("") != "" %}
-{% for prefixlist in neighbor.linkedPrefixlistOut.split(",") %}
-{% set prefixlist_data = helpers.getUUID(prefixlist) %}
-{% if prefixlist_data != {} and prefixlist_data.enabled == '1' %}
+{% endfor %}
+{% endif %}
+{% if neighbor.linkedPrefixlistOut|default("") != "" %}
+{% for prefixlist in neighbor.linkedPrefixlistOut.split(",") %}
+{% set prefixlist_data = helpers.getUUID(prefixlist) %}
+{% if prefixlist_data != {} and prefixlist_data.enabled == '1' %}
neighbor {{ neighbor.address }} prefix-list {{ prefixlist_data.name }} out
-{% endif %}
-{% endfor %}
{% endif %}
-{% if neighbor.linkedRoutemapIn|default("") != "" %}
-{% for aspath in neighbor.linkedRoutemapIn.split(",") %}
-{% set routemap2_data = helpers.getUUID(aspath) %}
-{% if routemap2_data != {} and routemap2_data.enabled == '1' %}
+{% endfor %}
+{% endif %}
+{% if neighbor.linkedRoutemapIn|default("") != "" %}
+{% for aspath in neighbor.linkedRoutemapIn.split(",") %}
+{% set routemap2_data = helpers.getUUID(aspath) %}
+{% if routemap2_data != {} and routemap2_data.enabled == '1' %}
neighbor {{ neighbor.address }} route-map {{ routemap2_data.name }} in
-{% endif %}
-{% endfor %}
{% endif %}
-{% if neighbor.linkedRoutemapOut|default("") != "" %}
-{% for aspath in neighbor.linkedRoutemapOut.split(",") %}
-{% set routemap_data = helpers.getUUID(aspath) %}
-{% if routemap_data != {} and routemap_data.enabled == '1' %}
+{% endfor %}
+{% endif %}
+{% if neighbor.linkedRoutemapOut|default("") != "" %}
+{% for aspath in neighbor.linkedRoutemapOut.split(",") %}
+{% set routemap_data = helpers.getUUID(aspath) %}
+{% if routemap_data != {} and routemap_data.enabled == '1' %}
neighbor {{ neighbor.address }} route-map {{ routemap_data.name }} out
-{% endif %}
-{% endfor %}
{% endif %}
-{% endif %}
-{% endfor %}
-{% endif %}
+{% endfor %}
+{% endif %}
+{% endfor %}
exit-address-family
!
+{% endfor %}
+
{% if helpers.exists('OPNsense.quagga.bgp.prefixlists.prefixlist') %}
{% for prefixlist in helpers.sortDictList(OPNsense.quagga.bgp.prefixlists.prefixlist, 'name', 'seqnumber' ) %}
-{% if prefixlist.enabled == '1' and ':' not in prefixlist.network %}
+{% if prefixlist.enabled == '1' and prefixlist.version == 'IPv4' %}
ip prefix-list {{ prefixlist.name }} seq {{ prefixlist.seqnumber }} {{ prefixlist.action }} {{ prefixlist.network }}
{% endif %}
!
-{% if prefixlist.enabled == '1' and ':' in prefixlist.network %}
+{% if prefixlist.enabled == '1' and prefixlist.version == 'IPv6' %}
ipv6 prefix-list {{ prefixlist.name }} seq {{ prefixlist.seqnumber }} {{ prefixlist.action }} {{ prefixlist.network }}
{% endif %}
{% endfor %}