Summary
The yamux stream muxer sends SYN, ACK, FIN, and RST control frames with TYPE_DATA (0) instead of TYPE_WINDOW_UPDATE (1). This violates the yamux spec and breaks interop with go-yamux, which expects control frames as TYPE_WINDOW_UPDATE.
Additionally, the length field in SYN/ACK frames should carry the window size (not 0), matching go-yamux behavior.
Impact
- Go interop broken: Go peers reject or misinterpret control frames sent as TYPE_DATA
- Frame interleaving: Without write serialization, concurrent writes can corrupt frames on the wire
Root Cause
In libp2p/stream_muxer/yamux/yamux.py:
open_stream() sends SYN with TYPE_DATA — should be TYPE_WINDOW_UPDATE with length=DEFAULT_WINDOW_SIZE
close() sends FIN with TYPE_DATA — should be TYPE_WINDOW_UPDATE
reset() sends RST with TYPE_DATA — should be TYPE_WINDOW_UPDATE
- ACK responses use
TYPE_DATA — should be TYPE_WINDOW_UPDATE with length=DEFAULT_WINDOW_SIZE
- No write lock — concurrent frame writes can interleave
Fix
A working fix is available at asabya/py-libp2p#1:
- Changed all control frames to
TYPE_WINDOW_UPDATE
- Added
_write_lock (trio.Lock) to serialize all frame writes
- SYN/ACK now include
DEFAULT_WINDOW_SIZE in the length field
Ref: go-yamux stream.go, session.go, const.go
Summary
The yamux stream muxer sends SYN, ACK, FIN, and RST control frames with
TYPE_DATA(0) instead ofTYPE_WINDOW_UPDATE(1). This violates the yamux spec and breaks interop with go-yamux, which expects control frames asTYPE_WINDOW_UPDATE.Additionally, the
lengthfield in SYN/ACK frames should carry the window size (not 0), matching go-yamux behavior.Impact
Root Cause
In
libp2p/stream_muxer/yamux/yamux.py:open_stream()sends SYN withTYPE_DATA— should beTYPE_WINDOW_UPDATEwithlength=DEFAULT_WINDOW_SIZEclose()sends FIN withTYPE_DATA— should beTYPE_WINDOW_UPDATEreset()sends RST withTYPE_DATA— should beTYPE_WINDOW_UPDATETYPE_DATA— should beTYPE_WINDOW_UPDATEwithlength=DEFAULT_WINDOW_SIZEFix
A working fix is available at asabya/py-libp2p#1:
TYPE_WINDOW_UPDATE_write_lock(trio.Lock) to serialize all frame writesDEFAULT_WINDOW_SIZEin the length fieldRef: go-yamux stream.go, session.go, const.go