Building any MP_JOIN option raises AttributeError, because TCP._make constructs the options before it assigns the self._flags those option makers read.
Mechanism
pcapkit/protocols/transport/tcp.py:547 builds the options first:
options_value, total_length = self._make_tcp_options(options)
and self._flags is only assigned twenty lines later, at tcp.py:567:
_flag = cast('Enum_Flags', 0)
for key, val in flags.items():
if val == 1:
_flag |= Enum_Flags.get(key.upper())
self._flags = _flag
But _make_mptcp_join, reached from _make_tcp_options, branches on it at tcp.py:2695-2699:
if Enum_Flags.SYN in self._flags and Enum_Flags.ACK not in self._flags: # MP_JOIN-SYN
if Enum_Flags.SYN in self._flags and Enum_Flags.ACK in self._flags: # MP_JOIN-SYN/ACK
if Enum_Flags.SYN not in self._flags and Enum_Flags.ACK in self._flags: # MP_JOIN-ACK
The read path has the same pair of branches at tcp.py:1521-1525, where the ordering is fine because _read assigns self._flags at tcp.py:485 before parsing options. Only the make path is inverted.
Reproduction
from pcapkit.protocols.transport.tcp import TCP
from pcapkit.const.tcp.option import Option
from pcapkit.const.tcp.mp_tcp_option import MPTCPOption
TCP(srcport=1, dstport=2, seq=0, ack=0, syn=True,
options=[(Option.Multipath_TCP, {'subtype': MPTCPOption.MP_JOIN, 'backup': False,
'addr_id': 1, 'token': 7, 'nonce': 9})])
AttributeError: 'TCP' object has no attribute '_flags'
Executed on origin/main (9c240a60e), CPython 3.14.7. On a fresh instance the attribute does not exist at all, so this is an outright failure rather than a stale-value read — though an instance that has already parsed a packet would instead silently pick up that packet's flags, which is the worse of the two outcomes and is why the fix should not simply be to initialise _flags to zero.
Notes
Building any MP_JOIN option raises
AttributeError, becauseTCP._makeconstructs the options before it assigns theself._flagsthose option makers read.Mechanism
pcapkit/protocols/transport/tcp.py:547builds the options first:and
self._flagsis only assigned twenty lines later, attcp.py:567:But
_make_mptcp_join, reached from_make_tcp_options, branches on it attcp.py:2695-2699:The read path has the same pair of branches at
tcp.py:1521-1525, where the ordering is fine because_readassignsself._flagsattcp.py:485before parsing options. Only the make path is inverted.Reproduction
Executed on
origin/main(9c240a60e), CPython 3.14.7. On a fresh instance the attribute does not exist at all, so this is an outright failure rather than a stale-value read — though an instance that has already parsed a packet would instead silently pick up that packet's flags, which is the worse of the two outcomes and is why the fix should not simply be to initialise_flagsto zero.Notes
tcp-mptcp/MP_JOINremains inEXPECTED_FAILURESintests/protocols/test_option_roundtrip_unit.pyand must stay there until this is fixed. Do not delete that entry as part of _make_mptcp_*/_read_mptcp_* length arithmetic is wrong at six more sites beyond MP_CAPABLE #576/fix(tcp): correct MPTCP option length arithmetic at all six #576 sites #585.