mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-07-15 09:28:45 +00:00
WireGuard outbound: Fix UDP FullCone NAT on Linux (#5858)
Fixes https://github.com/XTLS/Xray-core/issues/5848
This commit is contained in:
@@ -370,6 +370,18 @@ func (c *udpConnClient) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
return buf.MultiBuffer{b}, nil
|
return buf.MultiBuffer{b}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *udpConnClient) Write(p []byte) (int, error) {
|
func (c *udpConnClient) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||||
return c.Conn.(net.PacketConn).WriteTo(p, c.dest.RawNetAddr())
|
for i, b := range mb {
|
||||||
|
dst := c.dest
|
||||||
|
if b.UDP != nil {
|
||||||
|
dst = *b.UDP
|
||||||
|
}
|
||||||
|
_, err := c.Conn.(net.PacketConn).WriteTo(b.Bytes(), dst.RawNetAddr())
|
||||||
|
if err != nil {
|
||||||
|
buf.ReleaseMulti(mb[i:])
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.Release()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+55
-14
@@ -189,8 +189,14 @@ func createGVisorTun(localAddresses []netip.Addr, mtu int, handler promiscuousMo
|
|||||||
// if len(data) == 0 {
|
// if len(data) == 0 {
|
||||||
// return false
|
// return false
|
||||||
// }
|
// }
|
||||||
src := net.UDPDestination(net.IPAddress(id.RemoteAddress.AsSlice()), net.Port(id.RemotePort))
|
srcIP := net.IPAddress(id.RemoteAddress.AsSlice())
|
||||||
dst := net.UDPDestination(net.IPAddress(id.LocalAddress.AsSlice()), net.Port(id.LocalPort))
|
dstIP := net.IPAddress(id.LocalAddress.AsSlice())
|
||||||
|
if srcIP == nil || dstIP == nil {
|
||||||
|
errors.LogDebug(context.Background(), "drop udp with size ", len(data), " > invalid ip address ", id.RemoteAddress.AsSlice(), " ", id.LocalAddress.AsSlice())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
src := net.UDPDestination(srcIP, net.Port(id.RemotePort))
|
||||||
|
dst := net.UDPDestination(dstIP, net.Port(id.LocalPort))
|
||||||
manager.feed(src, dst, data)
|
manager.feed(src, dst, data)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
@@ -212,8 +218,12 @@ func (m *udpManager) feed(src net.Destination, dst net.Destination, data []byte)
|
|||||||
uc, ok := m.m[src.NetAddr()]
|
uc, ok := m.m[src.NetAddr()]
|
||||||
if ok {
|
if ok {
|
||||||
select {
|
select {
|
||||||
case uc.ch <- data:
|
case uc.queue <- &packet{
|
||||||
|
p: data,
|
||||||
|
dest: &dst,
|
||||||
|
}:
|
||||||
default:
|
default:
|
||||||
|
errors.LogDebug(context.Background(), "drop udp with size ", len(data), " to ", dst.NetAddr(), " original ", uc.dst.NetAddr(), " > queue full")
|
||||||
}
|
}
|
||||||
m.mutex.RUnlock()
|
m.mutex.RUnlock()
|
||||||
return
|
return
|
||||||
@@ -226,9 +236,9 @@ func (m *udpManager) feed(src net.Destination, dst net.Destination, data []byte)
|
|||||||
uc, ok = m.m[src.NetAddr()]
|
uc, ok = m.m[src.NetAddr()]
|
||||||
if !ok {
|
if !ok {
|
||||||
uc = &udpConn{
|
uc = &udpConn{
|
||||||
ch: make(chan []byte, 1024),
|
queue: make(chan *packet, 1024),
|
||||||
src: src,
|
src: src,
|
||||||
dst: dst,
|
dst: dst,
|
||||||
}
|
}
|
||||||
uc.writeFunc = m.writeRawUDPPacket
|
uc.writeFunc = m.writeRawUDPPacket
|
||||||
uc.closeFunc = func() {
|
uc.closeFunc = func() {
|
||||||
@@ -241,15 +251,19 @@ func (m *udpManager) feed(src net.Destination, dst net.Destination, data []byte)
|
|||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case uc.ch <- data:
|
case uc.queue <- &packet{
|
||||||
|
p: data,
|
||||||
|
dest: &dst,
|
||||||
|
}:
|
||||||
default:
|
default:
|
||||||
|
errors.LogDebug(context.Background(), "drop udp with size ", len(data), " to ", dst.NetAddr(), " original ", uc.dst.NetAddr(), " > queue full")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *udpManager) close(uc *udpConn) {
|
func (m *udpManager) close(uc *udpConn) {
|
||||||
if !uc.closed {
|
if !uc.closed {
|
||||||
uc.closed = true
|
uc.closed = true
|
||||||
close(uc.ch)
|
close(uc.queue)
|
||||||
delete(m.m, uc.src.NetAddr())
|
delete(m.m, uc.src.NetAddr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,8 +331,13 @@ func (m *udpManager) writeRawUDPPacket(payload []byte, src net.Destination, dst
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type packet struct {
|
||||||
|
p []byte
|
||||||
|
dest *net.Destination
|
||||||
|
}
|
||||||
|
|
||||||
type udpConn struct {
|
type udpConn struct {
|
||||||
ch chan []byte
|
queue chan *packet
|
||||||
src net.Destination
|
src net.Destination
|
||||||
dst net.Destination
|
dst net.Destination
|
||||||
writeFunc func(payload []byte, src net.Destination, dst net.Destination) error
|
writeFunc func(payload []byte, src net.Destination, dst net.Destination) error
|
||||||
@@ -326,13 +345,35 @@ type udpConn struct {
|
|||||||
closed bool
|
closed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *udpConn) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||||
|
for {
|
||||||
|
q, ok := <-c.queue
|
||||||
|
if !ok {
|
||||||
|
return nil, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
b := buf.New()
|
||||||
|
|
||||||
|
_, err := b.Write(q.p)
|
||||||
|
if err != nil {
|
||||||
|
errors.LogDebugInner(context.Background(), err, "drop udp with size ", len(q.p), " to ", q.dest.NetAddr(), " original ", c.dst.NetAddr())
|
||||||
|
b.Release()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
b.UDP = q.dest
|
||||||
|
|
||||||
|
return buf.MultiBuffer{b}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *udpConn) Read(p []byte) (int, error) {
|
func (c *udpConn) Read(p []byte) (int, error) {
|
||||||
b, ok := <-c.ch
|
q, ok := <-c.queue
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
n := copy(p, b)
|
n := copy(p, q.p)
|
||||||
if n != len(b) {
|
if n != len(q.p) {
|
||||||
return 0, io.ErrShortBuffer
|
return 0, io.ErrShortBuffer
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
@@ -368,11 +409,11 @@ func (c *udpConn) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *udpConn) LocalAddr() net.Addr {
|
func (c *udpConn) LocalAddr() net.Addr {
|
||||||
return c.src.RawNetAddr() // fake
|
return c.dst.RawNetAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *udpConn) RemoteAddr() net.Addr {
|
func (c *udpConn) RemoteAddr() net.Addr {
|
||||||
return c.src.RawNetAddr() // src
|
return c.src.RawNetAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *udpConn) SetDeadline(t time.Time) error {
|
func (c *udpConn) SetDeadline(t time.Time) error {
|
||||||
|
|||||||
@@ -10,18 +10,20 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/control"
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
wgtun "golang.zx2c4.com/wireguard/tun"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type deviceNet struct {
|
type deviceNet struct {
|
||||||
tunnel
|
tunnel
|
||||||
dialer net.Dialer
|
dialer *net.Dialer
|
||||||
|
lc *net.ListenConfig
|
||||||
|
|
||||||
handle *netlink.Handle
|
handle *netlink.Handle
|
||||||
linkAddrs []netlink.Addr
|
linkAddrs []netlink.Addr
|
||||||
@@ -47,10 +49,23 @@ func allocateIPv6TableIndex() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newDeviceNet(interfaceName string) *deviceNet {
|
func newDeviceNet(interfaceName string) *deviceNet {
|
||||||
var dialer net.Dialer
|
dialer := &net.Dialer{}
|
||||||
bindControl := control.BindToInterface(control.NewDefaultInterfaceFinder(), interfaceName, -1)
|
dialer.Control = func(network, address string, c syscall.RawConn) error {
|
||||||
dialer.Control = control.Append(dialer.Control, bindControl)
|
return c.Control(func(fd uintptr) {
|
||||||
return &deviceNet{dialer: dialer}
|
if err := syscall.BindToDevice(int(fd), interfaceName); err != nil {
|
||||||
|
errors.LogInfoInner(context.Background(), err, "failed to bind to device")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
lc := &net.ListenConfig{}
|
||||||
|
lc.Control = func(network, address string, c syscall.RawConn) error {
|
||||||
|
return c.Control(func(fd uintptr) {
|
||||||
|
if err := syscall.BindToDevice(int(fd), interfaceName); err != nil {
|
||||||
|
errors.LogInfoInner(context.Background(), err, "failed to bind to device")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return &deviceNet{dialer: dialer, lc: lc}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *deviceNet) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) (
|
func (d *deviceNet) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) (
|
||||||
@@ -60,9 +75,23 @@ func (d *deviceNet) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrP
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *deviceNet) DialUDPAddrPort(laddr, raddr netip.AddrPort) (net.Conn, error) {
|
func (d *deviceNet) DialUDPAddrPort(laddr, raddr netip.AddrPort) (net.Conn, error) {
|
||||||
dialer := d.dialer
|
var conn net.PacketConn
|
||||||
dialer.LocalAddr = &net.UDPAddr{IP: laddr.Addr().AsSlice(), Port: int(laddr.Port())}
|
var err error
|
||||||
return dialer.DialContext(context.Background(), "udp", raddr.String())
|
if raddr.Addr().Is4() {
|
||||||
|
conn, err = d.lc.ListenPacket(context.Background(), "udp4", ":0")
|
||||||
|
} else {
|
||||||
|
conn, err = d.lc.ListenPacket(context.Background(), "udp6", ":0")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &internet.PacketConnWrapper{
|
||||||
|
PacketConn: conn,
|
||||||
|
Dest: &net.UDPAddr{
|
||||||
|
IP: raddr.Addr().AsSlice(),
|
||||||
|
Port: int(raddr.Port()),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *deviceNet) Close() (err error) {
|
func (d *deviceNet) Close() (err error) {
|
||||||
@@ -134,7 +163,7 @@ func createKernelTun(localAddresses []netip.Addr, mtu int, handler promiscuousMo
|
|||||||
}
|
}
|
||||||
|
|
||||||
n := CalculateInterfaceName("wg")
|
n := CalculateInterfaceName("wg")
|
||||||
wgt, err := wgtun.CreateTUN(n, mtu)
|
wgt, err := tun.CreateTUN(n, mtu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/control"
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/features/dns"
|
"github.com/xtls/xray-core/features/dns"
|
||||||
@@ -20,7 +19,7 @@ type SystemDialer interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DefaultSystemDialer struct {
|
type DefaultSystemDialer struct {
|
||||||
controllers []control.Func
|
controllers []func(network, address string, c syscall.RawConn) error
|
||||||
dns dns.Client
|
dns dns.Client
|
||||||
obm outbound.Manager
|
obm outbound.Manager
|
||||||
}
|
}
|
||||||
@@ -204,7 +203,7 @@ func UseAlternativeSystemDialer(dialer SystemDialer) {
|
|||||||
// It only works when effective dialer is the default dialer.
|
// It only works when effective dialer is the default dialer.
|
||||||
//
|
//
|
||||||
// xray:api:beta
|
// xray:api:beta
|
||||||
func RegisterDialerController(ctl control.Func) error {
|
func RegisterDialerController(ctl func(network, address string, c syscall.RawConn) error) error {
|
||||||
if ctl == nil {
|
if ctl == nil {
|
||||||
return errors.New("nil listener controller")
|
return errors.New("nil listener controller")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pires/go-proxyproto"
|
"github.com/pires/go-proxyproto"
|
||||||
"github.com/sagernet/sing/common/control"
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
)
|
)
|
||||||
@@ -18,10 +17,10 @@ import (
|
|||||||
var effectiveListener = DefaultListener{}
|
var effectiveListener = DefaultListener{}
|
||||||
|
|
||||||
type DefaultListener struct {
|
type DefaultListener struct {
|
||||||
controllers []control.Func
|
controllers []func(network, address string, c syscall.RawConn) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []control.Func) func(network, address string, c syscall.RawConn) error {
|
func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []func(network, address string, c syscall.RawConn) error) func(network, address string, c syscall.RawConn) error {
|
||||||
return func(network, address string, c syscall.RawConn) error {
|
return func(network, address string, c syscall.RawConn) error {
|
||||||
return c.Control(func(fd uintptr) {
|
return c.Control(func(fd uintptr) {
|
||||||
for _, controller := range controllers {
|
for _, controller := range controllers {
|
||||||
@@ -186,7 +185,7 @@ func (dl *DefaultListener) ListenPacket(ctx context.Context, addr net.Addr, sock
|
|||||||
// The controller can be used to operate on file descriptors before they are put into use.
|
// The controller can be used to operate on file descriptors before they are put into use.
|
||||||
//
|
//
|
||||||
// xray:api:beta
|
// xray:api:beta
|
||||||
func RegisterListenerController(controller control.Func) error {
|
func RegisterListenerController(controller func(network, address string, c syscall.RawConn) error) error {
|
||||||
if controller == nil {
|
if controller == nil {
|
||||||
return errors.New("nil listener controller")
|
return errors.New("nil listener controller")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/control"
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
)
|
)
|
||||||
@@ -14,10 +13,9 @@ import (
|
|||||||
func TestRegisterListenerController(t *testing.T) {
|
func TestRegisterListenerController(t *testing.T) {
|
||||||
var gotFd uintptr
|
var gotFd uintptr
|
||||||
|
|
||||||
common.Must(internet.RegisterListenerController(func(network, address string, conn syscall.RawConn) error {
|
common.Must(internet.RegisterListenerController(func(network, address string, c syscall.RawConn) error {
|
||||||
return control.Raw(conn, func(fd uintptr) error {
|
return c.Control(func(fd uintptr) {
|
||||||
gotFd = fd
|
gotFd = fd
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user