Skip to content

Commit

Permalink
[nrf fromtree] net: dhcpv4: Accept unicast replies
Browse files Browse the repository at this point in the history
Some DHCPv4 servers do not respect BROADCAST flag set on DHCP Discover,
replying with unicast packet, making it impossible to obtain DHCP
address by Zephyr in such cases.

RFC1542 chapter 3.1.1 makes the following statement about the BROADCAST
flag:
    This addition to the protocol is a workaround for old host
    implementations.  Such implementations SHOULD be modified so
    that they may receive unicast BOOTREPLY messages, thus making
    use of this workaround unnecessary.  In general, the use of
    this mechanism is discouraged.

Making it clear that being able to process unicast replies from the DHCP
server is not only an optional behavior, but a recommended solution.

Therefore, introduce a support for unicast DHCPv4 in Zephyr. To achieve
this, add additional filtering rule at the IPv4 level - in case DHCPv4
is enabled, there is an active query and the packet is destined for the
DHCPv4 module, let it through for the DHCPv4 module to process,
regardless of the destination IP address.

Signed-off-by: Robert Lubos <[email protected]>
(cherry picked from commit a22f7e7)
  • Loading branch information
rlubos committed Jul 28, 2023
1 parent a2a683b commit f8818ca
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 5 deletions.
8 changes: 8 additions & 0 deletions subsys/net/ip/Kconfig.ipv4
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ config NET_DHCPV4_OPTION_CALLBACKS
can be added. These can be used to support otherwise
DHCP options not used by the rest of the system.

config NET_DHCPV4_ACCEPT_UNICAST
bool "Accept unicast DHCPv4 traffic"
depends on NET_DHCPV4
default y
help
If set, the network stack will accept unicast DHCPv4 responses from
servers, before the assigned address is configured on the interface.

config NET_IPV4_AUTO
bool "IPv4 autoconfiguration [EXPERIMENTAL]"
depends on NET_ARP
Expand Down
47 changes: 46 additions & 1 deletion subsys/net/ip/dhcpv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@ static struct net_pkt *dhcpv4_create_message(struct net_if *iface, uint8_t type,
msg->htype = HARDWARE_ETHERNET_TYPE;
msg->hlen = net_if_get_link_addr(iface)->len;
msg->xid = htonl(iface->config.dhcpv4.xid);
msg->flags = htons(DHCPV4_MSG_BROADCAST);
msg->flags = IS_ENABLED(CONFIG_NET_DHCPV4_ACCEPT_UNICAST) ?
htons(DHCPV4_MSG_UNICAST) : htons(DHCPV4_MSG_BROADCAST);

if (ciaddr) {
/* The ciaddr field was zero'd out above, if we are
Expand Down Expand Up @@ -1397,3 +1398,47 @@ int net_dhcpv4_init(void)
#endif
return 0;
}

#if defined(CONFIG_NET_DHCPV4_ACCEPT_UNICAST)
bool net_dhcpv4_accept_unicast(struct net_pkt *pkt)
{
NET_PKT_DATA_ACCESS_DEFINE(udp_access, struct net_udp_hdr);
struct net_pkt_cursor backup;
struct net_udp_hdr *udp_hdr;
struct net_if *iface;
bool accept = false;

iface = net_pkt_iface(pkt);
if (iface == NULL) {
return false;
}

/* Only accept DHCPv4 packets during active query. */
if (iface->config.dhcpv4.state != NET_DHCPV4_SELECTING &&
iface->config.dhcpv4.state != NET_DHCPV4_REQUESTING &&
iface->config.dhcpv4.state != NET_DHCPV4_RENEWING &&
iface->config.dhcpv4.state != NET_DHCPV4_REBINDING) {
return false;
}

net_pkt_cursor_backup(pkt, &backup);
net_pkt_skip(pkt, net_pkt_ip_hdr_len(pkt));

/* Verify destination UDP port. */
udp_hdr = (struct net_udp_hdr *)net_pkt_get_data(pkt, &udp_access);
if (udp_hdr == NULL) {
goto out;
}

if (udp_hdr->dst_port != htons(DHCPV4_CLIENT_PORT)) {
goto out;
}

accept = true;

out:
net_pkt_cursor_restore(pkt, &backup);

return accept;
}
#endif /* CONFIG_NET_DHCPV4_ACCEPT_UNICAST */
33 changes: 30 additions & 3 deletions subsys/net/ip/dhcpv4.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,7 @@ struct dhcp_msg {


/* TODO:
* 1) Support for UNICAST flag (some dhcpv4 servers will not reply if
* DISCOVER message contains BROADCAST FLAG).
* 2) Support T2(Rebind) timer.
* 1) Support T2(Rebind) timer.
*/

/* Maximum number of REQUEST or RENEWAL retransmits before reverting
Expand Down Expand Up @@ -115,4 +113,33 @@ int net_dhcpv4_init(void);

#endif /* CONFIG_NET_DHCPV4 */

#if defined(CONFIG_NET_DHCPV4) && defined(CONFIG_NET_DHCPV4_ACCEPT_UNICAST)

/**
* @brief Verify if the incoming packet should be accepted for the DHCPv4
* module to process.
*
* In case server responds with an unicast IP packet, the IP stack needs to
* pass it through for the DHCPv4 module to process, before the actual
* destination IP address is configured on an interface.
* This function allows to determine whether there is an active DHCPv4 query on
* the interface and the packet is destined for the DHCPv4 module to process.
*
* @param pkt A packet to analyze
*
* @return true if the packet shall be accepted, false otherwise
*/
bool net_dhcpv4_accept_unicast(struct net_pkt *pkt);

#else

static inline bool net_dhcpv4_accept_unicast(struct net_pkt *pkt)
{
ARG_UNUSED(pkt);

return false;
}

#endif /* CONFIG_NET_DHCPV4 && CONFIG_NET_DHCPV4_ACCEPT_UNICAST */

#endif /* __INTERNAL_DHCPV4_H */
4 changes: 3 additions & 1 deletion subsys/net/ip/ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ LOG_MODULE_REGISTER(net_ipv4, CONFIG_NET_IPV4_LOG_LEVEL);
#include "icmpv4.h"
#include "udp_internal.h"
#include "tcp_internal.h"
#include "dhcpv4.h"
#include "ipv4.h"

BUILD_ASSERT(sizeof(struct in_addr) == NET_IPV4_ADDR_SIZE);
Expand Down Expand Up @@ -318,7 +319,8 @@ enum net_verdict net_ipv4_input(struct net_pkt *pkt)
/* RFC 1122 ch. 3.3.6 The 0.0.0.0 is non-standard bcast addr */
(IS_ENABLED(CONFIG_NET_IPV4_ACCEPT_ZERO_BROADCAST) &&
net_ipv4_addr_cmp((struct in_addr *)hdr->dst,
net_ipv4_unspecified_address()))))) ||
net_ipv4_unspecified_address())) ||
net_dhcpv4_accept_unicast(pkt)))) ||
(hdr->proto == IPPROTO_TCP &&
net_ipv4_is_addr_bcast(net_pkt_iface(pkt), (struct in_addr *)hdr->dst))) {
NET_DBG("DROP: not for me");
Expand Down

0 comments on commit f8818ca

Please sign in to comment.