Skip to content

netutils/dhcpc: Send the REQUEST before using the offered address. - #3806

Open
ErikkEnglund wants to merge 2 commits into
apache:masterfrom
ErikkEnglund:dhcpc-request-source-address
Open

ErikkEnglund wants to merge 2 commits into
apache:masterfrom
ErikkEnglund:dhcpc-request-source-address

Conversation

@ErikkEnglund

Copy link
Copy Markdown

Note: Please adhere to Contributing Guidelines.

Summary

dhcpc_request() assigned the offered address to the interface as soon as the OFFER arrived, so that it could receive a unicast ACK. As a side effect the REQUEST went out with the offered address as IP source. RFC 2131 section 4.1 says:

DHCP messages broadcast by a client prior to that client obtaining its IP address must have the source address field in the IP header set to 0.

and the client only obtains the address with the DHCPACK (section 3.1, step 5).

Most servers accept the REQUEST anyway, but some routers treat such a client as one with a statically configured address. A TP-Link Deco mesh router, for example, lists the device as offline and does not offer address reservation for it, even though it serves the lease.

This PR:

  • sends each REQUEST from the address the interface had before (0.0.0.0 on first configuration) and only then sets the offered address while waiting for the ACK, so a unicast ACK is still received;
  • restores the old address when no ACK arrives, instead of leaving the unconfirmed offered address on the interface;
  • fixes the existing nxstyle issues in dhcpc.c in a separate commit (no functional change).

Setting CONFIG_NETUTILS_DHCPC_BOOTP_FLAGS=0x8000 and not using the offered address at all would also avoid the wrong source address, but then the OFFER and ACK are broadcast, and broadcast frames on Wi-Fi are not retransmitted. In testing that made DHCP noticeably less reliable (see below), so this change keeps the unicast ACK.

Impact

  • Users of dhcpc_request() (netinit, netlib_obtain_ipv4addr(), the renew command): the REQUEST is sent from 0.0.0.0 on first configuration, or from the current address when renewing. No API or Kconfig changes.
  • When no ACK arrives, the interface goes back to its previous address instead of keeping the offered one.
  • The fix relies on sendto() returning only after the REQUEST has been sent, which holds without CONFIG_NET_UDP_WRITE_BUFFERS. With UDP write buffers the REQUEST may still go out from the offered address, i.e. the same behaviour as before this change.
  • Build, hardware, documentation, security: none.

Testing

Host: Fedora 44 (Linux 7.2.7, x86_64), xPack riscv-none-elf-gcc 13.2.0-2.
Target: ESP32-C3-DevKitC-02 (ESP32-C3 rev v0.3), esp32c3-devkit:wifi with netinit DHCP (CONFIG_NETINIT_DHCPC=y, CONFIG_NETINIT_THREAD=y, CONFIG_NET_UDP_WRITE_BUFFERS not set), Wi-Fi station on a TP-Link Deco mesh (3 units) that runs the DHCP server.
Base: nuttx c046c337c1, nuttx-apps 00b6e59.

DHCP was captured with tcpdump on another host on the same LAN.

Before, at boot: the REQUEST is sent from the offered address. The Deco app lists the device as offline and offers no address reservation.

192.168.10.60.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from 7c:df:a1:ba:46:dc, xid 0xbc1aa1a8, Flags [none] (0x0000)
  DHCP-Message (53): Request
  Server-ID (54): 192.168.10.1
  Requested-IP (50): 192.168.10.60

After, at boot: DISCOVER and REQUEST are sent from 0.0.0.0, the unicast ACK is received and the address configured. The Deco app lists the device as online, and an address reservation could be created for it.

0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from 7c:df:a1:ba:46:dc, xid 0xed3192cb, Flags [none] (0x0000)
  DHCP-Message (53): Discover
0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from 7c:df:a1:ba:46:dc, xid 0xed3192cb, Flags [none] (0x0000)
  DHCP-Message (53): Request
  Server-ID (54): 192.168.10.1
  Requested-IP (50): 192.168.10.60

Reliability with this change:

  • renew wlan0 10 times in a row: 10/10 succeeded, about 0.1 s each.
  • 3 hard resets: an address was configured each time, already at the first check 3 s after reset.
  • An application calling dhcpc_request() to renew the lease while the interface has its address (REQUEST from the current address) renewed successfully at the T1 time the server sent (3600 s for a 7200 s lease).

For comparison, the broadcast-flag-only variant described in the summary: renew wlan0 succeeded 8/10 times, and a boot-time attempt failed with the board never seeing the broadcast OFFER.

Not exercised on hardware: the new path that restores the old address when no ACK arrives at all.

tools/nxstyle reports no issues for netutils/dhcpc/dhcpc.c after the second commit (six pre-existing issues before it).

dhcpc_request() assigned the offered address to the interface as soon
as the OFFER arrived, so that it could receive a unicast ACK.  The
REQUEST then went out with the offered address as IP source, while
RFC 2131 section 4.1 requires 0.0.0.0 until the server has assigned
the address.  Some routers treat such a client as one with a static
address; TP-Link Deco mesh routers, for example, list it as offline
and do not offer address reservation for it.

Send each REQUEST from the address the interface had before and only
then use the offered address while waiting for the ACK, so unicast ACKs
are still received.  Also restore the old address when no ACK arrives
instead of leaving the unconfirmed offered address in place.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Erik Englund <erik.englund@gmail.com>
Comment indentation in dhcpc_parseoptions() and missing blank lines after
declarations.  No functional change.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Erik Englund <erik.englund@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants