From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 33B781FF13A for ; Wed, 01 Apr 2026 16:42:56 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 2FAF5325DF; Wed, 1 Apr 2026 16:41:04 +0200 (CEST) From: Stefan Hanreich To: pve-devel@lists.proxmox.com Subject: [PATCH access-control/cluster/network/proxmox{-ve-rs,-perl-rs} v2 00/34] Add support for route maps / prefix lists to SDN Date: Wed, 1 Apr 2026 16:39:09 +0200 Message-ID: <20260401143957.386809-1-s.hanreich@proxmox.com> X-Mailer: git-send-email 2.47.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1775054347836 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.553 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment POISEN_SPAM_PILL 0.1 Meta: its spam POISEN_SPAM_PILL_1 0.1 random spam to be learned in bayes POISEN_SPAM_PILL_3 0.1 random spam to be learned in bayes 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: PFKS25B6HAQMR7NH5HDCSLCRJFURS5OZ X-Message-ID-Hash: PFKS25B6HAQMR7NH5HDCSLCRJFURS5OZ X-MailFrom: s.hanreich@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 X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: ## Introduction This patch adds support for managing route maps and prefix lists to the SDN stack. With this patch series, route maps can be applied to the BGP and EVPN controller for incoming / outgoing route filtering. There are currently some other features in development that would make use of route maps as well, namely: * VRF route leaking * Route Redistribution for Fabrics Prefix Lists can be used for matching inside route map match statements. They are implemented so they can be used inside route map match statements for now. FRR also provides access lists, which provide a subset of the functionality of prefix lists. For that reason, we decided to omit access lists for now, since everything can be modeled with prefix lists as well. Ran this against the e2e test suite, with all tests passing! ## Motivation There are a lot of use-cases for enabling users to create their own route-maps, which was currently only possible by utilizing frr.conf.local - which was clunky and prone to issues. Route maps can be used for filtering in/outgoing routes and modifiy them, so users could e.g. only selectively advertise some routes via BGP or only import certain EVPN routes from outside. It also allows us to programmatically manage route maps via the UI, e.g. for deeper EVPN integration in PDM. This opens up a lot of possibilities for new features. ## Configuration Format This patch series adds two new configuration files, route-maps.cfg and prefix-lists.cfg in /etc/pve/sdn. ### route-maps.cfg An example route map configuration looks as follows: route-map-entry: example_123 action permit match key=vni,value=23487 set key=tag,value=23487 This would create the following FRR route map entry: route-map example permit 123 match evpn vni 23487 set tag 23487 Every entry in route-maps.cfg maps to an entry in a route map. They are identified by their name as well as their ordering number. `example_123` specifies the 123th entry in the route map 'example'. The main reason for choosing this format is, that having a single section for one route-map would be quite unwieldy. It'd require some format like this, which is pretty awkward to handle / validate: route-map-entry: example action permit,seq=123 match key=vni,value=23487,seq=123 set key=tag,value=23487,seq=123 >>From a UI POV editing singular route map entries seems better as well, and with the mapping of section entries to route map entries, a suitable API design follows quite naturally and easily maps to the respective section config entries, without too much data mangling required. ### prefix-lists.cfg An example prefix list configuration looks as follows: prefix-list: example-1 entries action=permit,prefix=192.0.2.0/24 entries action=permit,prefix=192.0.2.0/24,le=32 entries action=permit,prefix=192.0.2.0/24,le=32,ge=24,seq=123 This would create the following FRR prefix list: ip prefix-list example-1 permit 192.0.2.0/24 ip prefix-list example-1 permit 192.0.2.0/24 le 32 ip prefix-list example-1 seq 123 permit 192.0.2.0/24 le 32 ge 24 ## API endpoints This patch series introduces the following API endpoints in the /cluster/sdn subfolder: ### Route Maps GET /route-maps - lists all route map entries GET /route-maps/ - lists all route map entries for the route map GET /route-maps// - gets the order'th entry in route map POST /route-maps - creates a new route map entry PUT /route-maps// - updates the order'th entry in route map DELETE /route-maps// - deletes the order'th entry in route map ### Prefix Lists GET /prefix-lists - lists all prefix lists GET /prefix-lists/ - get prefix list POST /prefix-lists - create a new prefix list PUT /prefix-lists/ - update prefix list DELETE /prefix-lists/ - delete prefix list ## Open questions How should we handle overriding the auto-generated route maps (e.g. in the EVPN controller) and prefix lists? Currently this patch series disallows creating any route map / prefix list that have the same name as PVE auto-generated ones via the API. They can be overridden by creating a new route map and then selecting it in the respective entity (e.g. via route-map-in in the EVPN controller). Pre-defined prefix-lists cannot currently be overridden, since this usually makes little sense, as they are used in the auto-generated route maps, which can be overridden anyway. This is the most restrictive option, which leaves the possibility of re-thinking our approach depending on if this comes up in the future. How should we handle setting custom route maps on exit nodes? For exit nodes a special route map entry is generated that disallows importing default routes to avoid traffic loops between exit nodes. With the current implementation, those entries still get created and executed in order to make it easy for users to use route maps on EVPN exit nodes. This also makes it impossible to override this behavior, since a route map terminates with the first matching entry. The proposed solution for this is a future patch series, that allows defining multiple EVPN controllers and limit them to specific nodes. Users could then manually build what we currently do on exit nodes together with this patch series. ## Dependencies proxmox-frr depends on proxmox-frr-templates proxmox-frr depends on proxmox-sdn-types proxmox-ve-config depends on proxmox-sdn-types proxmox-ve-config depends on proxmox-frr proxmox-perl-rs depends on proxmox-ve-config pve-network depends on proxmox-perl-rs pve-network depends on pve-cluster Changes from v1 (Thanks @Gabriel, @Hannes, @Wolfgang): * rebase on top of current master * fix newly introduced vtysh tests * include missing access-control patch * fix an error in the permission API path of GET /route-maps/{route-map-id} * fix permission check in list route maps / prefix lists endpoint * implement From instead of Into for section config to frr conversions * replace core::* imports with std::* * improve comments in both pve-rs modules * use get() instead of iter().find() in get methods of both pve-rs modules * use entry API when creating new entities in both pve-rs modules * removed duplicate PrefixList implementation block * fixed pending parameter in GET endpoints * add route maps / prefix lists to has_pending_changes method * fixed change detection for newly introduced fields in prefix lists / route maps * fixed reserved id 'loopbacks_ips' for prefix lists (instead of reserving loopback_ips) * properly pass delete parameter to the route map update pve-rs method * remove additional prefix list / route map rendering methods and just use dump instead in the ve-config FRR integration tests * improved documentation of the FRR route map generation logic, so it better explains *how* the configuration gets merged. * added another test-case for EVPN zones with a controller with custom route-map + exit nodes * implement exit action and call features of route maps * jump into user-supplied route maps instead of replacing them directly, to avoid breaking exit-node setups if users do not recreate the auto-generated route map * improve indentation of FRR template * update tests to reflect changes w.r.t. FRR config generation * improve error message on trying to GET non-existing route map entry * move the tests from the frr module in route maps / prefix lists to the integration tests in proxmox-ve-config * make order u16 instead of u32, because in FRR it is an u16 as well * add unit tests to some new types * change route map merging logic to overwrite existing route maps, if an entry with the same route map name exists in the section config * added separate patch for PrefixListName::new, since the vtysh patch from gabriel hasn't been applied yet, but this patch series requires the new function pve-cluster: Stefan Hanreich (2): cfs: add 'sdn/route-maps.cfg' to observed files cfs: add 'sdn/prefix-lists.cfg' to observed files src/PVE/Cluster.pm | 2 ++ src/pmxcfs/status.c | 2 ++ 2 files changed, 4 insertions(+) pve-access-control: Stefan Hanreich (1): permissions: add ACL path for prefix-lists and route-maps src/PVE/AccessControl.pm | 4 ++++ 1 file changed, 4 insertions(+) proxmox-ve-rs: Stefan Hanreich (13): frr: add constructor to prefix list name sdn-types: add common route-map helper types frr: change order type to u16 frr: implement routemap match/set statements via adjacent tagging frr: implement support for call and exit action frr-templates: change route maps template to adapt to new frr types ve-config: fabrics: adapt frr config generation ve-config: add prefix list section config ve-config: frr: implement frr config generation for prefix lists ve-config: add route map section config ve-config: frr: implement frr config generation for route maps ve-config: add prefix lists integration tests ve-config: add route maps integration tests .../templates/route_maps.jinja | 19 +- proxmox-frr/Cargo.toml | 2 +- proxmox-frr/debian/control | 2 + proxmox-frr/src/ser/route_map.rs | 108 ++- proxmox-sdn-types/src/bgp.rs | 62 ++ proxmox-sdn-types/src/lib.rs | 179 +++++ proxmox-ve-config/src/sdn/fabric/frr.rs | 33 +- proxmox-ve-config/src/sdn/mod.rs | 2 + proxmox-ve-config/src/sdn/prefix_list.rs | 220 ++++++ proxmox-ve-config/src/sdn/route_map.rs | 728 ++++++++++++++++++ proxmox-ve-config/tests/prefix_lists/main.rs | 112 +++ proxmox-ve-config/tests/route_maps/main.rs | 146 ++++ 12 files changed, 1561 insertions(+), 52 deletions(-) create mode 100644 proxmox-sdn-types/src/bgp.rs create mode 100644 proxmox-ve-config/src/sdn/prefix_list.rs create mode 100644 proxmox-ve-config/src/sdn/route_map.rs create mode 100644 proxmox-ve-config/tests/prefix_lists/main.rs create mode 100644 proxmox-ve-config/tests/route_maps/main.rs proxmox-perl-rs: Stefan Hanreich (3): pve-rs: sdn: add route maps module pve-rs: sdn: add prefix lists module sdn: add prefix list / route maps to frr config generation helper pve-rs/Cargo.toml | 1 + pve-rs/Makefile | 2 + pve-rs/src/bindings/sdn/mod.rs | 30 ++- pve-rs/src/bindings/sdn/prefix_lists.rs | 192 +++++++++++++++++ pve-rs/src/bindings/sdn/route_maps.rs | 262 ++++++++++++++++++++++++ 5 files changed, 484 insertions(+), 3 deletions(-) create mode 100644 pve-rs/src/bindings/sdn/prefix_lists.rs create mode 100644 pve-rs/src/bindings/sdn/route_maps.rs pve-network: Stefan Hanreich (15): controller: bgp: evpn: adapt to new match / set frr config syntax sdn: add prefix lists module api2: add prefix list module sdn: add route map module api2: add route maps api module api2: add route map module api2: add route map entry module evpn controller: add route_map_{in,out} parameter bgp controller: allow configuring custom route maps sdn: change detection for route maps / prefix lists sdn: generate route map / prefix list configuration on sdn apply tests: add simple route map test case tests: add bgp evpn route map/prefix list testcase tests: add route map with prefix list testcase tests: add exit node with custom route map testcase src/PVE/API2/Network/SDN.pm | 14 + src/PVE/API2/Network/SDN/Makefile | 13 +- src/PVE/API2/Network/SDN/PrefixLists.pm | 254 ++++++++++++++++++ src/PVE/API2/Network/SDN/RouteMaps.pm | 140 ++++++++++ src/PVE/API2/Network/SDN/RouteMaps/Makefile | 9 + .../API2/Network/SDN/RouteMaps/RouteMap.pm | 93 +++++++ .../Network/SDN/RouteMaps/RouteMapEntry.pm | 138 ++++++++++ src/PVE/Network/SDN.pm | 30 ++- src/PVE/Network/SDN/Controllers/BgpPlugin.pm | 24 +- src/PVE/Network/SDN/Controllers/EvpnPlugin.pm | 42 +-- src/PVE/Network/SDN/Controllers/Plugin.pm | 14 + src/PVE/Network/SDN/Makefile | 14 +- src/PVE/Network/SDN/PrefixLists.pm | 134 +++++++++ src/PVE/Network/SDN/RouteMaps.pm | 192 +++++++++++++ .../expected_controller_config | 80 ++++++ .../expected_sdn_interfaces | 41 +++ .../bgp_evpn_routemap_prefix_list/interfaces | 7 + .../bgp_evpn_routemap_prefix_list/sdn_config | 86 ++++++ .../evpn/routemap/expected_controller_config | 68 +++++ .../evpn/routemap/expected_sdn_interfaces | 41 +++ src/test/zones/evpn/routemap/interfaces | 7 + src/test/zones/evpn/routemap/sdn_config | 70 +++++ .../expected_controller_config | 101 +++++++ .../expected_sdn_interfaces | 41 +++ .../zones/evpn/routemap_exit_node/interfaces | 7 + .../zones/evpn/routemap_exit_node/sdn_config | 71 +++++ .../expected_controller_config | 53 ++++ .../expected_sdn_interfaces | 41 +++ .../evpn/routemap_prefix_list/interfaces | 7 + .../evpn/routemap_prefix_list/sdn_config | 58 ++++ 30 files changed, 1858 insertions(+), 32 deletions(-) create mode 100644 src/PVE/API2/Network/SDN/PrefixLists.pm create mode 100644 src/PVE/API2/Network/SDN/RouteMaps.pm create mode 100644 src/PVE/API2/Network/SDN/RouteMaps/Makefile create mode 100644 src/PVE/API2/Network/SDN/RouteMaps/RouteMap.pm create mode 100644 src/PVE/API2/Network/SDN/RouteMaps/RouteMapEntry.pm create mode 100644 src/PVE/Network/SDN/PrefixLists.pm create mode 100644 src/PVE/Network/SDN/RouteMaps.pm create mode 100644 src/test/zones/evpn/bgp_evpn_routemap_prefix_list/expected_controller_config create mode 100644 src/test/zones/evpn/bgp_evpn_routemap_prefix_list/expected_sdn_interfaces create mode 100644 src/test/zones/evpn/bgp_evpn_routemap_prefix_list/interfaces create mode 100644 src/test/zones/evpn/bgp_evpn_routemap_prefix_list/sdn_config create mode 100644 src/test/zones/evpn/routemap/expected_controller_config create mode 100644 src/test/zones/evpn/routemap/expected_sdn_interfaces create mode 100644 src/test/zones/evpn/routemap/interfaces create mode 100644 src/test/zones/evpn/routemap/sdn_config create mode 100644 src/test/zones/evpn/routemap_exit_node/expected_controller_config create mode 100644 src/test/zones/evpn/routemap_exit_node/expected_sdn_interfaces create mode 100644 src/test/zones/evpn/routemap_exit_node/interfaces create mode 100644 src/test/zones/evpn/routemap_exit_node/sdn_config create mode 100644 src/test/zones/evpn/routemap_prefix_list/expected_controller_config create mode 100644 src/test/zones/evpn/routemap_prefix_list/expected_sdn_interfaces create mode 100644 src/test/zones/evpn/routemap_prefix_list/interfaces create mode 100644 src/test/zones/evpn/routemap_prefix_list/sdn_config Summary over all repositories: 50 files changed, 3911 insertions(+), 87 deletions(-) -- Generated by murpp 0.11.0