From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id E10411FF0B0 for ; Fri, 23 Oct 2026 10:57:19 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 108532162C; Fri, 23 Oct 2026 10:57:19 +0200 (CEST) Date: Fri, 23 Oct 2026 10:57:13 +0200 From: Gabriel Goller To: Stefan Hanreich Subject: Re: [PATCH proxmox-ve-rs 03/15] sdn: generate fabric routing configuration in zone VRFs Message-ID: References: <20260821140404.322081-1-g.goller@proxmox.com> <20260821140404.322081-4-g.goller@proxmox.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: User-Agent: NeoMutt/20260504 X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1792745834263 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.255 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: ANCGBD3NEXDDDOQ2BIJLMJGM4YOVWA3S X-Message-ID-Hash: ANCGBD3NEXDDDOQ2BIJLMJGM4YOVWA3S X-MailFrom: g.goller@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: pve-devel@lists.proxmox.com X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: On 21.10.2026 18:14, Stefan Hanreich wrote: > On 8/21/26 4:03 PM, Gabriel Goller wrote: > > Map a fabric zone to the corresponding vrf_ FRR instance. Generate > > OSPF routers and interfaces in that VRF, and store BGP routers under the > > same VRF name. Keep fabrics without a zone in the default routing table. > > > > Collect OSPF redistribution rules on the router for the selected VRF so > > that several fabrics in one VRF contribute to the same OSPF instance. > > This keeps protocol configuration and interface configuration in the > > routing table selected by the zone. > > > > Signed-off-by: Gabriel Goller > > --- > > proxmox-ve-config/src/sdn/fabric/frr.rs | 82 ++++++++++++++++++------- > > 1 file changed, 61 insertions(+), 21 deletions(-) > > > > diff --git a/proxmox-ve-config/src/sdn/fabric/frr.rs b/proxmox-ve-config/src/sdn/fabric/frr.rs > > index 9203dcd16dae..ca0fbfe6d542 100644 > > --- a/proxmox-ve-config/src/sdn/fabric/frr.rs > > +++ b/proxmox-ve-config/src/sdn/fabric/frr.rs > > @@ -207,27 +210,50 @@ pub fn build_fabric( > > .get_or_insert(node.ip().expect("node must have an ipv4 address")); > > > > let fabric = ospf_entry.fabric_section(); > > + let vrf_name = fabric_vrf_name(fabric.properties().zone()); > > > > let frr_word_area = ser::FrrWord::new(fabric.properties().area.to_string())?; > > let frr_area = ser::ospf::Area::new(frr_word_area)?; > > > > - if frr_config.ospf.router.is_none() { > > - let mut ospf_router = build_ospf_router(*router_id)?; > > - > > - ospf_router.redistribute = fabric > > - .properties() > > - .redistributions() > > - .into_iter() > > - .cloned() > > - .map(OspfRedistribution::from) > > - .collect(); > > + let redistributions = fabric > > + .properties() > > + .redistributions() > > + .into_iter() > > + .cloned() > > + .map(OspfRedistribution::from); > > + > > + let ospf_router = if vrf_name == VrfName::Default { > > + if frr_config.ospf.router.is_none() { > > + frr_config.ospf.router = Some(build_ospf_router(*router_id)?); > > + } > > + frr_config > > + .ospf > > + .router > > + .as_mut() > > + .expect("router was inserted") > > + } else { > > + if !frr_config.ospf.vrf_router.contains_key(&vrf_name) { > > + frr_config > > + .ospf > > + .vrf_router > > + .insert(vrf_name.clone(), build_ospf_router(*router_id)?); > > + } > > + frr_config > > + .ospf > > + .vrf_router > > + .get_mut(&vrf_name) > > + .expect("router was inserted") > > could be something like this? > > vrf_router.entry(vrf_name).or_insert_with(|| vrf_router) > build_ospf_router returns a Result, so I'll use match frr_config.ospf.vrf_router.entry(vrf_name.clone()) { btree_map::Entry::Occupied(entry) => entry.into_mut(), btree_map::Entry::Vacant(entry) => { entry.insert(build_ospf_router(*router_id)?) } } Building the ospf_router eagerly before is weird IMO, because it could be unused. > > + }; > > + ospf_router.redistribute.extend(redistributions); > > > > - frr_config.ospf.router = Some(ospf_router); > > - } > > + let interface_vrf = match &vrf_name { > > + VrfName::Default => None, > > + custom => Some(custom.clone()), > > + }; > > > > // Add dummy interface > > let (interface, interface_name) = > > - build_ospf_dummy_interface(fabric_id, frr_area.clone())?; > > + build_ospf_dummy_interface(fabric_id, frr_area.clone(), interface_vrf.clone())?; > > > > if frr_config > > .ospf > > @@ -242,7 +268,7 @@ pub fn build_fabric( > > > > for interface in node.properties().interfaces.iter() { > > let (interface, interface_name) = > > - build_ospf_interface(frr_area.clone(), interface)?; > > + build_ospf_interface(frr_area.clone(), interface, interface_vrf.clone())?; > > > > if frr_config > > .ospf > > @@ -309,6 +335,7 @@ pub fn build_fabric( > > }; > > > > let fabric = bgp_entry.fabric_section(); > > + let vrf_name = fabric_vrf_name(fabric.properties().zone()); > > > > let local_asn = properties.asn().as_u32(); > > > > @@ -323,7 +350,7 @@ pub fn build_fabric( > > let local_as = frr_config > > .bgp > > .vrf_router > > - .get(&VrfName::Default) > > + .get(&vrf_name) > > .filter(|existing| existing.asn != local_asn) > > .map(|_| LocalAsSettings { > > asn: local_asn, > > [snip]