[Dnsmasq-discuss] PATCH] PXE boot server (PXEBS) responses broken in 2.92 — missing else in dhcp.c

Simon Kelley simon at thekelleys.org.uk
Sat Feb 7 12:23:26 UTC 2026


Thanks for an exemplary bug report. I wish they were all that good!

Completely agree with the diagnosis and fix. Apologies for the 
brain-fart on my part which caused this in the first place.

Patch applied with full honours.

Cheers,

Simon.

On 04.02.2026 20:12, Clayton O'Neill wrote:
> Hi folks,
> 
> I think I've found a regression in dnsmasq 2.92 that breaks PXE boot server
> (PXEBS) responses when running in proxy DHCP mode. Fair warning: I'm not
> familiar with the dnsmasq codebase and used AI tooling to help trace through
> the source and identify the issue, so please take the analysis below with
> appropriate skepticism. PXE boot works fine on 2.91
> but fails on 2.92 — the client gets the initial proxy DHCPOFFER, but the 
> PXEBS
> ACK on port 4011 never reaches it.
> 
> My setup is dnsmasq in proxy DHCP mode serving iPXE to Proxmox VMs via their
> virtio-net ROM. Here's a stripped-down version of my config:
> 
>    port=0
>    enable-tftp
>    tftp-root=/tftpboot
>    dhcp-range=172.19.74.0,proxy,255.255.255.0
>    interface=eno1
>    bind-interfaces
>    dhcp-match=set:ipxe,175
>    pxe-service=tag:ipxe,x86PC,"Network Boot",http://server:8081/ 
> boot.ipxe <http://server:8081/boot.ipxe>
>    pxe-service=tag:!ipxe,x86PC,"Network Boot",undionly.kpxe
>    log-dhcp
> 
> The issue seems to be in src/dhcp.c in the response routing logic after
> dhcp_reply() returns. In 2.91, the destination selection was an if/else-if
> chain:
> 
>    if (pxe_fd)
>        { ... }
>    else if (mess->giaddr.s_addr && !is_relay_reply)
>        { ... }
>    else if (mess->ciaddr.s_addr)
>        { ... }
>    else
>        { ... broadcast to 255.255.255.255:68 
> <http://255.255.255.255:68> ... }
> 
> In 2.92, the else between the pxe_fd block and the giaddr/relay check was
> removed in commit 4fbe1ad ("Implement RFC-4388 DHCPv4 leasequery") to
> accommodate the new is_relay_use_source logic:
> 
>    if (pxe_fd)
>        { ... }
>    if ((is_relay_use_source || mess->giaddr.s_addr) && !is_relay_reply)
>        { ... }
>    else if (mess->ciaddr.s_addr)
>        { ... }
>    else
>        { ... broadcast to 255.255.255.255:68 
> <http://255.255.255.255:68> ... }
> 
> For PXEBS responses, dhcp_reply() in rfc2131.c (around line 924-925) does:
> 
>    mess->yiaddr = mess->ciaddr;
>    mess->ciaddr.s_addr = 0;
> 
> So after dhcp_reply() returns for a PXEBS request, ciaddr is 0, giaddr is 0
> (no relay), and is_relay_use_source is 0. In 2.91, the pxe_fd block runs and
> the rest of the chain is skipped — dest stays as received from recvmsg, 
> and the
> response goes back to the client correctly. In 2.92, the pxe_fd block 
> runs but
> then falls through to the standalone if, which is false, so the else 
> block runs
> and sets dest to 255.255.255.255 port 68. The client is listening on 
> port 4011
> and ignores it.
> 
> Here are the relevant dnsmasq logs. With 2.91 (working), I see normal proxy
> DHCP and PXE boot server exchanges:
> 
>    dnsmasq-dhcp: DHCPDISCOVER(eno1) bc:24:11:59:85:90
>    dnsmasq-dhcp: DHCPOFFER(eno1) 172.19.74.60 bc:24:11:59:85:90
>    dnsmasq-dhcp: DHCPREQUEST(eno1) 172.19.74.60 bc:24:11:59:85:90
>    dnsmasq-dhcp: DHCPACK(eno1) 172.19.74.60 bc:24:11:59:85:90
>    dnsmasq-dhcp: PXE(eno1) bc:24:11:59:85:90 proxy
>    dnsmasq-dhcp: PXE(eno1) bc:24:11:59:85:90 proxy
>    dnsmasq-dhcp: PXEBS(eno1) bc:24:11:59:85:90 undionly.kpxe
>    dnsmasq-dhcp: PXE(eno1) bc:24:11:59:85:90 proxy
>    dnsmasq-dhcp: PXEBS(eno1) bc:24:11:59:85:90 http:// 
> infra1.oneill.net:8081/boot.ipxe <http://infra1.oneill.net:8081/boot.ipxe>
> 
> With 2.92 (broken), the DHCPDISCOVER/OFFER/REQUEST/ACK cycle and the proxy
> PXE response work, but the PXEBS response never reaches the client — it 
> times
> out after repeated attempts. The dnsmasq side shows it sending the response,
> but the client keeps retrying:
> 
>    dnsmasq-dhcp: PXE(eno1) bc:24:11:59:85:90 proxy
>    dnsmasq-dhcp: PXE(eno1) bc:24:11:59:85:90 proxy
>    dnsmasq-dhcp: PXEBS(eno1) bc:24:11:59:85:90 undionly.kpxe
>    dnsmasq-dhcp: PXEBS(eno1) bc:24:11:59:85:90 undionly.kpxe
>    dnsmasq-dhcp: PXEBS(eno1) bc:24:11:59:85:90 undionly.kpxe
>    dnsmasq-dhcp: PXEBS(eno1) bc:24:11:59:85:90 undionly.kpxe
> 
> I tested by restoring the else keyword and the fix appears to work — 
> 2.92 with
> the patch below PXE boots successfully. I believe this change preserves the
> leasequery behavior since that path only applies when pxe_fd is false 
> (normal
> DHCP handling, not port 4011).
> 
> --- a/src/dhcp.c
> +++ b/src/dhcp.c
> @@ -399,7 +399,7 @@ void dhcp_packet(time_t now, int pxe_fd)
>         if (mess->ciaddr.s_addr != 0)
> dest.sin_addr = mess->ciaddr;
>       }
> -  if ((is_relay_use_source || mess->giaddr.s_addr) && !is_relay_reply)
> +  else if ((is_relay_use_source || mess->giaddr.s_addr) && !is_relay_reply)
>       {
>         /* Send to BOOTP relay. */
>         if (is_relay_use_source)
> 
> Thanks,
> Clayton
> 
> 
> _______________________________________________
> Dnsmasq-discuss mailing list
> Dnsmasq-discuss at lists.thekelleys.org.uk
> https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss




More information about the Dnsmasq-discuss mailing list