From 72000f630ea3462f4655c1414c3f301d37bd194c Mon Sep 17 00:00:00 2001 From: JP Hutchins Date: Tue, 22 Sep 2026 16:25:36 -0700 Subject: [PATCH] fix(udp): cap the payload at the server's advertised buffer `SMPUDPTransport.max_unencoded_size` overrode the base implementation with the MSS alone, so the MCUmgr parameters that `SMPClient` reads on connect never reached the UDP transport: `initialize(buf_size)` stored the value and nothing read it. Zephyr's UDP SMP transport receives each request as a single datagram into one MCUmgr buffer of `CONFIG_MCUMGR_TRANSPORT_NETBUF_SIZE` bytes, the value advertised as `buf_size`. A request larger than that cannot be received. The defaults (2048-byte buffer, 1500-byte MTU) hide this. A build that lowers the buffer, e.g. to its 384-byte non-UDP default, does not. The payload is now `min(MSS, buf_size)`. Before the params are known, `buf_size or mtu` makes that the MSS, so nothing changes for a server that does not advertise them. Verified: `camas check` green; the new parametrized test fails on the 384-byte case without the fix. https://github.com/zephyrproject-rtos/zephyr/blob/70be2ff0b565a3313128f5577f51cfeb3ebcf602/subsys/mgmt/mcumgr/grp/os_mgmt/src/os_mgmt.c#L551-L554 https://github.com/zephyrproject-rtos/zephyr/blob/70be2ff0b565a3313128f5577f51cfeb3ebcf602/subsys/mgmt/mcumgr/transport/Kconfig#L33-L58 Co-Authored-By: Claude Opus 5.5 (1M context) --- src/smpclient/transport/udp.py | 5 +++-- tests/test_smp_udp_transport.py | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/smpclient/transport/udp.py b/src/smpclient/transport/udp.py index 9e4e568..af38d93 100644 --- a/src/smpclient/transport/udp.py +++ b/src/smpclient/transport/udp.py @@ -137,7 +137,8 @@ def max_unencoded_size(self) -> int: """Maximum UDP payload size (MSS) to avoid fragmentation. Subtracts IPv4/IPv6 and UDP header overhead from MTU per RFC 8085 section 3.2. - The IP version is auto-detected after connection. + The IP version is auto-detected after connection. Once the server's MCUmgr + parameters are known, the payload is also capped at its advertised buffer. """ overhead = IPV6_UDP_OVERHEAD if self._is_ipv6 else IPV4_UDP_OVERHEAD - return self._mtu - overhead + return min(self._mtu - overhead, self._smp_server_transport_buffer_size or self._mtu) diff --git a/tests/test_smp_udp_transport.py b/tests/test_smp_udp_transport.py index 6096b59..845fb70 100644 --- a/tests/test_smp_udp_transport.py +++ b/tests/test_smp_udp_transport.py @@ -132,6 +132,17 @@ def test_max_unencoded_size_custom_mtu() -> None: assert t.max_unencoded_size == 484 +@pytest.mark.parametrize( + "buf_size, expected", + [(384, 384), (1472, 1472), (2048, 1472)], +) +def test_max_unencoded_size_capped_by_server_buffer(buf_size: int, expected: int) -> None: + """Zephyr copies each datagram into one `buf_size` buffer, so neither bound may be exceeded.""" + t = SMPUDPTransport(mtu=1500) + t.initialize(buf_size) + assert t.max_unencoded_size == expected + + @pytest.mark.asyncio async def test_ipv4_detection_real_socket() -> None: """Test IPv4 auto-detection with real socket connection."""