Tunnel inbound: SO_REUSEPORT fix and IPv6 support for the OpenBSD transparent proxy (#6624)

Completes https://github.com/XTLS/Xray-core/pull/6546
This commit is contained in:
Maksim Varentsov
2026-08-26 21:05:05 +00:00
committed by GitHub
parent dffc7ada5e
commit 25c11e2d2b
4 changed files with 110 additions and 25 deletions
+11 -4
View File
@@ -14,11 +14,18 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error {
if config.ReceiveOriginalDestAddress && isUDPSocket(network) {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVDSTADDR, 1); err != nil {
return errors.New("failed to set IP_RECVDSTADDR").Base(err)
// Only the options matching the socket's address family are accepted,
// so one of the two pairs succeeding is enough.
err6 := unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_RECVPKTINFO, 1)
if err6 == nil {
err6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_RECVDSTPORT, 1)
}
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVDSTPORT, 1); err != nil {
return errors.New("failed to set IP_RECVDSTPORT").Base(err)
err4 := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVDSTADDR, 1)
if err4 == nil {
err4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVDSTPORT, 1)
}
if err4 != nil && err6 != nil {
return errors.New("failed to enable receiving the original destination").Base(err4)
}
}
return nil
+31 -15
View File
@@ -17,23 +17,39 @@ func retrieveOriginalDestFromControlMessages(msgs []unix.SocketControlMessage) n
var havePort bool
for _, msg := range msgs {
if msg.Header.Level != unix.IPPROTO_IP {
continue
}
switch msg.Header.Type {
case unix.IP_RECVDSTADDR:
if len(msg.Data) < 4 {
continue
switch msg.Header.Level {
case unix.IPPROTO_IP:
switch msg.Header.Type {
case unix.IP_RECVDSTADDR:
if len(msg.Data) < 4 {
continue
}
ip = append(ip[:0], msg.Data[:4]...)
haveAddress = true
case unix.IP_RECVDSTPORT:
if len(msg.Data) < 2 {
continue
}
port = binary.BigEndian.Uint16(msg.Data[:2])
havePort = true
}
ip = append(ip[:0], msg.Data[:4]...)
haveAddress = true
case unix.IP_RECVDSTPORT:
if len(msg.Data) < 2 {
continue
case unix.IPPROTO_IPV6:
switch msg.Header.Type {
case unix.IPV6_PKTINFO:
// struct in6_pktinfo: the destination address is followed by
// the interface index.
if len(msg.Data) < 16 {
continue
}
ip = append(ip[:0], msg.Data[:16]...)
haveAddress = true
case unix.IPV6_RECVDSTPORT:
if len(msg.Data) < 2 {
continue
}
port = binary.BigEndian.Uint16(msg.Data[:2])
havePort = true
}
port = binary.BigEndian.Uint16(msg.Data[:2])
havePort = true
}
}
@@ -33,6 +33,36 @@ func TestRetrieveOriginalDestFromControlMessages(t *testing.T) {
}
}
func TestRetrieveOriginalDestFromControlMessagesIPv6(t *testing.T) {
// 2001:db8::7 followed by the interface index, as in struct in6_pktinfo.
pktinfo := []byte{
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0x07,
0x02, 0, 0, 0,
}
msgs := []unix.SocketControlMessage{
{
Header: unix.Cmsghdr{Level: unix.IPPROTO_IPV6, Type: unix.IPV6_RECVDSTPORT},
Data: []byte{0x30, 0x39},
},
{
Header: unix.Cmsghdr{Level: unix.IPPROTO_IPV6, Type: unix.IPV6_PKTINFO},
Data: pktinfo,
},
}
dest := retrieveOriginalDestFromControlMessages(msgs)
if !dest.IsValid() {
t.Fatal("destination is invalid")
}
if got, want := dest.Address.IP().String(), "2001:db8::7"; got != want {
t.Fatalf("address = %q, want %q", got, want)
}
if got, want := dest.Port.Value(), uint16(12345); got != want {
t.Fatalf("port = %d, want %d", got, want)
}
}
func TestRetrieveOriginalDestRequiresAddressAndPort(t *testing.T) {
tests := [][]unix.SocketControlMessage{
{
@@ -47,6 +77,18 @@ func TestRetrieveOriginalDestRequiresAddressAndPort(t *testing.T) {
Data: []byte{0x30, 0x39},
},
},
{
{
Header: unix.Cmsghdr{Level: unix.IPPROTO_IPV6, Type: unix.IPV6_PKTINFO},
Data: make([]byte, 20),
},
},
{
{
Header: unix.Cmsghdr{Level: unix.IPPROTO_IPV6, Type: unix.IPV6_RECVDSTPORT},
Data: []byte{0x30, 0x39},
},
},
}
for _, msgs := range tests {