mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-09 01:00:12 +00:00
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:
@@ -12,12 +12,29 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func FakeUDP(addr *net.UDPAddr, mark int) (net.PacketConn, error) {
|
func FakeUDP(addr *net.UDPAddr, mark int) (net.PacketConn, error) {
|
||||||
ip4 := addr.IP.To4()
|
domain := unix.AF_INET6
|
||||||
if ip4 == nil {
|
var sockaddr unix.Sockaddr
|
||||||
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("IPv6 is not supported by the OpenBSD transparent UDP patch")}
|
if ip4 := addr.IP.To4(); ip4 != nil {
|
||||||
|
domain = unix.AF_INET
|
||||||
|
sa := &unix.SockaddrInet4{Port: addr.Port}
|
||||||
|
copy(sa.Addr[:], ip4)
|
||||||
|
sockaddr = sa
|
||||||
|
} else if ip6 := addr.IP.To16(); ip6 != nil {
|
||||||
|
sa := &unix.SockaddrInet6{Port: addr.Port}
|
||||||
|
copy(sa.Addr[:], ip6)
|
||||||
|
if addr.Zone != "" {
|
||||||
|
iface, err := net.InterfaceByName(addr.Zone)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("resolve zone %s: %w", addr.Zone, err)}
|
||||||
|
}
|
||||||
|
sa.ZoneId = uint32(iface.Index)
|
||||||
|
}
|
||||||
|
sockaddr = sa
|
||||||
|
} else {
|
||||||
|
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("unsupported address %v", addr.IP)}
|
||||||
}
|
}
|
||||||
|
|
||||||
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
|
fd, err := unix.Socket(domain, unix.SOCK_DGRAM, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("socket open: %w", err)}
|
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("socket open: %w", err)}
|
||||||
}
|
}
|
||||||
@@ -35,9 +52,12 @@ func FakeUDP(addr *net.UDPAddr, mark int) (net.PacketConn, error) {
|
|||||||
if err = unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEADDR, 1); err != nil {
|
if err = unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEADDR, 1); err != nil {
|
||||||
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("set socket option SO_REUSEADDR: %w", err)}
|
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("set socket option SO_REUSEADDR: %w", err)}
|
||||||
}
|
}
|
||||||
|
// Several client sessions can be answered from the same original
|
||||||
|
// destination at the same time, so the address has to be shareable.
|
||||||
|
if err = unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil {
|
||||||
|
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("set socket option SO_REUSEPORT: %w", err)}
|
||||||
|
}
|
||||||
|
|
||||||
sockaddr := &unix.SockaddrInet4{Port: addr.Port}
|
|
||||||
copy(sockaddr.Addr[:], ip4)
|
|
||||||
if err = unix.Bind(fd, sockaddr); err != nil {
|
if err = unix.Bind(fd, sockaddr); err != nil {
|
||||||
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("bind %s: %w", addr.String(), err)}
|
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("bind %s: %w", addr.String(), err)}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,18 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
|
|||||||
|
|
||||||
func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error {
|
func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error {
|
||||||
if config.ReceiveOriginalDestAddress && isUDPSocket(network) {
|
if config.ReceiveOriginalDestAddress && isUDPSocket(network) {
|
||||||
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVDSTADDR, 1); err != nil {
|
// Only the options matching the socket's address family are accepted,
|
||||||
return errors.New("failed to set IP_RECVDSTADDR").Base(err)
|
// 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 {
|
err4 := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVDSTADDR, 1)
|
||||||
return errors.New("failed to set IP_RECVDSTPORT").Base(err)
|
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
|
return nil
|
||||||
|
|||||||
@@ -17,23 +17,39 @@ func retrieveOriginalDestFromControlMessages(msgs []unix.SocketControlMessage) n
|
|||||||
var havePort bool
|
var havePort bool
|
||||||
|
|
||||||
for _, msg := range msgs {
|
for _, msg := range msgs {
|
||||||
if msg.Header.Level != unix.IPPROTO_IP {
|
switch msg.Header.Level {
|
||||||
continue
|
case unix.IPPROTO_IP:
|
||||||
}
|
switch msg.Header.Type {
|
||||||
|
case unix.IP_RECVDSTADDR:
|
||||||
switch msg.Header.Type {
|
if len(msg.Data) < 4 {
|
||||||
case unix.IP_RECVDSTADDR:
|
continue
|
||||||
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]...)
|
case unix.IPPROTO_IPV6:
|
||||||
haveAddress = true
|
switch msg.Header.Type {
|
||||||
case unix.IP_RECVDSTPORT:
|
case unix.IPV6_PKTINFO:
|
||||||
if len(msg.Data) < 2 {
|
// struct in6_pktinfo: the destination address is followed by
|
||||||
continue
|
// 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) {
|
func TestRetrieveOriginalDestRequiresAddressAndPort(t *testing.T) {
|
||||||
tests := [][]unix.SocketControlMessage{
|
tests := [][]unix.SocketControlMessage{
|
||||||
{
|
{
|
||||||
@@ -47,6 +77,18 @@ func TestRetrieveOriginalDestRequiresAddressAndPort(t *testing.T) {
|
|||||||
Data: []byte{0x30, 0x39},
|
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 {
|
for _, msgs := range tests {
|
||||||
|
|||||||
Reference in New Issue
Block a user