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]>
---
Changes v1 -> v2:
  - skip non-initial IPv4 fragments when parsing UDP headers  (suggested by 
Jerome Forissier)
  - avoid overwriting ipaddr for indexed lwIP devices  (suggested by Jerome 
Forissier)
  - Link to v1: 
https://patch.msgid.link/[email protected]
---
 arch/sandbox/cpu/eth-raw-os.c | 85 +++++++++++++++++++++++++++++--------------
 drivers/net/Kconfig           |  2 +-
 drivers/net/sandbox-raw.c     | 52 ++++++++++++++++++--------
 include/net-legacy.h          |  6 +++
 include/net-lwip.h            |  5 +++
 5 files changed, 107 insertions(+), 43 deletions(-)

diff --git a/arch/sandbox/cpu/eth-raw-os.c b/arch/sandbox/cpu/eth-raw-os.c
index 39ea3b3f012..42f291a5519 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,51 @@ 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 ||
+                   (ntohs(iph->frag_off) & IP_OFFMASK) ||
+                   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;
+               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 +295,25 @@ 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 &&
+                           !(ntohs(iph->frag_off) & IP_OFFMASK) &&
+                           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..f62758e6d44 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,16 +35,23 @@ 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");
 
        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) {
+                       env_set("ipaddr", "127.0.0.1");
+               } else if (idx > 0 && idx <= 99) {
+                       snprintf(ipaddr, sizeof(ipaddr), "ipaddr%d", idx);
+                       env_set(ipaddr, "127.0.0.1");
+               }
                env_set("serverip", "127.0.0.1");
                net_ip = string_to_ip("127.0.0.1");
-               net_server_ip = net_ip;
+               net_set_server_ip(net_ip);
        }
        return ret;
 }
@@ -44,13 +66,13 @@ static int sb_eth_raw_send(struct udevice *dev, void 
*packet, int length)
                struct ethernet_hdr *eth = packet;
 
                if (ntohs(eth->et_protlen) == PROT_ARP) {
-                       struct arp_hdr *arp = packet + ETHER_HDR_SIZE;
+                       struct sb_arp_hdr *arp = packet + ETHER_HDR_SIZE;
 
                        /**
                         * localhost works on a higher-level API in Linux than
                         * ARP packets, so fake it
                         */
-                       arp_ip = net_read_ip(&arp->ar_tpa);
+                       memcpy(&arp_ip, &arp->tpa, sizeof(arp_ip));
                        reply_arp = 1;
                        return 0;
                }
@@ -68,7 +90,7 @@ static int sb_eth_raw_recv(struct udevice *dev, int flags, 
uchar **packetp)
        int length;
 
        if (reply_arp) {
-               struct arp_hdr *arp = (void *)net_rx_packets[0] +
+               struct sb_arp_hdr *arp = (void *)net_rx_packets[0] +
                        ETHER_HDR_SIZE;
 
                /*
@@ -80,18 +102,18 @@ static int sb_eth_raw_recv(struct udevice *dev, int flags, 
uchar **packetp)
                 * to get a response. For this reason we fake the response to
                 * make the u-boot network stack happy.
                 */
-               arp->ar_hrd = htons(ARP_ETHER);
-               arp->ar_pro = htons(PROT_IP);
-               arp->ar_hln = ARP_HLEN;
-               arp->ar_pln = ARP_PLEN;
-               arp->ar_op = htons(ARPOP_REPLY);
+               arp->hrd = htons(ARP_ETHER);
+               arp->pro = htons(PROT_IP);
+               arp->hln = ARP_HLEN;
+               arp->pln = ARP_PROTOCOL_ADDR_LEN;
+               arp->op = htons(ARP_OP_REPLY);
                /* Any non-zero MAC address will work */
-               memset(&arp->ar_sha, 0x01, ARP_HLEN);
+               memset(arp->sha, 0x01, ARP_HLEN);
                /* Use whatever IP we were looking for (always 127.0.0.1?) */
-               net_write_ip(&arp->ar_spa, arp_ip);
-               memcpy(&arp->ar_tha, pdata->enetaddr, ARP_HLEN);
-               net_write_ip(&arp->ar_tpa, net_ip);
-               length = ARP_HDR_SIZE;
+               memcpy(&arp->spa, &arp_ip, sizeof(arp->spa));
+               memcpy(arp->tha, pdata->enetaddr, ARP_HLEN);
+               memcpy(&arp->tpa, &net_ip, sizeof(arp->tpa));
+               length = sizeof(*arp);
        } else {
                /* If local, the Ethernet header won't be included; skip it */
                uchar *pktptr = priv->local ?
diff --git a/include/net-legacy.h b/include/net-legacy.h
index d3b122c9062..2737b683cb1 100644
--- a/include/net-legacy.h
+++ b/include/net-legacy.h
@@ -288,6 +288,12 @@ extern char        
net_root_path[CONFIG_BOOTP_MAX_ROOT_PATH_LEN];  /* Our root path */
 extern u8              net_ethaddr[ARP_HLEN];          /* Our ethernet address 
*/
 extern u8              net_server_ethaddr[ARP_HLEN];   /* Boot server enet 
address */
 extern struct in_addr  net_server_ip;  /* Server IP addr (0 = unknown) */
+
+static inline void net_set_server_ip(struct in_addr ip)
+{
+       net_server_ip = ip;
+}
+
 extern uchar           *net_tx_packet;         /* THE transmit packet */
 extern uchar           *net_rx_packet;         /* Current receive packet */
 extern int             net_rx_packet_len;      /* Current rx packet length */
diff --git a/include/net-lwip.h b/include/net-lwip.h
index 8e59a2299e0..9b7755e335e 100644
--- a/include/net-lwip.h
+++ b/include/net-lwip.h
@@ -26,6 +26,11 @@ enum proto_t {
        TFTPGET
 };
 
+static inline void net_set_server_ip(struct in_addr ip)
+{
+       /* lwIP reads serverip from the environment when starting a command. */
+}
+
 static inline int eth_is_on_demand_init(void)
 {
        return 1;

---
base-commit: cc557af4553382f6f50e3ed62b9577054e7bc54f
change-id: 20260825-submit-lwip-sandbox-raw-v1-749315041b86

Best regards,
--  
James Hilliard <[email protected]>

Reply via email to