2022-11-22 09:05:54 +08:00
|
|
|
package wireguard
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
2026-03-23 01:42:40 +08:00
|
|
|
"io"
|
2023-11-12 15:10:01 -05:00
|
|
|
"runtime"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
2023-11-18 11:27:17 +08:00
|
|
|
"time"
|
2022-11-22 09:05:54 +08:00
|
|
|
|
2026-03-23 01:42:40 +08:00
|
|
|
"github.com/xtls/xray-core/common/buf"
|
2024-06-29 14:32:57 -04:00
|
|
|
"github.com/xtls/xray-core/common/errors"
|
2025-12-10 15:17:29 +08:00
|
|
|
"github.com/xtls/xray-core/common/net"
|
2026-03-23 01:42:40 +08:00
|
|
|
"gvisor.dev/gvisor/pkg/buffer"
|
2023-11-18 11:27:17 +08:00
|
|
|
"gvisor.dev/gvisor/pkg/tcpip"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
2026-03-23 01:42:40 +08:00
|
|
|
"gvisor.dev/gvisor/pkg/tcpip/checksum"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
2023-11-18 11:27:17 +08:00
|
|
|
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
|
|
|
|
"gvisor.dev/gvisor/pkg/waiter"
|
2023-11-12 15:10:01 -05:00
|
|
|
)
|
2022-11-22 09:05:54 +08:00
|
|
|
|
2023-11-12 15:10:01 -05:00
|
|
|
func CalculateInterfaceName(name string) (tunName string) {
|
|
|
|
|
if runtime.GOOS == "darwin" {
|
|
|
|
|
tunName = "utun"
|
|
|
|
|
} else if name != "" {
|
|
|
|
|
tunName = name
|
2022-11-22 09:05:54 +08:00
|
|
|
} else {
|
2023-11-12 15:10:01 -05:00
|
|
|
tunName = "tun"
|
2022-11-22 09:05:54 +08:00
|
|
|
}
|
2023-11-12 15:10:01 -05:00
|
|
|
interfaces, err := net.Interfaces()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
2022-11-22 09:05:54 +08:00
|
|
|
}
|
2023-11-12 15:10:01 -05:00
|
|
|
var tunIndex int
|
|
|
|
|
for _, netInterface := range interfaces {
|
|
|
|
|
if strings.HasPrefix(netInterface.Name, tunName) {
|
|
|
|
|
index, parseErr := strconv.ParseInt(netInterface.Name[len(tunName):], 10, 16)
|
|
|
|
|
if parseErr == nil {
|
|
|
|
|
tunIndex = int(index) + 1
|
2022-11-22 09:05:54 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-12 15:10:01 -05:00
|
|
|
tunName = fmt.Sprintf("%s%d", tunName, tunIndex)
|
|
|
|
|
return
|
2022-11-22 09:05:54 +08:00
|
|
|
}
|
2023-11-18 11:27:17 +08:00
|
|
|
|
2026-06-16 23:24:39 +08:00
|
|
|
func createForwarder(gstack *stack.Stack, handler func(conn net.Conn, dest net.Destination)) {
|
|
|
|
|
gstack.SetPromiscuousMode(1, true)
|
|
|
|
|
gstack.SetSpoofing(1, true)
|
2023-11-18 11:27:17 +08:00
|
|
|
|
2026-06-16 23:24:39 +08:00
|
|
|
tcpForwarder := tcp.NewForwarder(gstack, 0, 65535, func(r *tcp.ForwarderRequest) {
|
|
|
|
|
go func(r *tcp.ForwarderRequest) {
|
|
|
|
|
var wq waiter.Queue
|
|
|
|
|
id := r.ID()
|
2023-11-18 11:27:17 +08:00
|
|
|
|
2026-06-16 23:24:39 +08:00
|
|
|
ep, err := r.CreateEndpoint(&wq)
|
|
|
|
|
if err != nil {
|
|
|
|
|
errors.LogError(context.Background(), err.String())
|
|
|
|
|
r.Complete(true)
|
|
|
|
|
return
|
|
|
|
|
}
|
2023-11-18 11:27:17 +08:00
|
|
|
|
2026-06-16 23:24:39 +08:00
|
|
|
options := ep.SocketOptions()
|
|
|
|
|
options.SetKeepAlive(false)
|
|
|
|
|
options.SetReuseAddress(true)
|
|
|
|
|
options.SetReusePort(true)
|
2026-03-23 01:42:40 +08:00
|
|
|
|
2026-06-16 23:24:39 +08:00
|
|
|
handler(gonet.NewTCPConn(&wq, ep), net.TCPDestination(net.IPAddress(id.LocalAddress.AsSlice()), net.Port(id.LocalPort)))
|
2023-11-18 11:27:17 +08:00
|
|
|
|
2026-06-16 23:24:39 +08:00
|
|
|
ep.Close()
|
|
|
|
|
r.Complete(false)
|
|
|
|
|
}(r)
|
|
|
|
|
})
|
|
|
|
|
gstack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
|
2026-01-12 19:18:02 +01:00
|
|
|
|
2026-06-16 23:24:39 +08:00
|
|
|
manager := &udpManager{
|
|
|
|
|
stack: gstack,
|
|
|
|
|
handler: handler,
|
|
|
|
|
m: make(map[string]*udpConn),
|
2023-11-18 11:27:17 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-16 23:24:39 +08:00
|
|
|
gstack.SetTransportProtocolHandler(udp.ProtocolNumber, func(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
|
|
|
|
|
data := pkt.Clone().Data().AsRange().ToSlice()
|
|
|
|
|
// if len(data) == 0 {
|
|
|
|
|
// return false
|
|
|
|
|
// }
|
|
|
|
|
srcIP := net.IPAddress(id.RemoteAddress.AsSlice())
|
|
|
|
|
dstIP := net.IPAddress(id.LocalAddress.AsSlice())
|
|
|
|
|
if srcIP == nil || dstIP == nil {
|
|
|
|
|
panic(id)
|
|
|
|
|
}
|
|
|
|
|
src := net.UDPDestination(srcIP, net.Port(id.RemotePort))
|
|
|
|
|
dst := net.UDPDestination(dstIP, net.Port(id.LocalPort))
|
|
|
|
|
manager.feed(src, dst, data)
|
|
|
|
|
return true
|
|
|
|
|
})
|
2023-11-18 11:27:17 +08:00
|
|
|
}
|
2026-03-23 01:42:40 +08:00
|
|
|
|
|
|
|
|
type udpManager struct {
|
|
|
|
|
stack *stack.Stack
|
2026-06-16 23:24:39 +08:00
|
|
|
handler func(conn net.Conn, dest net.Destination)
|
2026-03-23 01:42:40 +08:00
|
|
|
m map[string]*udpConn
|
|
|
|
|
mutex sync.RWMutex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *udpManager) feed(src net.Destination, dst net.Destination, data []byte) {
|
|
|
|
|
m.mutex.RLock()
|
|
|
|
|
uc, ok := m.m[src.NetAddr()]
|
|
|
|
|
if ok {
|
|
|
|
|
select {
|
2026-04-05 20:57:08 +08:00
|
|
|
case uc.queue <- &packet{
|
|
|
|
|
p: data,
|
|
|
|
|
dest: &dst,
|
|
|
|
|
}:
|
2026-03-23 01:42:40 +08:00
|
|
|
default:
|
2026-04-05 20:57:08 +08:00
|
|
|
errors.LogDebug(context.Background(), "drop udp with size ", len(data), " to ", dst.NetAddr(), " original ", uc.dst.NetAddr(), " > queue full")
|
2026-03-23 01:42:40 +08:00
|
|
|
}
|
|
|
|
|
m.mutex.RUnlock()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
m.mutex.RUnlock()
|
|
|
|
|
|
|
|
|
|
m.mutex.Lock()
|
|
|
|
|
defer m.mutex.Unlock()
|
|
|
|
|
|
|
|
|
|
uc, ok = m.m[src.NetAddr()]
|
|
|
|
|
if !ok {
|
|
|
|
|
uc = &udpConn{
|
2026-04-05 20:57:08 +08:00
|
|
|
queue: make(chan *packet, 1024),
|
|
|
|
|
src: src,
|
|
|
|
|
dst: dst,
|
2026-03-23 01:42:40 +08:00
|
|
|
}
|
|
|
|
|
uc.writeFunc = m.writeRawUDPPacket
|
|
|
|
|
uc.closeFunc = func() {
|
|
|
|
|
m.mutex.Lock()
|
|
|
|
|
m.close(uc)
|
|
|
|
|
m.mutex.Unlock()
|
|
|
|
|
}
|
|
|
|
|
m.m[src.NetAddr()] = uc
|
2026-06-16 23:24:39 +08:00
|
|
|
go m.handler(uc, dst)
|
2026-03-23 01:42:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
select {
|
2026-04-05 20:57:08 +08:00
|
|
|
case uc.queue <- &packet{
|
|
|
|
|
p: data,
|
|
|
|
|
dest: &dst,
|
|
|
|
|
}:
|
2026-03-23 01:42:40 +08:00
|
|
|
default:
|
2026-04-15 20:11:51 +08:00
|
|
|
errors.LogDebug(context.Background(), "drop udp with size ", len(data), " to ", dst.NetAddr(), " original ", uc.dst.NetAddr(), " > queue full 2")
|
2026-03-23 01:42:40 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *udpManager) close(uc *udpConn) {
|
|
|
|
|
if !uc.closed {
|
|
|
|
|
uc.closed = true
|
2026-04-05 20:57:08 +08:00
|
|
|
close(uc.queue)
|
2026-03-23 01:42:40 +08:00
|
|
|
delete(m.m, uc.src.NetAddr())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *udpManager) writeRawUDPPacket(payload []byte, src net.Destination, dst net.Destination) error {
|
|
|
|
|
udpLen := header.UDPMinimumSize + len(payload)
|
|
|
|
|
srcIP := tcpip.AddrFromSlice(src.Address.IP())
|
|
|
|
|
dstIP := tcpip.AddrFromSlice(dst.Address.IP())
|
|
|
|
|
|
|
|
|
|
// build packet with appropriate IP header size
|
|
|
|
|
isIPv4 := dst.Address.Family().IsIPv4()
|
|
|
|
|
ipHdrSize := header.IPv6MinimumSize
|
|
|
|
|
ipProtocol := header.IPv6ProtocolNumber
|
|
|
|
|
if isIPv4 {
|
|
|
|
|
ipHdrSize = header.IPv4MinimumSize
|
|
|
|
|
ipProtocol = header.IPv4ProtocolNumber
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
|
|
|
|
ReserveHeaderBytes: ipHdrSize + header.UDPMinimumSize,
|
|
|
|
|
Payload: buffer.MakeWithData(payload),
|
|
|
|
|
})
|
|
|
|
|
defer pkt.DecRef()
|
|
|
|
|
|
|
|
|
|
// Build UDP header
|
|
|
|
|
udpHdr := header.UDP(pkt.TransportHeader().Push(header.UDPMinimumSize))
|
|
|
|
|
udpHdr.Encode(&header.UDPFields{
|
|
|
|
|
SrcPort: uint16(src.Port),
|
|
|
|
|
DstPort: uint16(dst.Port),
|
|
|
|
|
Length: uint16(udpLen),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Calculate and set UDP checksum
|
|
|
|
|
xsum := header.PseudoHeaderChecksum(header.UDPProtocolNumber, srcIP, dstIP, uint16(udpLen))
|
|
|
|
|
udpHdr.SetChecksum(^udpHdr.CalculateChecksum(checksum.Checksum(payload, xsum)))
|
|
|
|
|
|
|
|
|
|
// Build IP header
|
|
|
|
|
if isIPv4 {
|
|
|
|
|
ipHdr := header.IPv4(pkt.NetworkHeader().Push(header.IPv4MinimumSize))
|
|
|
|
|
ipHdr.Encode(&header.IPv4Fields{
|
|
|
|
|
TotalLength: uint16(header.IPv4MinimumSize + udpLen),
|
|
|
|
|
TTL: 64,
|
|
|
|
|
Protocol: uint8(header.UDPProtocolNumber),
|
|
|
|
|
SrcAddr: srcIP,
|
|
|
|
|
DstAddr: dstIP,
|
|
|
|
|
})
|
|
|
|
|
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
|
|
|
|
|
} else {
|
|
|
|
|
ipHdr := header.IPv6(pkt.NetworkHeader().Push(header.IPv6MinimumSize))
|
|
|
|
|
ipHdr.Encode(&header.IPv6Fields{
|
|
|
|
|
PayloadLength: uint16(udpLen),
|
|
|
|
|
TransportProtocol: header.UDPProtocolNumber,
|
|
|
|
|
HopLimit: 64,
|
|
|
|
|
SrcAddr: srcIP,
|
|
|
|
|
DstAddr: dstIP,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// dispatch the packet
|
|
|
|
|
err := m.stack.WriteRawPacket(1, ipProtocol, buffer.MakeWithView(pkt.ToView()))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.New("failed to write raw udp packet back to stack err ", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 20:57:08 +08:00
|
|
|
type packet struct {
|
|
|
|
|
p []byte
|
|
|
|
|
dest *net.Destination
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-23 01:42:40 +08:00
|
|
|
type udpConn struct {
|
2026-04-05 20:57:08 +08:00
|
|
|
queue chan *packet
|
2026-03-23 01:42:40 +08:00
|
|
|
src net.Destination
|
|
|
|
|
dst net.Destination
|
|
|
|
|
writeFunc func(payload []byte, src net.Destination, dst net.Destination) error
|
|
|
|
|
closeFunc func()
|
|
|
|
|
closed bool
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 20:57:08 +08:00
|
|
|
func (c *udpConn) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
2026-06-10 04:53:55 +08:00
|
|
|
for {
|
|
|
|
|
q, ok := <-c.queue
|
|
|
|
|
if !ok {
|
|
|
|
|
return nil, io.EOF
|
|
|
|
|
}
|
2026-04-05 20:57:08 +08:00
|
|
|
|
2026-06-10 04:53:55 +08:00
|
|
|
b := buf.New()
|
|
|
|
|
if _, err := b.Write(q.p); err != nil {
|
|
|
|
|
errors.LogErrorInner(context.Background(), err, "drop packet to ", q.dest, " with size ", len(q.p))
|
|
|
|
|
b.Release()
|
|
|
|
|
continue
|
|
|
|
|
}
|
2026-04-16 00:57:51 +08:00
|
|
|
|
2026-06-10 04:53:55 +08:00
|
|
|
b.UDP = q.dest
|
2026-04-05 20:57:08 +08:00
|
|
|
|
2026-06-10 04:53:55 +08:00
|
|
|
return buf.MultiBuffer{b}, nil
|
|
|
|
|
}
|
2026-04-05 20:57:08 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-23 01:42:40 +08:00
|
|
|
func (c *udpConn) Read(p []byte) (int, error) {
|
2026-04-05 20:57:08 +08:00
|
|
|
q, ok := <-c.queue
|
2026-03-23 01:42:40 +08:00
|
|
|
if !ok {
|
|
|
|
|
return 0, io.EOF
|
|
|
|
|
}
|
2026-04-05 20:57:08 +08:00
|
|
|
n := copy(p, q.p)
|
|
|
|
|
if n != len(q.p) {
|
2026-03-23 01:42:40 +08:00
|
|
|
return 0, io.ErrShortBuffer
|
|
|
|
|
}
|
|
|
|
|
return n, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *udpConn) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|
|
|
|
for i, b := range mb {
|
|
|
|
|
dst := c.dst
|
|
|
|
|
if b.UDP != nil {
|
2026-06-10 04:53:55 +08:00
|
|
|
if b.UDP.Address.Family().IsDomain() {
|
|
|
|
|
errors.LogError(context.Background(), "impossible domain packet ", b.UDP, " reply via original target ", dst)
|
|
|
|
|
} else {
|
|
|
|
|
dst = *b.UDP
|
|
|
|
|
}
|
2026-03-23 01:42:40 +08:00
|
|
|
}
|
|
|
|
|
err := c.writeFunc(b.Bytes(), dst, c.src)
|
|
|
|
|
if err != nil {
|
|
|
|
|
buf.ReleaseMulti(mb[i:])
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
b.Release()
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *udpConn) Write(p []byte) (int, error) {
|
|
|
|
|
err := c.writeFunc(p, c.dst, c.src)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
return len(p), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *udpConn) Close() error {
|
|
|
|
|
c.closeFunc()
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *udpConn) LocalAddr() net.Addr {
|
2026-04-05 20:57:08 +08:00
|
|
|
return c.dst.RawNetAddr()
|
2026-03-23 01:42:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *udpConn) RemoteAddr() net.Addr {
|
2026-04-05 20:57:08 +08:00
|
|
|
return c.src.RawNetAddr()
|
2026-03-23 01:42:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *udpConn) SetDeadline(t time.Time) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *udpConn) SetReadDeadline(t time.Time) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *udpConn) SetWriteDeadline(t time.Time) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|