Repository: go
Issue: x/net: Failure to send IPv4 UDP packet on ipv6 socket with ControlMessage
Labels: NeedsInvestigation

### Go version

go version go1.23.2 (Red Hat 1.23.2-1.el9) linux/amd64

### Output of `go env` in your module/workspace:

```shell
GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='~/.cache/go-build'
GOENV='~/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='~/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='~/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/lib/golang'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/usr/lib/golang/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.23.2 (Red Hat 1.23.2-1.el9)'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='~/.config/go/telemetry'
GCCGO='/bin/gccgo'
GOAMD64='v2'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='~/temp/test/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2023383051=/tmp/go-build -gno-record-gcc-switches'
```


### What did you do?

The following program successfully receives and sends traffic on both IPv4 and IPv6:
```
package main

import (
        "fmt"
        "golang.org/x/net/ipv6"
        "net"
)

func main() {

        listenSocket, err := net.ListenPacket("udp", ":12345")
        wrapped := ipv6.NewPacketConn(listenSocket)
        wrapped.SetControlMessage(ipv6.FlagDst, false)

        data := make([]byte, 65535)
        n, cm, remote, err := wrapped.ReadFrom(data)
        fmt.Printf("Bytes read %d\n", n)
        fmt.Printf("ReadFrom: %T %+v %+v\n", remote, remote, cm)

        n, err = wrapped.WriteTo(data[:n], cm, remote)
        fmt.Printf("Bytes write %d\n", n)
        if err != nil {
                fmt.Printf("WriteTo: %+v\n", err)
        }

}
```

Output when receiving traffic on 127.0.0.1:
```
Bytes read 4
ReadFrom: *net.UDPAddr 127.0.0.1:57398 <nil>
Bytes write 4
```

Output when receiving traffic on ::1:
```
Bytes read 4
ReadFrom: *net.UDPAddr [::1]:48275 <nil>
Bytes write 4
```

When changing `false` to `true` in the `SetControlMessage()` call, the program still works on IPv6, but fails on IPv4.

Output when receiving traffic on 127.0.0.1:
```
Bytes read 4
ReadFrom: *net.UDPAddr 127.0.0.1:59701 tclass=0x0 hoplim=0 src=<nil> dst=127.0.0.1 ifindex=1 nexthop=<nil> mtu=0
Bytes write 0
WriteTo: write udp [::]:12345->127.0.0.1:59701: sendmsg: invalid argument
```

Output when receiving traffic on ::1:
```
Bytes read 4
ReadFrom: *net.UDPAddr [::1]:49933 tclass=0x0 hoplim=0 src=::1 dst=::1 ifindex=1 nexthop=<nil> mtu=0
Bytes write 4
```

### What did you see happen?

Error when sending traffic to an IPv4 address when `ControlMessage` is used. This is likely because of a confusion in the system call, where the destination address is an IPv4 struct, while the socket options are an IPv6 struct. The fact that the destination address was originally received on an IPv6 socket, and should be passed back into the system as such gets lost.

Example system call capture of a failed IPv4 send:
```
[pid 48372] recvmsg(3, {msg_name={sa_family=AF_INET6, sin6_port=htons(49970), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_scope_id=0}, msg_namelen=112 => 28, msg_iov=[{iov_base="foo\n", iov_len=65535}], msg_iovlen=1, msg_control=[{cmsg_len=36, cmsg_level=SOL_IPV6, cmsg_type=0x32, cmsg_data="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x7f\x00\x00\x01\x01\x00\x00\x00"}], msg_controllen=40, msg_flags=0}, 0) = 4
[pid 48372] getsockopt(3, SOL_SOCKET, SO_PROTOCOL, [17], [4]) = 0
[pid 48372] sendmsg(3, {msg_name={sa_family=AF_INET, sin_port=htons(49970), sin_addr=inet_addr("127.0.0.1")}, msg_namelen=16, msg_iov=[{iov_base="foo\n", iov_len=4}], msg_iovlen=1, msg_control=[{cmsg_len=36, cmsg_level=SOL_IPV6, cmsg_type=0x32, cmsg_data="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00"}], msg_controllen=40, msg_flags=0}, 0) = -1 EINVAL (Invalid argument)
```

### What did you expect to see?

Traffic being able to be sent with both IPv4 and IPv6, as is possible without using `ControlMessage`