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 D13951FF0DD for ; Wed, 21 Oct 2026 10:45:12 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 253A9214DF; Wed, 21 Oct 2026 10:45:10 +0200 (CEST) From: Hannes Laimer To: pve-devel@lists.proxmox.com Subject: [PATCH container/docs/manager/network/proxmox{-ebpf,-perl-rs,-ve-rs}/qemu-server v4 00/20] sdn: implement DHCP for all zones using eBPF Date: Wed, 21 Oct 2026 10:44:25 +0200 Message-ID: <20261021084445.462195-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: 1792572301018 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.231 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 Message-ID-Hash: VIDJGB7TKUYBE4NNNOD4ZAK6MRT46YCR X-Message-ID-Hash: VIDJGB7TKUYBE4NNNOD4ZAK6MRT46YCR 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: This series adds a second DHCP backend, ebpf, next to dnsmasq. A zone picks its backend. dnsmasq stays limited to simple zones, while ebpf answers on the guest's own interface and works on every zone type. A guest on a zone with the ebpf backend gets the address its NIC has in the IPAM. There is no daemon per zone and no lease file. The answers are held in the kernel and do not depend on the IPAM being reachable. The responder is a BPF program on the ingress of each guest interface of a zone with the ebpf backend. It answers a request from the record of that interface, and the exchange never reaches the bridge. A request whose client MAC is not the interface's own passes on unanswered. So a guest cannot read another guest's record, and a nested client does not get the outer guest's address. An edit through /cluster/sdn/vnets/{vnet}/ips is proxied to the guest's node and updates the backend there. The guest sees the change at its next renewal, so the lease time bounds how long an old answer survives. Subnets get a dhcp-lease-time property for both backends. Without it dnsmasq hands out infinite leases and the responder ten minutes. An apply attaches the responder to running guests, so a zone switched to ebpf serves them without a restart. Changes reach the kernel in the order they read their input. So no change overwrites a newer one with older input. A hotplug moves the interface before the guest config names the new bridge. An apply or an edit that read the older config in between leaves the moved interface as its plug left it. A change that fails only warns, and what was served stays served. Nothing of the responder survives a reboot, so it is loaded again at boot before the guests start. Only DHCPv4 is answered. A request the guest tagged itself passes unanswered as well, so a trunk port keeps working as before. The request is answered on the guest interface, so the guest's firewall rules, its DHCP firewall option and its rate limit do not apply to it. The reply leaves through the interface's egress, where the rate limit shapes it. A request the responder does not answer passes on to the bridge and through the firewall as before. Mappings changed directly in an external IPAM are not picked up, as with dnsmasq. In patch order, the series adds - proxmox-ebpf, the dhcp subsystem, a tc program answering on the guest interface and the per-interface map it answers from - pve-rs, the bindings - pve-network, the ebpf backend, the hooks that keep a backend current when a guest interface or a mapping changes, dhcp on every zone type, and fixes to the dnsmasq reservations and the MAC cache - qemu-server and pve-container, the reports of a guest NIC coming, going or moving, and for VMs the push at a migration's resume - pve-manager, the backend and subnet options in the UI, and the backends brought up at boot ahead of the guests - proxmox-ve-rs, the ebpf variant of the dhcp backend type - pve-docs, documentation - proxmox-ebpf applies on top of the v4 of its own series [1] - pve-rs needs proxmox-ebpf with the dhcp feature - pve-network needs pve-rs, at build and run time - qemu-server, pve-container and pve-manager need pve-network - proxmox-ve-config needs nothing, and proxmox-firewall needs its dependency on it bumped, since an older one fails to parse a zone with the ebpf backend and the firewall then cannot load its config An older pve-network fails its apply on the unknown backend, so every node needs the new one before a zone gets ebpf. Changes since v3, thanks @Stefan!: * a mapping edit runs on the guest's node instead of being pushed to it, the node endpoint is gone * the MAC cache is read and written with lower-case keys, and its three fixes are three commits * the guest NIC list is keyed by MAC * add the missing proxmox-ve-rs patch, the ebpf variant of the dhcp backend type * reword log lines and warnings * (notes on patches with changes) Changes since v2: * drop the generation counter. Every change takes the responder's lock before it reads its input instead. An edit takes it exclusively * the resume at the end of a live migration pushes the guest's mappings again on the target, so an edit made during the migration reaches it * new first pve-network commit, a dnsmasq fix that replaces a stale reservation of another MAC instead of dropping it * no MTU option for a zone without an MTU, where v2 handed out 1500 * a mapping edit without a MAC no longer reaches the backends * rewrite commit messages, cover letter and parts of the docs * rebase onto current master (pre-build packages on sani) [1] https://lore.proxmox.com/pve-devel/20261019142413.2255644-1-h.laimer@proxmox.com/ proxmox-ebpf: Hannes Laimer (2): dhcp: add the per-tap DHCPv4 responder program dhcp: add the subsystem that loads the responder and keeps its records Cargo.toml | 5 + debian/control | 6 +- src/bpf-shim/bpf/bpf_helpers.h | 3 + src/bpf-shim/vmlinux.h | 1 + src/dhcp/bpf/dhcp.bpf.c | 401 ++++++++++++ src/dhcp/bpf/types.h | 24 + src/dhcp/mod.rs | 1037 ++++++++++++++++++++++++++++++++ src/dhcp/types.rs | 64 ++ src/lib.rs | 3 + src/subsystem.rs | 2 +- tests/common/mod.rs | 63 +- tests/dhcp.rs | 825 +++++++++++++++++++++++++ 12 files changed, 2428 insertions(+), 6 deletions(-) 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 bindings for the eBPF DHCP responder pve-rs/Cargo.toml | 2 + pve-rs/Makefile | 3 + pve-rs/debian/control | 2 + pve-rs/src/bindings/sdn/dhcp.rs | 229 ++++++++++++++++++++++++++++++++ pve-rs/src/bindings/sdn/mod.rs | 1 + 5 files changed, 237 insertions(+) create mode 100644 pve-rs/src/bindings/sdn/dhcp.rs pve-network: Hannes Laimer (11): sdn: ipam: stop caching IPAM misses sdn: ipam: write the lookup's cache entry under the cluster lock sdn: ipam: key the MAC cache by lower-case MAC sdn: dhcp: replace a stale dnsmasq reservation instead of dropping it sdn: dhcp: update reservations when ip mappings change sdn: dhcp: make the lease time configurable per subnet sdn: dhcp: require a backend only if a zone on this node uses it sdn: dhcp: add an eBPF backend answering on each guest interface sdn: zones: report interface plugs and unplugs to the DHCP backends sdn: dhcp: run IP mapping edits on the guest's node sdn: zones: allow the ebpf DHCP backend on every zone type debian/libpve-network-perl.prerm | 13 + src/PVE/API2/Network/SDN/Ips.pm | 23 + src/PVE/API2/Network/SDN/Zones.pm | 11 +- src/PVE/Network/SDN/Dhcp.pm | 139 +- src/PVE/Network/SDN/Dhcp/Dnsmasq.pm | 106 +- src/PVE/Network/SDN/Dhcp/Ebpf.pm | 295 +++++ src/PVE/Network/SDN/Dhcp/Makefile | 2 +- src/PVE/Network/SDN/Dhcp/Plugin.pm | 26 +- src/PVE/Network/SDN/Ipams.pm | 50 +- src/PVE/Network/SDN/SubnetPlugin.pm | 11 + src/PVE/Network/SDN/Vnets.pm | 5 +- src/PVE/Network/SDN/Zones.pm | 29 +- src/PVE/Network/SDN/Zones/EvpnPlugin.pm | 32 + src/PVE/Network/SDN/Zones/FaucetPlugin.pm | 1 + src/PVE/Network/SDN/Zones/QinQPlugin.pm | 7 + src/PVE/Network/SDN/Zones/SimplePlugin.pm | 2 +- src/PVE/Network/SDN/Zones/VlanPlugin.pm | 7 + src/PVE/Network/SDN/Zones/VxlanPlugin.pm | 25 + src/test/run_test_vnets_blackbox.pl | 1464 ++++++++++++++++++++- 19 files changed, 2169 insertions(+), 79 deletions(-) create mode 100755 debian/libpve-network-perl.prerm create mode 100644 src/PVE/Network/SDN/Dhcp/Ebpf.pm qemu-server: Hannes Laimer (1): network: sdn: keep the DHCP backends current on guest NICs src/PVE/CLI/qm.pm | 4 ++-- src/PVE/QemuServer.pm | 4 ++++ src/PVE/QemuServer/Network.pm | 19 +++++++++++++++++-- src/PVE/QemuServer/RunState.pm | 2 ++ src/usr/pve-bridge | 8 +++++++- src/usr/pve-bridgedown | 4 ++-- 6 files changed, 34 insertions(+), 7 deletions(-) pve-container: Hannes Laimer (1): net: report NIC plugs and removals to the SDN DHCP backends src/PVE/LXC.pm | 13 +++++++++++-- src/PVE/LXC/Config.pm | 3 ++- src/lxc-pve-poststop-hook | 6 +++--- 3 files changed, 16 insertions(+), 6 deletions(-) pve-manager: Hannes Laimer (2): ui: sdn: add zone DHCP backend selector and subnet DHCP options sdn: bring up the DHCP backends at boot before the guests start bin/pve-sdn-commit | 4 ++++ services/pve-sdn-commit.service | 1 + www/manager6/sdn/SubnetEdit.js | 23 +++++++++++++++++++++++ www/manager6/sdn/zones/Base.js | 17 +++++++++++++++++ www/manager6/sdn/zones/SimpleEdit.js | 11 ----------- 5 files changed, 45 insertions(+), 11 deletions(-) proxmox-ve-rs: Hannes Laimer (1): ve-config: sdn: add the ebpf dhcp backend proxmox-ve-config/src/sdn/config.rs | 3 +++ 1 file changed, 3 insertions(+) pve-docs: Hannes Laimer (1): sdn: dhcp: add eBPF backend section pvesdn.adoc | 101 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 83 insertions(+), 18 deletions(-) Summary over all repositories: 52 files changed, 5015 insertions(+), 127 deletions(-) -- Generated by murpp 0.12.0