diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | arch/arm/mach-aspeed/ast2600/u-boot-spl.lds | 2 | ||||
| -rw-r--r-- | cmd/lwip/ping.c | 19 | ||||
| -rw-r--r-- | cmd/lwip/sntp.c | 10 | ||||
| -rw-r--r-- | doc/develop/release_cycle.rst | 2 | ||||
| -rw-r--r-- | drivers/net/airoha_eth.c | 78 | ||||
| -rw-r--r-- | include/net-lwip.h | 1 | ||||
| -rw-r--r-- | net/cdp.c | 8 | ||||
| -rw-r--r-- | net/lwip/dhcp.c | 15 | ||||
| -rw-r--r-- | net/lwip/dns.c | 7 | ||||
| -rw-r--r-- | net/lwip/net-lwip.c | 16 | ||||
| -rw-r--r-- | net/lwip/nfs.c | 4 | ||||
| -rw-r--r-- | net/lwip/tftp.c | 4 | ||||
| -rw-r--r-- | net/lwip/wget.c | 107 | ||||
| -rw-r--r-- | net/net.c | 9 | ||||
| -rw-r--r-- | test/dm/Makefile | 1 | ||||
| -rw-r--r-- | test/dm/net_defrag.c | 82 |
17 files changed, 272 insertions, 95 deletions
@@ -3,7 +3,7 @@ VERSION = 2026 PATCHLEVEL = 07 SUBLEVEL = -EXTRAVERSION = -rc4 +EXTRAVERSION = -rc5 NAME = # *DOCUMENTATION* diff --git a/arch/arm/mach-aspeed/ast2600/u-boot-spl.lds b/arch/arm/mach-aspeed/ast2600/u-boot-spl.lds index 303ace2f61c..894eda1db77 100644 --- a/arch/arm/mach-aspeed/ast2600/u-boot-spl.lds +++ b/arch/arm/mach-aspeed/ast2600/u-boot-spl.lds @@ -42,6 +42,7 @@ SECTIONS . = ALIGN(4); __u_boot_list : { KEEP(*(SORT(__u_boot_list*))); + . = ALIGN(8); } > .nor . = ALIGN(4); @@ -49,7 +50,6 @@ SECTIONS __binman_sym_start = .; KEEP(*(SORT(.binman_sym*))); __binman_sym_end = .; - . = ALIGN(8); } > .nor /* diff --git a/cmd/lwip/ping.c b/cmd/lwip/ping.c index fc4cf7bde5f..98fa8e22bce 100644 --- a/cmd/lwip/ping.c +++ b/cmd/lwip/ping.c @@ -163,6 +163,7 @@ static int ping_loop(struct udevice *udev, const ip_addr_t *addr) int do_ping(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { ip_addr_t addr; + int ret; if (argc < 2) return CMD_RET_USAGE; @@ -171,13 +172,15 @@ int do_ping(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) return CMD_RET_USAGE; net_try_count = 1; -restart: - if (net_lwip_eth_start() < 0 || ping_loop(eth_get_dev(), &addr) < 0) { - if (net_start_again() == 0) - goto restart; - else - return CMD_RET_FAILURE; - } - return CMD_RET_SUCCESS; + do { + if (net_lwip_eth_start() == 0) { + ret = ping_loop(eth_get_dev(), &addr); + net_lwip_eth_stop(); + if (ret == 0) + return CMD_RET_SUCCESS; + } + } while (net_start_again() == 0); + + return CMD_RET_FAILURE; } diff --git a/cmd/lwip/sntp.c b/cmd/lwip/sntp.c index 608345c873b..5fa400b104a 100644 --- a/cmd/lwip/sntp.c +++ b/cmd/lwip/sntp.c @@ -101,6 +101,7 @@ int do_sntp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) ip_addr_t *srvip; char *server; ip_addr_t ipaddr; + int ret = CMD_RET_FAILURE; switch (argc) { case 1: @@ -127,7 +128,12 @@ int do_sntp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) return CMD_RET_FAILURE; if (sntp_loop(eth_get_dev(), srvip) < 0) - return CMD_RET_FAILURE; + goto out; + + ret = CMD_RET_SUCCESS; + +out: + net_lwip_eth_stop(); - return CMD_RET_SUCCESS; + return ret; } diff --git a/doc/develop/release_cycle.rst b/doc/develop/release_cycle.rst index 1e3e179d77f..2f024905157 100644 --- a/doc/develop/release_cycle.rst +++ b/doc/develop/release_cycle.rst @@ -79,7 +79,7 @@ For the next scheduled release, release candidates were made on: * U-Boot |next_ver|-rc4 was released on Mon 08 June 2026. -.. * U-Boot |next_ver|-rc5 was released on Mon 22 June 2026. +* U-Boot |next_ver|-rc5 was released on Mon 22 June 2026. Please note that the following dates are planned only and may be deviated from as needed. diff --git a/drivers/net/airoha_eth.c b/drivers/net/airoha_eth.c index 84ee9b2ad76..e5d39b95cc5 100644 --- a/drivers/net/airoha_eth.c +++ b/drivers/net/airoha_eth.c @@ -845,11 +845,45 @@ static int airoha_alloc_gdm_port(struct udevice *dev, ofnode node) (ulong)eth, node, &gdm_dev); } +static struct udevice *airoha_switch_mdio_init(struct udevice *dev) +{ + struct airoha_eth_soc_data *data = (void *)dev_get_driver_data(dev); + ofnode switch_node, mdio_node; + struct udevice *mdio_dev; + int ret; + + if (!CONFIG_IS_ENABLED(MDIO_MT7531_MMIO)) + return NULL; + + switch_node = ofnode_by_compatible(ofnode_null(), + data->switch_compatible); + if (!ofnode_valid(switch_node)) { + debug("Warning: missing airoha switch node\n"); + return ERR_PTR(-EINVAL); + } + + mdio_node = ofnode_find_subnode(switch_node, "mdio"); + if (!ofnode_valid(mdio_node)) { + debug("Warning: missing airoha switch mdio subnode\n"); + return ERR_PTR(-EINVAL); + } + + ret = device_bind_driver_to_node(dev, "mt7531-mdio-mmio", "mt7531-mdio", + mdio_node, &mdio_dev); + if (ret) { + debug("Warning: failed to bind airoha switch mdio\n"); + return ERR_PTR(ret); + } + + return mdio_dev; +} + static int airoha_eth_probe(struct udevice *dev) { struct airoha_eth_soc_data *data = (void *)dev_get_driver_data(dev); struct airoha_eth *eth = dev_get_priv(dev); struct regmap *scu_regmap; + struct udevice *mdio_dev; ofnode node; int i, ret; @@ -908,10 +942,10 @@ static int airoha_eth_probe(struct udevice *dev) if (ret) return ret; - if (eth->switch_mdio_dev) { - if (!device_probe(eth->switch_mdio_dev)) - debug("Warning: failed to probe airoha switch mdio\n"); - } + /* Airoha switch mdio PHYs maybe used by several GDM devices */ + mdio_dev = airoha_switch_mdio_init(dev); + if (!IS_ERR_OR_NULL(mdio_dev)) + eth->switch_mdio_dev = mdio_dev; ofnode_for_each_subnode(node, dev_ofnode(dev)) { if (!ofnode_device_is_compatible(node, "airoha,eth-mac")) @@ -957,6 +991,16 @@ static int airoha_eth_port_probe(struct udevice *dev) #else return -EINVAL; #endif + } else { + /* + * GDM1 device connected to airoha switch. Probe airoha switch + * mdio to be able set/query states of corresponding LAN ports. + */ + ret = device_probe(eth->switch_mdio_dev); + if (ret) { + debug("Warning: failed to probe airoha switch mdio\n"); + eth->switch_mdio_dev = NULL; + } } return 0; @@ -1202,38 +1246,12 @@ static int arht_eth_write_hwaddr(struct udevice *dev) static int airoha_eth_bind(struct udevice *dev) { - struct airoha_eth_soc_data *data = (void *)dev_get_driver_data(dev); - struct airoha_eth *eth = dev_get_priv(dev); - ofnode switch_node, mdio_node; - int ret; - /* * Force Probe as we set the Main ETH driver as misc * to register multiple eth port for each GDM */ dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND); - if (!CONFIG_IS_ENABLED(MDIO_MT7531_MMIO)) - return 0; - - switch_node = ofnode_by_compatible(ofnode_null(), - data->switch_compatible); - if (!ofnode_valid(switch_node)) { - debug("Warning: missing switch node\n"); - return 0; - } - - mdio_node = ofnode_find_subnode(switch_node, "mdio"); - if (!ofnode_valid(mdio_node)) { - debug("Warning: missing mdio node\n"); - return 0; - } - - ret = device_bind_driver_to_node(dev, "mt7531-mdio-mmio", "mt7531-mdio", - mdio_node, ð->switch_mdio_dev); - if (ret) - debug("Warning: failed to bind mdio controller\n"); - return 0; } diff --git a/include/net-lwip.h b/include/net-lwip.h index 20cb0992cce..5d0627eb271 100644 --- a/include/net-lwip.h +++ b/include/net-lwip.h @@ -35,6 +35,7 @@ int eth_init_state_only(void); /* Set active state */ int net_lwip_dns_init(void); int net_lwip_eth_start(void); +void net_lwip_eth_stop(void); struct netif *net_lwip_new_netif(struct udevice *udev); struct netif *net_lwip_new_netif_noip(struct udevice *udev); void net_lwip_remove_netif(struct netif *netif); diff --git a/net/cdp.c b/net/cdp.c index 6e404981d4a..300b3d5c409 100644 --- a/net/cdp.c +++ b/net/cdp.c @@ -276,7 +276,13 @@ void cdp_receive(const uchar *pkt, unsigned len) ss = (const ushort *)pkt; type = ntohs(ss[0]); tlen = ntohs(ss[1]); - if (tlen > len) + /* + * tlen includes the 4-byte TLV header, so it must be at + * least 4. Without this check a crafted tlen < 4 makes the + * "tlen -= 4" below underflow (tlen is a ushort), and a tlen + * of 0 also fails to advance pkt/len, hanging the loop. + */ + if (tlen < 4 || tlen > len) goto pkt_short; pkt += tlen; diff --git a/net/lwip/dhcp.c b/net/lwip/dhcp.c index acdf601d7eb..18dc36ae7ca 100644 --- a/net/lwip/dhcp.c +++ b/net/lwip/dhcp.c @@ -138,18 +138,25 @@ int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) dev = eth_get_dev(); if (!dev) { log_err("No network device\n"); - return CMD_RET_FAILURE; + ret = CMD_RET_FAILURE; + goto out; } ret = dhcp_loop(dev); if (ret) - return ret; + goto out; if (argc > 1) { struct cmd_tbl cmdtp = {}; - return do_tftpb(&cmdtp, 0, argc, argv); + ret = do_tftpb(&cmdtp, 0, argc, argv); + goto out; } - return CMD_RET_SUCCESS; + ret = CMD_RET_SUCCESS; + +out: + net_lwip_eth_stop(); + + return ret; } diff --git a/net/lwip/dns.c b/net/lwip/dns.c index 8b7b3b7f970..b620b0611d6 100644 --- a/net/lwip/dns.c +++ b/net/lwip/dns.c @@ -91,6 +91,7 @@ int do_dns(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { char *name; char *var = NULL; + int ret; if (argc == 1 || argc > 3) return CMD_RET_USAGE; @@ -103,5 +104,9 @@ int do_dns(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) if (net_lwip_eth_start() < 0) return CMD_RET_FAILURE; - return dns_loop(eth_get_dev(), name, var); + ret = dns_loop(eth_get_dev(), name, var); + + net_lwip_eth_stop(); + + return ret; } diff --git a/net/lwip/net-lwip.c b/net/lwip/net-lwip.c index 0c83c004cab..cfe5a6a640d 100644 --- a/net/lwip/net-lwip.c +++ b/net/lwip/net-lwip.c @@ -31,6 +31,7 @@ void (*push_packet)(void *, int len) = 0; int net_try_count; static int net_restarted; int net_restart_wrap; +static int net_lwip_eth_started; static uchar net_pkt_buf[(PKTBUFSRX) * PKTSIZE_ALIGN + PKTALIGN] __aligned(PKTALIGN); const u8 net_bcast_ethaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -180,11 +181,15 @@ int net_lwip_eth_start(void) { int ret; + if (net_lwip_eth_started++ > 0) + return 0; + net_init(); eth_halt(); eth_set_current(); ret = eth_init(); if (ret < 0) { + net_lwip_eth_started--; eth_halt(); return ret; } @@ -192,6 +197,17 @@ int net_lwip_eth_start(void) return 0; } +void net_lwip_eth_stop(void) +{ + if (!net_lwip_eth_started) + return; + + if (--net_lwip_eth_started) + return; + + eth_halt(); +} + static struct netif *new_netif(struct udevice *udev, bool with_ip) { unsigned char enetaddr[ARP_HLEN]; diff --git a/net/lwip/nfs.c b/net/lwip/nfs.c index 9e6b801e465..4cc36373fdd 100644 --- a/net/lwip/nfs.c +++ b/net/lwip/nfs.c @@ -187,6 +187,7 @@ static int nfs_loop(struct udevice *udev, ulong addr, char *fname, int do_nfs(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { int ret = CMD_RET_SUCCESS; + bool started = false; char *arg = NULL; char *words[2] = { }; char *fname = NULL; @@ -281,10 +282,13 @@ int do_nfs(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) ret = CMD_RET_FAILURE; goto out; } + started = true; if (nfs_loop(eth_get_dev(), laddr, fname, srvip) < 0) ret = CMD_RET_FAILURE; out: + if (started) + net_lwip_eth_stop(); if (arg != net_boot_file_name) free(arg); return ret; diff --git a/net/lwip/tftp.c b/net/lwip/tftp.c index 7f3b28b8507..571c38172f9 100644 --- a/net/lwip/tftp.c +++ b/net/lwip/tftp.c @@ -261,6 +261,7 @@ static int tftp_loop(struct udevice *udev, ulong addr, char *fname, int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { int ret = CMD_RET_SUCCESS; + bool started = false; char *arg = NULL; char *words[3] = { }; char *fname = NULL; @@ -365,12 +366,15 @@ int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) ret = CMD_RET_FAILURE; goto out; } + started = true; if (tftp_loop(eth_get_dev(), laddr, fname, srvip, port) < 0) ret = CMD_RET_FAILURE; else image_load_addr = laddr; out: + if (started) + net_lwip_eth_stop(); if (arg != net_boot_file_name) free(arg); return ret; diff --git a/net/lwip/wget.c b/net/lwip/wget.c index 502c0faebb2..247ece18e2b 100644 --- a/net/lwip/wget.c +++ b/net/lwip/wget.c @@ -292,46 +292,22 @@ static err_t httpc_headers_done_cb(httpc_state_t *connection, void *arg, struct #if CONFIG_IS_ENABLED(WGET_CACERT) #endif -int wget_do_request(ulong dst_addr, char *uri) +static int wget_handle_request(struct wget_ctx *ctx, bool is_https, + struct udevice *udev, struct netif *netif) { #if CONFIG_IS_ENABLED(WGET_HTTPS) altcp_allocator_t tls_allocator; #endif httpc_connection_t conn; httpc_state_t *state; - struct udevice *udev; - struct netif *netif; - struct wget_ctx ctx; - char *path; - bool is_https; - - ctx.daddr = dst_addr; - ctx.saved_daddr = dst_addr; - ctx.done = NOT_DONE; - ctx.size = 0; - ctx.prevsize = 0; - ctx.start_time = 0; - ctx.content_len = 0; - ctx.hash_count = 0; - - if (parse_url(uri, ctx.server_name, &ctx.port, &path, &is_https)) - return CMD_RET_USAGE; - - if (net_lwip_eth_start() < 0) - return CMD_RET_FAILURE; - - if (!wget_info) - wget_info = &default_wget_info; - - udev = eth_get_dev(); - - netif = net_lwip_new_netif(udev); - if (!netif) - return -1; + int ret; /* if URL with hostname init dns */ - if (!ipaddr_aton(ctx.server_name, NULL) && net_lwip_dns_init()) - return CMD_RET_FAILURE; + if (!ipaddr_aton(ctx->server_name, NULL)) { + ret = net_lwip_dns_init(); + if (ret) + return ret; + } memset(&conn, 0, sizeof(conn)); #if CONFIG_IS_ENABLED(WGET_HTTPS) @@ -353,7 +329,7 @@ int wget_do_request(ulong dst_addr, char *uri) printf("Error: cacert authentication " "mode is 'required' but no CA " "certificates given\n"); - return CMD_RET_FAILURE; + return -EINVAL; } } else if (cacert_auth_mode == AUTH_NONE) { ca = NULL; @@ -374,12 +350,11 @@ int wget_do_request(ulong dst_addr, char *uri) tls_allocator.alloc = &altcp_tls_alloc; tls_allocator.arg = altcp_tls_create_config_client(ca, ca_sz, - ctx.server_name); + ctx->server_name); if (!tls_allocator.arg) { log_err("error: Cannot create a TLS connection\n"); - net_lwip_remove_netif(netif); - return -1; + return -ENODEV; } conn.altcp_allocator = &tls_allocator; @@ -388,30 +363,70 @@ int wget_do_request(ulong dst_addr, char *uri) conn.result_fn = httpc_result_cb; conn.headers_done_fn = httpc_headers_done_cb; - ctx.path = path; - if (httpc_get_file_dns(ctx.server_name, ctx.port, path, &conn, httpc_recv_cb, - &ctx, &state)) { - net_lwip_remove_netif(netif); - return CMD_RET_FAILURE; + if (httpc_get_file_dns(ctx->server_name, ctx->port, ctx->path, &conn, + httpc_recv_cb, ctx, &state)) { + return -ENODEV; } errno = 0; - while (!ctx.done) { + while (!ctx->done) { net_lwip_rx(udev, netif); if (ctrlc()) break; } - net_lwip_remove_netif(netif); - - if (ctx.done == SUCCESS) + if (ctx->done == SUCCESS) return 0; if (errno == EPERM && !wget_info->silent) printf("Certificate verification failed\n"); - return -1; + return -errno ?: -EIO; +} + +int wget_do_request(ulong dst_addr, char *uri) +{ + struct udevice *udev; + struct wget_ctx ctx; + struct netif *netif; + bool is_https; + int ret; + + ctx.daddr = dst_addr; + ctx.saved_daddr = dst_addr; + ctx.done = NOT_DONE; + ctx.size = 0; + ctx.prevsize = 0; + ctx.start_time = 0; + ctx.content_len = 0; + ctx.hash_count = 0; + + ret = parse_url(uri, ctx.server_name, &ctx.port, &ctx.path, &is_https); + if (ret) + return ret; + + ret = net_lwip_eth_start(); + if (ret) + return ret; + + if (!wget_info) + wget_info = &default_wget_info; + + udev = eth_get_dev(); + + netif = net_lwip_new_netif(udev); + if (!netif) { + net_lwip_eth_stop(); + return -ENODEV; + } + + ret = wget_handle_request(&ctx, is_https, udev, netif); + + net_lwip_remove_netif(netif); + net_lwip_eth_stop(); + + return ret; } /** diff --git a/net/net.c b/net/net.c index ae3b977781f..61c5a6ef6c4 100644 --- a/net/net.c +++ b/net/net.c @@ -1103,6 +1103,15 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp) *lenp = total_len + IP_HDR_SIZE; localip->ip_len = htons(*lenp); + + /* + * Mark the reassembly state empty so that any further + * fragment goes through the normal re-init path and + * rebuilds a clean hole list + */ + total_len = 0; + first_hole = 0; + return localip; } diff --git a/test/dm/Makefile b/test/dm/Makefile index d69b0e08d66..0e3c63568dd 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_MULTIPLEXER) += mux-emul.o obj-$(CONFIG_MUX_MMIO) += mux-mmio.o obj-y += fdtdec.o obj-$(CONFIG_MTD_RAW_NAND) += nand.o +obj-$(CONFIG_IP_DEFRAG) += net_defrag.o obj-$(CONFIG_UT_DM) += nop.o obj-y += ofnode.o obj-y += ofread.o diff --git a/test/dm/net_defrag.c b/test/dm/net_defrag.c new file mode 100644 index 00000000000..3fd40de90cd --- /dev/null +++ b/test/dm/net_defrag.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Regression test for IP fragment reassembly. + * + * The test drives the real RX path via net_process_received_packet(). Final IP + * fragment (MF=0) is duplicated, crafted payload triggers redelivery of the datagram, + * which fails the test for the unfixed code. + */ + +#include <net.h> +#include <string.h> +#include <test/ut.h> +#include <dm/test.h> + +#define FRAG_LEN (8) +#define PAYLOAD_OFFSET (ETHER_HDR_SIZE + IP_HDR_SIZE) +#define FRAME_LEN (PAYLOAD_OFFSET + FRAG_LEN) + +static int udp_rx_count; + +static void defrag_udp_handler(uchar *pkt, unsigned int dport, + struct in_addr sip, unsigned int sport, + unsigned int len) +{ + udp_rx_count++; +} + +static int build_frag(uchar *buf, u16 off_flags, const u16 *payload) +{ + struct ethernet_hdr *et = (struct ethernet_hdr *)buf; + struct ip_udp_hdr *ip = (struct ip_udp_hdr *)(buf + ETHER_HDR_SIZE); + + memset(buf, 0, FRAME_LEN); + et->et_protlen = htons(PROT_IP); + + ip->ip_hl_v = 0x45; + ip->ip_len = htons(IP_HDR_SIZE + FRAG_LEN); + ip->ip_id = htons(0x4321); + ip->ip_off = htons(off_flags); + ip->ip_ttl = 64; + ip->ip_p = IPPROTO_UDP; + /* Broadcast destination is accepted regardless of net_ip. */ + ip->ip_dst.s_addr = 0xffffffff; + ip->ip_sum = compute_ip_checksum(ip, IP_HDR_SIZE); + + memcpy(buf + PAYLOAD_OFFSET, payload, FRAG_LEN); + + return FRAME_LEN; +} + +static int dm_test_net_ip_defrag_dup_last(struct unit_test_state *uts) +{ + rxhand_f *saved_handler = net_get_udp_handler(); + uchar frame[FRAME_LEN]; + /* UDP header, carried by first fragment. */ + u16 udp_hdr[4] = { htons(5000), htons(5001), + htons(UDP_HDR_SIZE + FRAG_LEN), 0 }; + /* + * Second fragment's payload doubles as a fake hole + * {last_byte >= FRAG_LEN, next_hole = 0, prev_hole = 0}, so that the + * buggy code re-reading it on a duplicate re-delivers the datagram. + */ + u16 frag_b[4] = { 2 * FRAG_LEN, 0, 0, 0 }; + + udp_rx_count = 0; + net_set_udp_handler(defrag_udp_handler); + + /* UDP header, offset 0, MF=1; then data, offset 1, MF=0 */ + net_process_received_packet(frame, build_frag(frame, IP_FLAGS_MFRAG, udp_hdr)); + net_process_received_packet(frame, build_frag(frame, 1, frag_b)); + ut_asserteq(1, udp_rx_count); + + /* Duplicate the final fragment: UDP datagram must not be delivered again. */ + net_process_received_packet(frame, build_frag(frame, 1, frag_b)); + ut_asserteq(1, udp_rx_count); + + net_set_udp_handler(saved_handler); + + return 0; +} + +DM_TEST(dm_test_net_ip_defrag_dup_last, 0); |
