Dual-mode IPv6 sockets do not accept IPv4 connections via localhost
- Dominant language
- C++
- Stars
- 33.7k
- Forks
- 1.8k
- Avg merge
- 3d 17h
- Merged PRs (30d)
- 116
Description
### Windows Version
Microsoft Windows [Version 10.0.26200.7705]
### WSL Version
2.6.3.0
### Are you using WSL 1 or WSL 2?
- [x] WSL 2
- [ ] WSL 1
### Kernel Version
6.6.87.2-1
### Distro Version
Fedora Linux 43
### Other Software
N/A
### Repro Steps
1. Create a simple HTTP server in Go that listens on :8000 (dual-stack by default)
```golang
// main.go
package main
import "net/http"
func main() { http.ListenAndServe(":8000", nil) }
```
1. Run the server in WSL2
```bash
go run main.go
```
1. From the same WSL2 instance, try to connect via IPv4
```bash
curl http://localhost:8000/ -v -4
```
1. From the same WSL2 instance, try to connect via IPv6
```bash
curl http://localhost:8000/ -v -6
```
### Actual curl output
**IPv4 connection (FAILS):**
```console
$ curl http://localhost:8000/ -v -4
* Host localhost:8000 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying 127.0.0.1:8000...
* connect to 127.0.0.1 port 8000 from 127.0.0.1 port 51042 failed: Connection refused
* Failed to connect to localhost port 8000 after 0 ms: Could not connect to server
* closing connection #0
curl: (7) Failed to connect to localhost port 8000 after 0 ms: Could not connect to server
```
**IPv6 connection (WORKS):**
```console
$ curl http://localhost:8000/ -v -6
* Host localhost:8000 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8000...
* Connected to localhost (::1) port 8000
* using HTTP/1.x
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/8.15.0
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 404 Not Found
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Tue, 03 Feb 2026 14:15:18 GMT
< Content-Length: 19
<
404 page not found
* Connection #0 to host localhost left intact
```
### Expected Behavior
On standard Linux, when an IPv6 socket is created without setting `IPV6_V6ONLY`, it operates in **dual-stack mode** and accepts connections from both IPv4 and IPv6 clients. IPv4 connections are received as IPv4-mapped IPv6 addresses (e.g., `::ffff:127.0.0.1`).
The expected behavior is:
- `127.0.0.1:8080` → Connection succeeds (mapped to `::ffff:127.0.0.1`)
- `[::1]:8080` → Connection succeeds
### Actual Behavior
In WSL2, IPv4 connections to a dual-stack socket listening on `::` are refused, even when connecting from within the same WSL2 instance.
- `curl -4 http://localhost:8000/` → **Connection refused**
- `curl -6 http://localhost:8000/` → Connection succeeds (404 is expected, server is working)
### Additional Context
This issue is related to #4851 (Localhost relay does not support dual-mode sockets), but demonstrates that the problem also occurs **within WSL2 itself** (not just Windows-to-WSL communication).
Go's `net/http` package, when given an address like `:8000`, creates a dual-stack socket listening on `[::]` by default. This is standard behavior that works correctly on native Linux systems. Many applications and frameworks (e.g., Node.js, Python's `http.server`, various web frameworks) use this same pattern to serve both IPv4 and IPv6 clients with a single socket.
### Diagnostic Logs
[WslLogs-2026-02-03_23-34-10.zip](https://github.com/user-attachments/files/25048869/WslLogs-2026-02-03_23-34-10.zip)
Contributor guide
Research direction
Start by reproducing the issue with the main.go server and the curl -4 and curl -6 commands from the report, then review the WSL2 networking path implicated by the diagnostic logs. Done means a socket listening on [::] accepts both 127.0.0.1 and ::1 connections within WSL2, matching the expected dual-stack behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- networking, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100