IPv4 destinations on dual-stack IPv6 sockets fail on macOS
- Dominant language
- C++
- Stars
- 28.9k
- Forks
- 5.6k
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 437
Description
*Description*:
`IoSocketHandleImpl::connect()`/`sendmsg()` pass a `sockaddr_in` to `AF_INET6` sockets for `AF_INET` destinations. Fails on macOS, works on Linux. The v4-mapped `sockaddr_in6` form works everywhere.
Doesn't concern any client code, client sockets take their family from the address they're created with, so the families never diverge. It only arises on dual-stack listeners, the peer address arrives v4-mapped and `addressFromSockAddr()` normalizes it to `Ipv4Instance` because `socket_v6only_` is false, so the reply goes back out on that AF_INET6 fd with a `sockaddr_in`.
Tested some things over loopback [here](https://gist.github.com/filipcacky/f2af5a4eab084db5b8cd0a772015a135), didn't check on real network, so might be different.
| Operation | V6ONLY | Debian 6.12 | macOS 26.5 Darwin 25.5.0 | iOS 26.5.2 (23F84) |
|--------------------------------------------|------------|----------------------|--------------------------|-------------------------|
| TCP connect, sockaddr_in | dual-stack | EINVAL | EAFNOSUPPORT | EPERM |
| UDP connect, sockaddr_in | dual-stack | OK | EINVAL | EPERM |
| UDP sendto, sockaddr_in | dual-stack | OK | EINVAL | EPERM |
| TCP connect, v4-mapped sockaddr_in6 | 1 | ENETUNREACH | EAFNOSUPPORT | EAFNOSUPPORT |
| UDP connect + send, v4-mapped sockaddr_in6 | 1 | connect ENETUNREACH | connect OK, send EINVAL | connect OK, send EINVAL |
| UDP sendto, v4-mapped sockaddr_in6 | 1 | ENETUNREACH | OK | OK |
The UDP sendto to v4-mapped on V6ONLY doesn't concern this, but it's _interesting_.
*Reproduction*:
- QuicHttpIntegrationSPATest.UsesPreferredAddressDualStack
```
[ RUN ] QuicHttpIntegrationSPATests/QuicHttpIntegrationSPATest.UsesPreferredAddressDualStack/IPv4_quiche_client_impl_migration_in_house
[error][io] [source/common/network/io_socket_handle_impl.cc:227] EINVAL error. Socket is open: true, IPv4.
[error][io] [./source/common/network/io_socket_handle_impl.h:91] Invalid argument passed in.
[error][quic] [external/quiche/quiche/quic/core/quic_connection.cc:4093] Server: Write failed with error: 22 (Invalid argument)
[error][io] [source/common/network/io_socket_handle_impl.cc:227] EINVAL error. Socket is open: true, IPv4.
[error][io] [./source/common/network/io_socket_handle_impl.h:91] Invalid argument passed in.
[error][io] [source/common/network/io_socket_handle_impl.cc:227] EINVAL error. Socket is open: true, IPv4.
[error][io] [./source/common/network/io_socket_handle_impl.h:91] Invalid argument passed in.
[error][testing] [./test/integration/quic_http_integration_test.h:330] Fail to connect to server with error: QUIC_NETWORK_IDLE_TIMEOUT with details: No recent network activity after 4002631us. Timeout:4s num_undecryptable_packets: 0 {}
[error][io] [source/common/network/io_socket_handle_impl.cc:227] EINVAL error. Socket is open: true, IPv4.
[error][io] [./source/common/network/io_socket_handle_impl.h:91] Invalid argument passed in.
...
```
- UDP proxy
Dual-stack listener on `[::]` + `ipv4_compat`, v4 client gets no reply. Datagram is forwarded upstream fine, the reply `sendmsg()` on the v6 fd fails with:
```
[debug][filter] [source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc:662] creating new session: downstream=127.0.0.1:52645 local=127.0.0.1:10000 upstream=127.0.0.1:10001
[error][io] [source/common/network/io_socket_handle_impl.cc:227] EINVAL error. Socket is open: true, IPv4.
[error][io] [./source/common/network/io_socket_handle_impl.h:91] Invalid argument passed in.
[debug][misc] [source/common/network/utility.cc:527] sendmsg failed with error code 11: Invalid argument
```
Contributor guide
Research direction
Start in source/common/network/io_socket_handle_impl.cc at IoSocketHandleImpl::connect(), sendmsg(), and addressFromSockAddr(). Run QuicHttpIntegrationSPATest.UsesPreferredAddressDualStack and reproduce the UDP proxy case on macOS. Done means IPv4 destinations sent through dual-stack IPv6 sockets no longer fail with EINVAL or EAFNOSUPPORT.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, macos
- Domain
- networking
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100