Hi James,
On 25/08/2026 22:14, James Hilliard wrote:
> ETH_SANDBOX_RAW is restricted to NET_LEGACY despite operating through
> the common Ethernet driver API.
>
> Make the localhost shim stack-neutral: define the ARP layout locally,
> populate lwIP's per-device ipaddr environment variable, and retain the
> legacy address state when that stack is selected.
>
> Linux loopback can expose UDP checksum-offload seeds through raw sockets
> without the metadata needed to validate them. Normalize these to the valid
> IPv4 no-checksum form. Only update the dummy UDP bind for UDP output so
> lwIP-generated ICMP packets cannot disturb it.
>
> This enables both real-interface and localhost raw Ethernet with lwIP.
>
> Signed-off-by: James Hilliard <[email protected]>
> ---
> arch/sandbox/cpu/eth-raw-os.c | 83
> +++++++++++++++++++++++++++++--------------
> drivers/net/Kconfig | 2 +-
> drivers/net/sandbox-raw.c | 49 +++++++++++++++++--------
> include/net-legacy.h | 6 ++++
> include/net-lwip.h | 5 +++
> 5 files changed, 103 insertions(+), 42 deletions(-)
>
> diff --git a/arch/sandbox/cpu/eth-raw-os.c b/arch/sandbox/cpu/eth-raw-os.c
> index 39ea3b3f012..c9c3a561a76 100644
> --- a/arch/sandbox/cpu/eth-raw-os.c
> +++ b/arch/sandbox/cpu/eth-raw-os.c
> @@ -208,7 +208,6 @@ int sandbox_eth_raw_os_send(void *packet, int length,
> struct eth_sandbox_raw_priv *priv)
> {
> int retval;
> - struct udphdr *udph = packet + sizeof(struct iphdr);
>
> if (priv->sd < 0 || !priv->device)
> return -EINVAL;
> @@ -226,38 +225,50 @@ int sandbox_eth_raw_os_send(void *packet, int length,
> * stack from sending that ICMP message claiming that the port has no
> * bound socket.
> */
> - if (priv->local && (priv->local_bind_sd == -1 ||
> - priv->local_bind_udp_port != udph->source)) {
> + if (priv->local && length >= sizeof(struct iphdr)) {
> struct iphdr *iph = packet;
> - struct sockaddr_in addr;
> + unsigned int iphdr_len = iph->ihl * 4;
> + struct udphdr *udph;
>
> - if (priv->local_bind_sd != -1)
> - os_close(priv->local_bind_sd);
> + if (iph->protocol != IPPROTO_UDP ||
> + iphdr_len < sizeof(*iph) ||
> + length < iphdr_len + sizeof(*udph))
> + goto send;
>
> - /* A normal UDP socket is required to bind */
> - priv->local_bind_sd = socket(AF_INET, SOCK_DGRAM, 0);
> - if (priv->local_bind_sd < 0) {
> - printf("Failed to open bind sd: %d %s\n", errno,
> - strerror(errno));
> - return -errno;
> + udph = packet + iphdr_len;
This would not work if the packet is fragmented, which can happen since
U-Boot’s lwIP configuration has IP_FRAG enabled in lib/lwip/u-boot/lwipopts.h.
So I believe the above test should be:
if (iph->protocol != IPPROTO_UDP ||
(ntohs(iph->frag_off) & IP_OFFMASK) ||
iphdr_len < sizeof(*iph) ||
length < iphdr_len + sizeof(*udph))
goto send;
> + if (priv->local_bind_sd == -1 ||
> + priv->local_bind_udp_port != udph->source) {
> + struct sockaddr_in addr = {};
> +
> + if (priv->local_bind_sd != -1)
> + os_close(priv->local_bind_sd);
> +
> + /* A normal UDP socket is required to bind */
> + priv->local_bind_sd = socket(AF_INET, SOCK_DGRAM, 0);
> + if (priv->local_bind_sd < 0) {
> + printf("Failed to open bind sd: %d %s\n", errno,
> + strerror(errno));
> + return -errno;
> + }
> + priv->local_bind_udp_port = udph->source;
> +
> + /**
> + * Bind the UDP port that we intend to use as our
> source port
> + * so that the kernel will not send an ICMP port
> unreachable
> + * message to the server
> + */
> + addr.sin_family = AF_INET;
> + addr.sin_port = udph->source;
> + addr.sin_addr.s_addr = iph->saddr;
> + retval = bind(priv->local_bind_sd,
> + (struct sockaddr *)&addr, sizeof(addr));
> + if (retval < 0)
> + printf("Failed to bind: %d %s\n", errno,
> + strerror(errno));
> }
> - priv->local_bind_udp_port = udph->source;
> -
> - /**
> - * Bind the UDP port that we intend to use as our source port
> - * so that the kernel will not send an ICMP port unreachable
> - * message to the server
> - */
> - addr.sin_family = AF_INET;
> - addr.sin_port = udph->source;
> - addr.sin_addr.s_addr = iph->saddr;
> - retval = bind(priv->local_bind_sd, (struct sockaddr *)&addr,
> - sizeof(addr));
> - if (retval < 0)
> - printf("Failed to bind: %d %s\n", errno,
> - strerror(errno));
> }
>
> +send:
> retval = sendto(priv->sd, packet, length, 0,
> (struct sockaddr *)priv->device,
> sizeof(struct sockaddr_ll));
> @@ -283,6 +294,24 @@ int sandbox_eth_raw_os_recv(void *packet, int *length,
> (socklen_t *)&saddr_size);
> *length = 0;
> if (retval >= 0) {
> + if (priv->local && retval >= sizeof(struct iphdr)) {
> + struct iphdr *iph = packet;
> + unsigned int iphdr_len = iph->ihl * 4;
> +
> + if (iph->protocol == IPPROTO_UDP &&
> + iphdr_len >= sizeof(*iph) &&
> + retval >= iphdr_len + sizeof(struct udphdr)) {
> + struct udphdr *udph = packet + iphdr_len;
> +
> + /*
> + * Loopback packets can retain a
> checksum-offload seed
> + * which a raw socket cannot validate without
> skb metadata.
> + * An IPv4 UDP checksum of zero explicitly
> disables the
> + * checksum, so present that portable form to
> U-Boot.
> + */
> + udph->check = 0;
> + }
> + }
> *length = retval;
> return 0;
> }
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index faa08e9ebba..c057504cf66 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -366,7 +366,7 @@ config ETH_SANDBOX
>
> config ETH_SANDBOX_RAW
> depends on SANDBOX
> - depends on NET_LEGACY
> + depends on NET
> default y
> bool "Sandbox: Bridge to Linux Raw Sockets"
> help
> diff --git a/drivers/net/sandbox-raw.c b/drivers/net/sandbox-raw.c
> index c3d40f0b59e..c57893c48ce 100644
> --- a/drivers/net/sandbox-raw.c
> +++ b/drivers/net/sandbox-raw.c
> @@ -13,6 +13,21 @@
> #include <malloc.h>
> #include <net.h>
>
> +#define ARP_PROTOCOL_ADDR_LEN 4
> +#define ARP_OP_REPLY 2
> +
> +struct sb_arp_hdr {
> + u16 hrd;
> + u16 pro;
> + u8 hln;
> + u8 pln;
> + u16 op;
> + u8 sha[ARP_HLEN];
> + struct in_addr spa;
> + u8 tha[ARP_HLEN];
> + struct in_addr tpa;
> +} __packed;
> +
> static int reply_arp;
> static struct in_addr arp_ip;
>
> @@ -20,6 +35,8 @@ static int sb_eth_raw_start(struct udevice *dev)
> {
> struct eth_sandbox_raw_priv *priv = dev_get_priv(dev);
> struct eth_pdata *pdata = dev_get_plat(dev);
> + char ipaddr[sizeof("ipaddr99")];
> + int idx = dev_seq(dev);
> int ret;
>
> debug("eth_sandbox_raw: Start\n");
> @@ -27,9 +44,13 @@ static int sb_eth_raw_start(struct udevice *dev)
> ret = sandbox_eth_raw_os_start(priv, pdata->enetaddr);
> if (priv->local) {
> env_set("ipaddr", "127.0.0.1");
> + if (IS_ENABLED(CONFIG_NET_LWIP) && idx > 0 && idx <= 99) {
> + snprintf(ipaddr, sizeof(ipaddr), "ipaddr%d", idx);
> + env_set(ipaddr, "127.0.0.1");
> + }
Not sure it matters, but when idx > 0 we should not overwrite "ipaddr" I
think.
[...]
Thanks,
--
Jerome