From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id 02D051FF0AB for ; Wed, 21 Oct 2026 09:35:41 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 771C6214FE; Wed, 21 Oct 2026 09:35:38 +0200 (CEST) Message-ID: <379a9844-db93-493b-88e6-3a2ab4c6535d@proxmox.com> Date: Wed, 21 Oct 2026 09:35:31 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH container/docs/manager/network/proxmox{-ebpf,-perl-rs}/qemu-server v3 00/17] sdn: implement DHCP for all zones using eBPF To: Stefan Hanreich , pve-devel@lists.proxmox.com References: <20261020080005.470757-1-h.laimer@proxmox.com> <10d049a5-09ab-4b06-8efb-5de7629df91f@proxmox.com> From: Hannes Laimer Content-Language: en-US In-Reply-To: <10d049a5-09ab-4b06-8efb-5de7629df91f@proxmox.com> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1792568131595 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.232 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: Z6HTO6ZQJ4RP3TT3EMSCXPLBGOKBO5D4 X-Message-ID-Hash: Z6HTO6ZQJ4RP3TT3EMSCXPLBGOKBO5D4 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: On 2026-10-20 15:11, Stefan Hanreich wrote: > Did some testing: > > * PVE IPAM / All zones / eBPF backend > * PVE IPAM / Simple zone / dnsmasq backend > * Netbox IPAM / All zones / eBPF backend > * Netbox IPAM / Simple zone / dnsmasq backend > * switching from dnsmasq -> eBPF and vice-versa > -> pre-existing: dnsmasq doesn't clean its config folder > * changing bridge on network devices > * changing subnets on the vnet and then reacquiring a lease > > Everything worked really well, also the pre-existing dnsmasq zone. This > revision seems a lot nicer already without the generation counter, which > seemed a bit clunky. This is a more elegant solution imo. > > One thing I found: > > The new DHCP backend type needs to be added to proxmox-ve-rs as well, > otherwise the config fails to parse (firewall). We should add fallback > variants there... could have caught that :) thanks! > > for code-related stuff see the respective patches. Thanks for taking a look and the feedback! I'll prepare a v4 > > On 10/20/26 10:00 AM, Hannes Laimer wrote: >> 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 reaches the guest's node >> by a detached call to a new node endpoint, POST >> /nodes/{node}/sdn/dhcp-mapping. 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. A >> failed push is logged in the journal, and the responder on the guest's >> node catches up at the node's next apply or the guest's next start. An >> apply also 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 >> - 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 >> 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 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 | 1031 ++++++++++++++++++++++++++++++++ >> 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, 2422 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 (9): >> sdn: dhcp: replace a stale dnsmasq reservation instead of dropping it >> sdn: dhcp: update reservations when ip mappings change >> sdn: ipam: key the MAC cache by lower-case MAC, stop caching misses >> 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: push IP mapping edits to 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 | 10 + >> src/PVE/API2/Network/SDN/Nodes/Status.pm | 37 + >> src/PVE/API2/Network/SDN/Zones.pm | 11 +- >> src/PVE/Network/SDN/Dhcp.pm | 200 ++- >> src/PVE/Network/SDN/Dhcp/Dnsmasq.pm | 114 +- >> src/PVE/Network/SDN/Dhcp/Ebpf.pm | 308 +++++ >> src/PVE/Network/SDN/Dhcp/Makefile | 2 +- >> src/PVE/Network/SDN/Dhcp/Plugin.pm | 26 +- >> src/PVE/Network/SDN/Ipams.pm | 61 +- >> 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 | 1442 ++++++++++++++++++++- >> 20 files changed, 2263 insertions(+), 80 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 | 3 +++ >> 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, 44 insertions(+), 11 deletions(-) >> >> >> 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, 5099 insertions(+), 128 deletions(-) >> > > > > >