Tunnel inbound: Support TPROXY on OpenBSD as well (#6546)

https://github.com/XTLS/Xray-core/pull/6546#issuecomment-5100711574
This commit is contained in:
Maksim Varentsov
2026-08-12 04:51:38 +00:00
committed by GitHub
parent bc6e966af8
commit a000371b2a
7 changed files with 211 additions and 6 deletions
+58
View File
@@ -0,0 +1,58 @@
//go:build openbsd
// +build openbsd
package dokodemo
import (
"fmt"
"net"
"os"
"golang.org/x/sys/unix"
)
func FakeUDP(addr *net.UDPAddr, mark int) (net.PacketConn, error) {
ip4 := addr.IP.To4()
if ip4 == nil {
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("IPv6 is not supported by the OpenBSD transparent UDP patch")}
}
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
if err != nil {
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("socket open: %w", err)}
}
closeFD := true
defer func() {
if closeFD {
unix.Close(fd)
}
}()
if err = unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_BINDANY, 1); err != nil {
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("set socket option SO_BINDANY: %w", err)}
}
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)}
}
sockaddr := &unix.SockaddrInet4{Port: addr.Port}
copy(sockaddr.Addr[:], ip4)
if err = unix.Bind(fd, sockaddr); err != nil {
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("bind %s: %w", addr.String(), err)}
}
fdFile := os.NewFile(uintptr(fd), fmt.Sprintf("net-udp-bindany-%s", addr.String()))
if fdFile == nil {
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("convert descriptor to file")}
}
defer fdFile.Close()
packetConn, err := net.FilePacketConn(fdFile)
if err != nil {
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("convert descriptor to packet connection: %w", err)}
}
closeFD = false
return packetConn, nil
}
+2 -2
View File
@@ -1,5 +1,5 @@
//go:build !linux
// +build !linux
//go:build !linux && !openbsd
// +build !linux,!openbsd
package dokodemo
+33
View File
@@ -0,0 +1,33 @@
//go:build openbsd
// +build openbsd
package internet
import (
"github.com/xtls/xray-core/common/errors"
"golang.org/x/sys/unix"
)
func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error {
return nil
}
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)
}
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)
}
}
return nil
}
func setReuseAddr(fd uintptr) error {
return nil
}
func setReusePort(fd uintptr) error {
return nil
}
+2 -2
View File
@@ -1,5 +1,5 @@
//go:build js || netbsd || openbsd || solaris
// +build js netbsd openbsd solaris
//go:build js || netbsd || solaris
// +build js netbsd solaris
package internet
+57
View File
@@ -0,0 +1,57 @@
//go:build openbsd
// +build openbsd
package udp
import (
"encoding/binary"
"github.com/xtls/xray-core/common/net"
"golang.org/x/sys/unix"
)
func retrieveOriginalDestFromControlMessages(msgs []unix.SocketControlMessage) net.Destination {
var ip []byte
var port uint16
var haveAddress bool
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
}
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
}
}
if !haveAddress || !havePort || port == 0 {
return net.Destination{}
}
return net.UDPDestination(net.IPAddress(ip), net.Port(port))
}
func RetrieveOriginalDest(oob []byte) net.Destination {
msgs, err := unix.ParseSocketControlMessage(oob)
if err != nil {
return net.Destination{}
}
return retrieveOriginalDestFromControlMessages(msgs)
}
func ReadUDPMsg(conn *net.UDPConn, payload []byte, oob []byte) (int, int, int, *net.UDPAddr, error) {
return conn.ReadMsgUDP(payload, oob)
}
@@ -0,0 +1,57 @@
//go:build openbsd
// +build openbsd
package udp
import (
"testing"
"golang.org/x/sys/unix"
)
func TestRetrieveOriginalDestFromControlMessages(t *testing.T) {
msgs := []unix.SocketControlMessage{
{
Header: unix.Cmsghdr{Level: unix.IPPROTO_IP, Type: unix.IP_RECVDSTPORT},
Data: []byte{0x30, 0x39},
},
{
Header: unix.Cmsghdr{Level: unix.IPPROTO_IP, Type: unix.IP_RECVDSTADDR},
Data: []byte{203, 0, 113, 7},
},
}
dest := retrieveOriginalDestFromControlMessages(msgs)
if !dest.IsValid() {
t.Fatal("destination is invalid")
}
if got, want := dest.Address.String(), "203.0.113.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{
{
{
Header: unix.Cmsghdr{Level: unix.IPPROTO_IP, Type: unix.IP_RECVDSTADDR},
Data: []byte{203, 0, 113, 7},
},
},
{
{
Header: unix.Cmsghdr{Level: unix.IPPROTO_IP, Type: unix.IP_RECVDSTPORT},
Data: []byte{0x30, 0x39},
},
},
}
for _, msgs := range tests {
if dest := retrieveOriginalDestFromControlMessages(msgs); dest.IsValid() {
t.Fatalf("unexpected destination: %v", dest)
}
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
//go:build !linux && !freebsd && !darwin
// +build !linux,!freebsd,!darwin
//go:build !linux && !freebsd && !darwin && !openbsd
// +build !linux,!freebsd,!darwin,!openbsd
package udp