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

Reply via email to