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 CFC361FF0A5 for ; Fri, 04 Sep 2026 11:38:45 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 8E72521590; Fri, 04 Sep 2026 11:38:45 +0200 (CEST) From: Hannes Laimer To: pve-devel@lists.proxmox.com Subject: [PATCH manager/network/proxmox{-ebpf,-perl-rs} 00/12] sdn: implement DHCP for all zones using eBPF Date: Fri, 4 Sep 2026 11:38:23 +0200 Message-ID: <20260904093835.1050030-1-h.laimer@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: 1788514714062 X-SPAM-LEVEL: Spam detection results: 0 AWL -1.029 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) 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 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 URIBL_BLACK 3 Contains an URL listed in the URIBL blacklist [types.rs] Message-ID-Hash: XYT3Q54XW5Q2OPBOF2OCFW5XYVMZS3ZP X-Message-ID-Hash: XYT3Q54XW5Q2OPBOF2OCFW5XYVMZS3ZP X-MailFrom: h.laimer@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: Adds a second DHCP backend, `ebpf`, next to dnsmasq, selectable per zone. It aims to replace dnsmasq eventually, for now it is a second implementation, which keeps a migration simple. Every zone type can enable DHCP through a dropdown selector, `dnsmasq` stays limited to simple zones. The responder is a subsystem of `proxmox-ebpf` [1], Perl reaches it through new pve-rs bindings (PVE::RS::SDN::Dhcp), so the pve-network patches need the pve-rs of this series. Currently only supports DHCPv4, but adding v6 is very possible once we're happy with the overall design. # How An eBPF program on the ingress of every guest tap parses DHCP requests, looks the client MAC up in a mac -> ip+options map and rewrites the request into the reply in place, redirected back out of the tap. The exchange never reaches the bridge. Everything else, including MACs without a map entry, passes untouched, so attaching is a no-op for unmanaged MACs. IPAM is the source of the assignments, the map is a per-node copy of the records. Every trigger below runs the same full pass, the plugin collects all records of the ebpf zones and the guest interfaces on their vnets, the responder diffs both against the kernel state, so programs, links and records converge from any starting point: - guest start / NIC hotplug / migration: add_dhcp_mapping already fires here, before the interface is plugged, a new tap_plug hook of the dhcp plugins then attaches the program. - mapping create/update/delete through the API: the editing node runs it and pokes the node running the guest to do the same through a new node endpoint (POST /nodes/{node}/sdn/dhcp-mapping), detached from the request. Best effort, an unreachable node catches up on its next apply or the guest's next start. - SDN apply: also refreshes the programs, a rebuild on a schema change is refilled in the same pass, and a zone switching its backend takes effect for running guests too. - boot: nothing is pinned, the first pass after boot loads the programs and fills the map. Subnets get a `dhcp-lease-time` property, used by both backends, dnsmasq keeps handing out infinite leases without it and the responder defaults to ten minutes. The responder identifies itself with the subnet gateway, so a subnet without one is not served, and it hands out IPv4 resolvers only, a v6 one configured on a v4 subnet is left out of the answers. Changes made directly on an external IPAM service are not detectable and the per-MAC answers are cached, so they are not picked up on apply either, exactly like with dnsmasq today. The pve-network patches apply on top of the separately posted patch pushing ipam API mapping changes to the dhcp backend [2]. pre-build packages are on sani(`packages/ebpf-dhcp-v1`) since the RFC: - every trigger runs the same full pass instead of per-trigger map updates, the responder diffs programs, links and records against the kernel state, so a schema rebuild is refilled by the pass that caused it and a zone switching to ebpf serves its running guests - the guest node is poked through a node endpoint, not all nodes - the tap plug goes through a hook of the dhcp plugin base - the bridge-change paths of guests push their record changes too - the records are collected under the macdb lock - a v6 resolver on a v4 subnet is left out instead of failing the pass, a subnet without a gateway is skipped with a warning - dnsmasq honours dhcp-lease-time as well - the mapping push endpoint checks the vnet belongs to the zone [1] https://lore.proxmox.com/pve-devel/20260904090458.990888-1-h.laimer@proxmox.com/T/#t [2] https://lore.proxmox.com/pve-devel/20260902125357.757029-1-h.laimer@proxmox.com/T/#u proxmox-ebpf: Hannes Laimer (2): dhcp: add per-tap responder BPF program dhcp: add responder subsystem Cargo.toml | 5 + debian/control | 6 +- src/dhcp/bpf/dhcp.bpf.c | 324 +++++++++++++++++++ src/dhcp/bpf/types.h | 25 ++ src/dhcp/mod.rs | 247 +++++++++++++++ src/dhcp/types.rs | 53 ++++ src/lib.rs | 3 + tests/dhcp.rs | 668 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 1330 insertions(+), 1 deletion(-) create mode 100644 src/dhcp/bpf/dhcp.bpf.c create mode 100644 src/dhcp/bpf/types.h create mode 100644 src/dhcp/mod.rs create mode 100644 src/dhcp/types.rs create mode 100644 tests/dhcp.rs proxmox-perl-rs: Hannes Laimer (1): pve-rs: sdn: add dhcp responder bindings pve-rs/Cargo.toml | 2 + pve-rs/Makefile | 1 + pve-rs/debian/control | 2 + pve-rs/src/bindings/sdn/dhcp.rs | 81 +++++++++++++++++++++++++++++++++ pve-rs/src/bindings/sdn/mod.rs | 1 + 5 files changed, 87 insertions(+) create mode 100644 pve-rs/src/bindings/sdn/dhcp.rs pve-network: Hannes Laimer (8): sdn: ipam: do not cache negative per-MAC answers, lock the write sdn: subnets: add dhcp-lease-time property sdn: dhcp: only assert a backend's availability for zones using it sdn: dhcp: add ebpf plugin sdn: zones: attach the dhcp responder on tap plug sdn: dhcp: apply mapping edits on the node serving the guest sdn: zones: offer dhcp on all zone types, keep dnsmasq simple-only tests: cover the ebpf dhcp backend and ipam API mapping pushes src/PVE/API2/Network/SDN/Ips.pm | 3 + src/PVE/API2/Network/SDN/Nodes/Status.pm | 42 +++- src/PVE/API2/Network/SDN/Zones.pm | 8 +- src/PVE/Network/SDN/Dhcp.pm | 87 +++++++- src/PVE/Network/SDN/Dhcp/Dnsmasq.pm | 3 +- src/PVE/Network/SDN/Dhcp/Ebpf.pm | 187 ++++++++++++++++++ src/PVE/Network/SDN/Dhcp/Makefile | 2 +- src/PVE/Network/SDN/Dhcp/Plugin.pm | 6 + src/PVE/Network/SDN/Ipams.pm | 24 ++- src/PVE/Network/SDN/SubnetPlugin.pm | 9 + src/PVE/Network/SDN/Zones.pm | 3 + src/PVE/Network/SDN/Zones/EvpnPlugin.pm | 1 + src/PVE/Network/SDN/Zones/FaucetPlugin.pm | 1 + src/PVE/Network/SDN/Zones/QinQPlugin.pm | 7 + src/PVE/Network/SDN/Zones/VlanPlugin.pm | 7 + src/PVE/Network/SDN/Zones/VxlanPlugin.pm | 9 + src/test/run_test_vnets_blackbox.pl | 231 ++++++++++++++++++++++ 17 files changed, 619 insertions(+), 11 deletions(-) create mode 100644 src/PVE/Network/SDN/Dhcp/Ebpf.pm pve-manager: Hannes Laimer (1): ui: sdn: dhcp backend selector on all zones, expose dhcp options www/manager6/sdn/SubnetEdit.js | 24 ++++++++++++++++++++++++ www/manager6/sdn/zones/Base.js | 17 +++++++++++++++++ www/manager6/sdn/zones/SimpleEdit.js | 11 ----------- 3 files changed, 41 insertions(+), 11 deletions(-) Summary over all repositories: 33 files changed, 2077 insertions(+), 23 deletions(-) -- Generated by murpp 0.12.0