-
Notifications
You must be signed in to change notification settings - Fork 144
Description
It seems that recent neqo doesn't handle situations where GSO is not supported / not working.
# ./neqo-client --version
neqo-bin 0.16.1
# tcpdump -i eth0 -n &
# ./neqo-client https://172.24.77.164/foo.txt # address doesn't matter
07:26:44.883052 IP 10.10.0.1.47950 > 172.24.77.164.443: UDP, length 2504
Note the length of first packet. It used GSO to send multiple chunks in single syscall. In strace this looks like:
sendmsg(6, {msg_name={sa_family=AF_INET, sin_port=htons(443), sin_addr=inet_addr("172.24.77.164")}, msg_namelen=16, msg_iov=[{iov_base="\307\0\0\0\1\tV\2446\334\210.\24Q\246\0\0D\321\32\332i[\332\"\215{\205\303q\307\350"..., iov_len=2504}], msg_iovlen=1, msg_control=[{cmsg_len=20, cmsg_level=SOL_IP, cmsg_type=IP_TOS, cmsg_data=[0, 0, 0, 0]}, {cmsg_len=18, cmsg_level=SOL_UDP, cmsg_type=0x67, cmsg_data="\xe4\x04"}], msg_controllen=48, msg_flags=0}, 0) = 2504
So far so good, no issue here.
Now try again after disabling tx checksums.
# ethtool -K eth0 tx off
Actual changes:
tx-checksum-ip-generic: off
tx-tcp-segmentation: off [not requested]
tx-tcp-ecn-segmentation: off [not requested]
tx-tcp-mangleid-segmentation: off [not requested]
tx-tcp6-segmentation: off [not requested]
tx-checksum-sctp: off
# ./neqo-client https://172.24.77.164/foo.txt
Error: Io(Os { code: 5, kind: Uncategorized, message: "Input/output error" })
RUST_LOG=debug ./neqo-client https://172.24.77.164/foo.txt
0.000 DEBUG Logging initialized
0.001 DEBUG Default socket send buffer size is Ok(212992)
0.001 DEBUG Increasing socket recv buffer size from 212992 to 1048576, now: Ok(425984)
0.001 INFO h3 Client connecting: 0.0.0.0:54926 -> 172.24.77.164:443
0.001 DEBUG [CryptoStates] Creating initial cipher state v=Version2, role=Client dcid=22e26b67a8f22a8e60e3fe3f2b
0.001 DEBUG Making Write Initial CryptoDxState, v=Version2 cipher=4865 min_pn=29
0.002 DEBUG Making Read Initial CryptoDxState, v=Version2 cipher=4865 min_pn=0
0.002 DEBUG [CryptoStates] Creating initial cipher state v=Version1, role=Client dcid=22e26b67a8f22a8e60e3fe3f2b
0.002 DEBUG Making Write Initial CryptoDxState, v=Version1 cipher=4865 min_pn=29
0.002 DEBUG Making Read Initial CryptoDxState, v=Version1 cipher=4865 min_pn=0
0.002 DEBUG Outbound interface eth0 for destination 172.24.77.164 has MTU 1500
0.002 DEBUG [unv-path 0.0.0.0:54926->172.24.77.164:443] Make permanent
0.002 DEBUG [unv-path:22e26b67a8f22a8e60e3fe3f2b 0.0.0.0:54926->172.24.77.164:443] set as primary path
0.002 DEBUG [pri-unv-path:22e26b67a8f22a8e60e3fe3f2b 0.0.0.0:54926->172.24.77.164:443] Path validated Instant { tv_sec: 850677, tv_nsec: 929556533 }
0.002 DEBUG Decoder: creating a new qpack decoder
0.002 DEBUG [Client 22e26b67a8f22a8e60e3fe3f2b] client_start
0.003 DEBUG Writing transport parameters, msg=1
0.003 DEBUG [Agent 0x5596ff851d80] state -> InProgress
0.003 DEBUG [Client 22e26b67a8f22a8e60e3fe3f2b] State change from Init -> WaitInitial
0.003 DEBUG [Client 22e26b67a8f22a8e60e3fe3f2b] output_path send_profile SendProfile { limit: 1252, pto: None, probe: EnumSet(), paced: false }
0.003 DEBUG Building Initial dcid Some(CID [13]: 22e26b67a8f22a8e60e3fe3f2b) scid Some(CID [0]: )
0.003 DEBUG CRYPTO for in offset=0, len=1208
0.003 DEBUG [Client 22e26b67a8f22a8e60e3fe3f2b] pn=29 type=Initial pri-path:22e26b67a8f22a8e60e3fe3f2b 0.0.0.0:54926->172.24.77.164:443 Tos(Cs0, NotEct) len 1252
TX -> Crypto { offset: 0, len: 1208 }
0.003 DEBUG packet_sent this=0x5596ff86bfa0, pn=29, ps=1252
0.003 DEBUG [Client 22e26b67a8f22a8e60e3fe3f2b] output_path send_profile SendProfile { limit: 1252, pto: None, probe: EnumSet(), paced: false }
0.003 DEBUG Building Initial dcid Some(CID [13]: 22e26b67a8f22a8e60e3fe3f2b) scid Some(CID [0]: )
0.003 DEBUG CRYPTO for in offset=1208, len=312
0.003 DEBUG [Client 22e26b67a8f22a8e60e3fe3f2b] pn=30 type=Initial pri-path:22e26b67a8f22a8e60e3fe3f2b 0.0.0.0:54926->172.24.77.164:443 Tos(Cs0, NotEct) len 357
TX -> Crypto { offset: 1208, len: 312 }
0.003 DEBUG [Client 22e26b67a8f22a8e60e3fe3f2b] pad Initial from 357 to PLPMTU 1252
0.003 DEBUG packet_sent this=0x5596ff86bfa0, pn=30, ps=1252
0.003 DEBUG [Client 22e26b67a8f22a8e60e3fe3f2b] output_path send_profile SendProfile { limit: 255, pto: None, probe: EnumSet(), paced: true }
0.003 DEBUG Building Initial dcid Some(CID [13]: 22e26b67a8f22a8e60e3fe3f2b) scid Some(CID [0]: )
0.003 DEBUG TX blocked, profile=SendProfile { limit: 255, pto: None, probe: EnumSet(), paced: true }
0.003 DEBUG [Http3 client] check_connection_events - event StateChange(WaitInitial)
0.003 DEBUG [Http3 connection] Handle state change WaitInitial
0.004 INFO `libc::sendmsg` failed with Input/output error (os error 5); halting segmentation offload
Error: Io(Os { code: 5, kind: Uncategorized, message: "Input/output error" })
The affected syscall in strace:
sendmsg(6, {msg_name={sa_family=AF_INET, sin_port=htons(443), sin_addr=inet_addr("172.24.77.164")}, msg_namelen=16, msg_iov=[{iov_base="\313\0\0\0\1\r\324U\26\3014\265\316[E(\2329/\0\0D\315\236}\25\3671-\207\227\222"..., iov_len=2504}], msg_iovlen=1, msg_control=[{cmsg_len=20, cmsg_level=SOL_IP, cmsg_type=IP_TOS, cmsg_data=[0, 0, 0, 0]}, {cmsg_len=18, cmsg_level=SOL_UDP, cmsg_type=0x67, cmsg_data="\xe4\x04"}], msg_controllen=48, msg_flags=0}, 0) = -1 EIO (Input/output error)
Note if I would use pre-GSO version of neqo everything runs fine even with tx checksums are disabled.
I came across this issues when looking at neqo as part of quic interop.
In its environment it explicitly disables tx checksumming:
https://github.com/quic-interop/quic-network-simulator/blob/75a5ca5beb660a0e851b7971d602d63ee318b8a8/endpoint/setup.sh#L7
and yet it seems everything is working fine:
https://interop.seemann.io/logs/2025-10-06T02:00/quiche_neqo/http3/output.txt
In the above output you can clearly see the tx-checksumming was disabled so I suspect the interop is not using latest neqo.