all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Hannes Laimer <h.laimer@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH proxmox-perl-rs 03/12] pve-rs: sdn: add dhcp responder bindings
Date: Fri,  4 Sep 2026 11:38:26 +0200	[thread overview]
Message-ID: <20260904093835.1050030-4-h.laimer@proxmox.com> (raw)
In-Reply-To: <20260904093835.1050030-1-h.laimer@proxmox.com>

The consumer hands the responder subsystem of proxmox-ebpf its full
state on every trigger, the interfaces to serve and all records, and
the subsystem diffs that against the kernel state.

Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
 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

diff --git a/pve-rs/Cargo.toml b/pve-rs/Cargo.toml
index 5ae9082..0adf00b 100644
--- a/pve-rs/Cargo.toml
+++ b/pve-rs/Cargo.toml
@@ -34,6 +34,7 @@ proxmox-apt = { version = "1.0.1", features = ["cache"] }
 proxmox-apt-api-types = "3"
 proxmox-base64 = "1"
 proxmox-config-digest = "1"
+proxmox-ebpf = { version = "0.1", features = ["dhcp"] }
 proxmox-frr = { version = "0.5.1" }
 proxmox-http = { version = "1.0.2", features = ["client-sync", "client-trait"] }
 proxmox-http-error = "1"
@@ -70,6 +71,7 @@ proxmox-wireguard = { version = "0.1.2" }
 # proxmox-config-digest = { path = "../../proxmox/proxmox-config-digest" }
 # proxmox-daemon = { path = "../../proxmox/proxmox-daemon" }
 # proxmox-dns-api = { path = "../../proxmox/proxmox-dns-api" }
+# proxmox-ebpf = { path = "../../proxmox-ebpf" }
 # proxmox-http-error = { path = "../../proxmox/proxmox-http-error" }
 # proxmox-http = { path = "../../proxmox/proxmox-http" }
 # proxmox-human-byte = { path = "../../proxmox/proxmox-human-byte" }
diff --git a/pve-rs/Makefile b/pve-rs/Makefile
index bb1cd2d..7609a1e 100644
--- a/pve-rs/Makefile
+++ b/pve-rs/Makefile
@@ -32,6 +32,7 @@ PERLMOD_PACKAGES := \
 	  PVE::RS::OpenId \
 	  PVE::RS::ResourceScheduling::Static \
 	  PVE::RS::ResourceScheduling::Dynamic \
+	  PVE::RS::SDN::Dhcp \
 	  PVE::RS::SDN::Fabrics \
 	  PVE::RS::SDN::PrefixLists \
 	  PVE::RS::SDN::RouteMaps \
diff --git a/pve-rs/debian/control b/pve-rs/debian/control
index 70367dc..521d966 100644
--- a/pve-rs/debian/control
+++ b/pve-rs/debian/control
@@ -20,6 +20,8 @@ Build-Depends: debhelper-compat (= 13),
                librust-proxmox-apt-api-types-3+default-dev (>= 3.0.0-~~),
                librust-proxmox-base64-1+default-dev,
                librust-proxmox-config-digest-1+default-dev,
+               librust-proxmox-ebpf-0.1+default-dev,
+               librust-proxmox-ebpf-0.1+dhcp-dev,
                librust-proxmox-frr-0.5+default-dev (>= 0.5.1-~~),
                librust-proxmox-http-1+client-sync-dev (>= 1.0.2-~~),
                librust-proxmox-http-1+client-trait-dev (>= 1.0.2-~~),
diff --git a/pve-rs/src/bindings/sdn/dhcp.rs b/pve-rs/src/bindings/sdn/dhcp.rs
new file mode 100644
index 0000000..510e115
--- /dev/null
+++ b/pve-rs/src/bindings/sdn/dhcp.rs
@@ -0,0 +1,81 @@
+#[perlmod::package(name = "PVE::RS::SDN::Dhcp", lib = "pve_rs")]
+pub mod pve_rs_sdn_dhcp {
+    //! The `PVE::RS::SDN::Dhcp` package.
+    //!
+    //! Bindings for the per-tap eBPF DHCP responder. Records handed in here are
+    //! answered directly from the kernel.
+
+    use std::net::Ipv4Addr;
+
+    use anyhow::{Context, Error, bail};
+    use serde::Deserialize;
+
+    use proxmox_ebpf::dhcp::{DhcpSubsystem, Record};
+
+    /// One MAC's DHCP answer, the address and every option the reply carries.
+    #[derive(Deserialize)]
+    pub struct DhcpRecord {
+        mac: String,
+        ip: Ipv4Addr,
+        prefixlen: u8,
+        server_id: Ipv4Addr,
+        lease: u32,
+        router: Option<Ipv4Addr>,
+        dns: Option<Ipv4Addr>,
+        mtu: Option<u16>,
+    }
+
+    fn parse_mac(s: &str) -> Result<[u8; 6], Error> {
+        let mut addr = [0u8; 6];
+        let mut n = 0;
+        for part in s.split(':') {
+            if n >= 6 || part.len() != 2 {
+                bail!("invalid MAC {s:?}");
+            }
+            addr[n] = u8::from_str_radix(part, 16).with_context(|| format!("invalid MAC {s:?}"))?;
+            n += 1;
+        }
+        if n != 6 {
+            bail!("invalid MAC {s:?}");
+        }
+        Ok(addr)
+    }
+
+    fn to_records(records: Vec<DhcpRecord>) -> Result<Vec<Record>, Error> {
+        records
+            .into_iter()
+            .map(|r| {
+                Ok(Record {
+                    mac: parse_mac(&r.mac)?,
+                    ip: r.ip,
+                    prefixlen: r.prefixlen,
+                    server_id: r.server_id,
+                    lease: r.lease,
+                    router: r.router,
+                    dns: r.dns,
+                    mtu: r.mtu,
+                })
+            })
+            .collect()
+    }
+
+    /// The full pass. Make the responder programs current, attach them to exactly the given
+    /// interfaces and make the record map hold exactly the given records.
+    #[export]
+    pub fn apply(ifaces: Vec<String>, records: Vec<DhcpRecord>) -> Result<(), Error> {
+        let ifaces: Vec<&str> = ifaces.iter().map(String::as_str).collect();
+        DhcpSubsystem::new().apply(&ifaces, &to_records(records)?)
+    }
+
+    /// Attach the responder to a guest interface.
+    #[export]
+    pub fn attach(iface: &str) -> Result<(), Error> {
+        DhcpSubsystem::new().attach(iface)
+    }
+
+    /// Detach the responder everywhere and drop its pinned state.
+    #[export]
+    pub fn clear() -> Result<(), Error> {
+        DhcpSubsystem::new().clear()
+    }
+}
diff --git a/pve-rs/src/bindings/sdn/mod.rs b/pve-rs/src/bindings/sdn/mod.rs
index dcae046..4b99b8f 100644
--- a/pve-rs/src/bindings/sdn/mod.rs
+++ b/pve-rs/src/bindings/sdn/mod.rs
@@ -1,3 +1,4 @@
+pub(crate) mod dhcp;
 pub(crate) mod fabrics;
 pub(crate) mod prefix_lists;
 pub(crate) mod route_maps;
-- 
2.47.3





  parent reply	other threads:[~2026-09-04  9:39 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-04  9:38 [PATCH manager/network/proxmox{-ebpf,-perl-rs} 00/12] sdn: implement DHCP for all zones using eBPF Hannes Laimer
2026-09-04  9:38 ` [PATCH proxmox-ebpf 01/12] dhcp: add per-tap responder BPF program Hannes Laimer
2026-09-04  9:38 ` [PATCH proxmox-ebpf 02/12] dhcp: add responder subsystem Hannes Laimer
2026-09-04  9:38 ` Hannes Laimer [this message]
2026-09-04  9:38 ` [PATCH pve-network 04/12] sdn: ipam: do not cache negative per-MAC answers, lock the write Hannes Laimer
2026-09-04  9:38 ` [PATCH pve-network 05/12] sdn: subnets: add dhcp-lease-time property Hannes Laimer
2026-09-04  9:38 ` [PATCH pve-network 06/12] sdn: dhcp: only assert a backend's availability for zones using it Hannes Laimer
2026-09-04  9:38 ` [PATCH pve-network 07/12] sdn: dhcp: add ebpf plugin Hannes Laimer
2026-09-04  9:38 ` [PATCH pve-network 08/12] sdn: zones: attach the dhcp responder on tap plug Hannes Laimer
2026-09-04  9:38 ` [PATCH pve-network 09/12] sdn: dhcp: apply mapping edits on the node serving the guest Hannes Laimer
2026-09-04  9:38 ` [PATCH pve-network 10/12] sdn: zones: offer dhcp on all zone types, keep dnsmasq simple-only Hannes Laimer
2026-09-04  9:38 ` [PATCH pve-network 11/12] tests: cover the ebpf dhcp backend and ipam API mapping pushes Hannes Laimer
2026-09-04  9:38 ` [PATCH pve-manager 12/12] ui: sdn: dhcp backend selector on all zones, expose dhcp options Hannes Laimer
  -- strict thread matches above, loose matches on Subject: below --
2026-09-02 12:47 [RFC manager/network/proxmox{-ebpf,-perl-rs} 00/12] sdn: implement DHCP for all zones using eBPF Hannes Laimer
2026-09-02 12:47 ` [PATCH proxmox-perl-rs 03/12] pve-rs: sdn: add dhcp responder bindings Hannes Laimer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260904093835.1050030-4-h.laimer@proxmox.com \
    --to=h.laimer@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal