[PATCH] net: sandbox: support raw Ethernet with lwIP
James Hilliard <[email protected]>
| Newsgroups | org.u-boot-project.lists.u-boot |
|---|---|
| Message-ID | <[email protected]> |
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; + 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"); + } 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 +65,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 +89,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 +101,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: 964ad5b5c91b7be56e443e899d7f873e6aa8c9fc change-id: 20260825-submit-lwip-sandbox-raw-v1-749315041b86 Best regards, -- James Hilliard <[email protected]>