diff --git a/subsys/net/ip/Kconfig.ipv4 b/subsys/net/ip/Kconfig.ipv4 index a16e025a948..a5e073051de 100644 --- a/subsys/net/ip/Kconfig.ipv4 +++ b/subsys/net/ip/Kconfig.ipv4 @@ -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 diff --git a/subsys/net/ip/dhcpv4.c b/subsys/net/ip/dhcpv4.c index be01851bd89..d5aefb3ba90 100644 --- a/subsys/net/ip/dhcpv4.c +++ b/subsys/net/ip/dhcpv4.c @@ -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 @@ -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 */ diff --git a/subsys/net/ip/dhcpv4.h b/subsys/net/ip/dhcpv4.h index 52663f7ee07..a274e3a6aea 100644 --- a/subsys/net/ip/dhcpv4.h +++ b/subsys/net/ip/dhcpv4.h @@ -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 @@ -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 */ diff --git a/subsys/net/ip/ipv4.c b/subsys/net/ip/ipv4.c index 83827303f6e..922068274f7 100644 --- a/subsys/net/ip/ipv4.c +++ b/subsys/net/ip/ipv4.c @@ -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); @@ -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");