[PATCH 01/10] net: add net_eth_to_udp() helper for validated UDP extraction

Sascha Hauer
2026-04-02T06:37:25Z

The existing net_eth_to_udp_payload(), net_eth_to_udphdr() and
net_eth_to_udplen() helpers parse protocol headers by offset arithmetic
without checking the actual packet length. A short or malformed packet
can cause out-of-bounds reads.

Introduce net_eth_to_udp() which takes the frame pointer and the
NIC-reported length, validates that the packet is large enough to
contain ethernet + IP + UDP headers, cross-checks the UDP length field
against available bytes, and returns all three pieces of information
(udp header, payload pointer, payload length) callers typically need.

The old helpers are kept for now; callers will be converted in subsequent
commits.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
include/net.h | 9 +++++++++
net/net.c | 40 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 49 insertions(+)

diff --git a/include/net.h b/include/net.h
index 43d718353a..bdf39bc531 100644
--- a/include/net.h
+++ b/include/net.h
@@ -308,6 +308,15 @@ static inline int net_eth_to_udplen(char *pkt)
return ntohs(udp->uh_ulen) - 8;
}

+struct net_udp_pkt {
+ struct udphdr *udp;
+ void *payload;
+ unsigned int len;
+};
+
+int net_eth_to_udp(char *pkt, unsigned int framelen,
+ struct net_udp_pkt *udp_pkt);
+
int net_checksum_ok(unsigned char *, int); /* Return true if cksum OK */
uint16_t net_checksum(unsigned char *, int); /* Calculate the checksum */

diff --git a/net/net.c b/net/net.c
index fc32c4562b..67c0eeb2ae 100644
--- a/net/net.c
+++ b/net/net.c
@@ -60,6 +60,46 @@ const char *net_get_domainname(void)
return net_domainname;
}

+/**
+ * net_eth_to_udp - extract and validate UDP payload from an ethernet frame
+ * @pkt: pointer to start of ethernet frame
+ * @framelen: total frame length as reported by the NIC
+ * @udp_pkt: output struct, filled on success
+ *
+ * Validates that the frame is large enough to contain the ethernet, IP and
+ * UDP headers and clamps the reported UDP payload length to what is actually
+ * available in the frame.
+ *
+ * Return: 0 on success, negative error code on malformed/short packets.
+ */
+int net_eth_to_udp(char *pkt, unsigned int framelen,
+ struct net_udp_pkt *udp_pkt)
+{
+ unsigned int hdr_len = ETHER_HDR_SIZE + sizeof(struct iphdr) +
+ sizeof(struct udphdr);
+ struct udphdr *udp;
+ unsigned int payload_len;
+
+ if (framelen < hdr_len)
+ return -EINVAL;
+
+ udp = (struct udphdr *)((struct iphdr *)(pkt + ETHER_HDR_SIZE) + 1);
+
+ if (ntohs(udp->uh_ulen) < sizeof(struct udphdr))
+ return -EINVAL;
+
+ payload_len = ntohs(udp->uh_ulen) - sizeof(struct udphdr);
+
+ if (payload_len > framelen - hdr_len)
+ return -EINVAL;
+
+ udp_pkt->udp = udp;
+ udp_pkt->payload = (char *)(udp + 1);
+ udp_pkt->len = payload_len;
+
+ return 0;
+}
+
int net_checksum_ok(unsigned char *ptr, int len)
{
return net_checksum(ptr, len) == 0xffff;

--
2.47.3