Pull request net-20260623.

net:
- airoha_eth: fix mt7531 mdio related initialization bug

net-legacy:
- cdp: reject CDP TLVs with a length below the 4-byte header
- Clear IP defragmentation state after returning a complete packet

net-lwip:
- Halt ethernet after network commands
This commit is contained in:
Tom Rini
2026-06-23 10:36:58 -06:00
14 changed files with 269 additions and 92 deletions
+11 -8
View File
@@ -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;
}
+8 -2
View File
@@ -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;
return CMD_RET_SUCCESS;
ret = CMD_RET_SUCCESS;
out:
net_lwip_eth_stop();
return ret;
}
+48 -30
View File
@@ -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, &eth->switch_mdio_dev);
if (ret)
debug("Warning: failed to bind mdio controller\n");
return 0;
}
+1
View File
@@ -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);
+7 -1
View File
@@ -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;
+11 -4
View File
@@ -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;
}
+6 -1
View File
@@ -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;
}
+16
View File
@@ -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];
+4
View File
@@ -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;
+4
View File
@@ -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;
+61 -46
View File
@@ -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;
}
/**
+9
View File
@@ -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;
}
+1
View File
@@ -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
+82
View File
@@ -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);