From b5b310f3c8f9fa103dcbf6831ad791cb3c4d9fb2 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 14 Jun 2026 10:34:45 +0200 Subject: [PATCH] webui: fix enabling DHCP/L3 on an interface with no prior IPv4/IPv6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SaveIPv4Settings/SaveIPv6Settings PATCHed the ietf-ip:ipv4 container to set forwarding, but a plain PATCH 400s ("Target resource does not exist") when the interface has no L3 config yet — exactly the first-time DHCP-enable case. PATCH the interface instead, with the family container nested in a single-element list entry (rousette rejects the bare-object form with LY_EVALID once an augmented container is nested), so the container is created on first use. Signed-off-by: Joachim Wiberg --- .../internal/handlers/configure_interfaces.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/webui/internal/handlers/configure_interfaces.go b/src/webui/internal/handlers/configure_interfaces.go index e9ceb900..b59fd1cb 100644 --- a/src/webui/internal/handlers/configure_interfaces.go +++ b/src/webui/internal/handlers/configure_interfaces.go @@ -2180,8 +2180,23 @@ func (h *ConfigureInterfacesHandler) saveIPSettings(w http.ResponseWriter, r *ht base := ifacePath(name) + "/" + container forwarding := r.FormValue("forwarding") == "true" - body := map[string]any{container: map[string]any{"forwarding": forwarding}} - if err := h.RC.Patch(r.Context(), base, body); err != nil { + // PATCH the interface (which always exists) with the family container + // nested inside, so the container is created on first use. PATCHing + // `base` (the ipv4/ipv6 container) directly 400s "Target resource does + // not exist" on a fresh interface that has no L3 config yet — which is + // exactly the case when enabling DHCP for the first time. The list + // entry must be a single-element array: rousette rejects the bare-object + // form with LY_EVALID once an augmented container (ietf-ip:ipv4) is + // nested inside. + body := map[string]any{ + "ietf-interfaces:interface": []any{ + map[string]any{ + "name": name, + container: map[string]any{"forwarding": forwarding}, + }, + }, + } + if err := h.RC.Patch(r.Context(), ifacePath(name), body); err != nil { log.Printf("configure interfaces %s %s settings: forwarding: %v", name, family, err) renderSaveError(w, err) return