Browse Source

support udp redirection

v2ray 9 years ago
parent
commit
210a32dc12

+ 10 - 4
proxy/dokodemo/dokodemo.go

@@ -90,7 +90,7 @@ func (this *DokodemoDoor) Start() error {
 
 func (this *DokodemoDoor) ListenUDP() error {
 	this.udpServer = udp.NewUDPServer(this.meta, this.packetDispatcher)
-	udpHub, err := udp.ListenUDP(this.meta.Address, this.meta.Port, this.handleUDPPackets)
+	udpHub, err := udp.ListenUDP(this.meta.Address, this.meta.Port, udp.ListenOption{Callback: this.handleUDPPackets})
 	if err != nil {
 		log.Error("Dokodemo failed to listen on ", this.meta.Address, ":", this.meta.Port, ": ", err)
 		return err
@@ -101,9 +101,15 @@ func (this *DokodemoDoor) ListenUDP() error {
 	return nil
 }
 
-func (this *DokodemoDoor) handleUDPPackets(payload *alloc.Buffer, dest v2net.Destination) {
-	this.udpServer.Dispatch(
-		&proxy.SessionInfo{Source: dest, Destination: v2net.UDPDestination(this.address, this.port)}, payload, this.handleUDPResponse)
+func (this *DokodemoDoor) handleUDPPackets(payload *alloc.Buffer, session *proxy.SessionInfo) {
+	if session.Destination == nil && this.address != nil && this.port > 0 {
+		session.Destination = v2net.UDPDestination(this.address, this.port)
+	}
+	if session.Destination == nil {
+		log.Info("Dokodemo: Unknown destination, stop forwarding...")
+		return
+	}
+	this.udpServer.Dispatch(session, payload, this.handleUDPResponse)
 }
 
 func (this *DokodemoDoor) handleUDPResponse(dest v2net.Destination, payload *alloc.Buffer) {

+ 3 - 2
proxy/shadowsocks/server.go

@@ -71,7 +71,7 @@ func (this *Server) Start() error {
 
 	if this.config.UDP {
 		this.udpServer = udp.NewUDPServer(this.meta, this.packetDispatcher)
-		udpHub, err := udp.ListenUDP(this.meta.Address, this.meta.Port, this.handlerUDPPayload)
+		udpHub, err := udp.ListenUDP(this.meta.Address, this.meta.Port, udp.ListenOption{Callback: this.handlerUDPPayload})
 		if err != nil {
 			log.Error("Shadowsocks: Failed to listen UDP on ", this.meta.Address, ":", this.meta.Port, ": ", err)
 			return err
@@ -84,9 +84,10 @@ func (this *Server) Start() error {
 	return nil
 }
 
-func (this *Server) handlerUDPPayload(payload *alloc.Buffer, source v2net.Destination) {
+func (this *Server) handlerUDPPayload(payload *alloc.Buffer, session *proxy.SessionInfo) {
 	defer payload.Release()
 
+	source := session.Source
 	ivLen := this.config.Cipher.IVSize()
 	iv := payload.Value[:ivLen]
 	key := this.config.Key

+ 3 - 2
proxy/socks/server_udp.go

@@ -11,7 +11,7 @@ import (
 
 func (this *Server) listenUDP() error {
 	this.udpServer = udp.NewUDPServer(this.meta, this.packetDispatcher)
-	udpHub, err := udp.ListenUDP(this.meta.Address, this.meta.Port, this.handleUDPPayload)
+	udpHub, err := udp.ListenUDP(this.meta.Address, this.meta.Port, udp.ListenOption{Callback: this.handleUDPPayload})
 	if err != nil {
 		log.Error("Socks: Failed to listen on udp ", this.meta.Address, ":", this.meta.Port)
 		return err
@@ -23,7 +23,8 @@ func (this *Server) listenUDP() error {
 	return nil
 }
 
-func (this *Server) handleUDPPayload(payload *alloc.Buffer, source v2net.Destination) {
+func (this *Server) handleUDPPayload(payload *alloc.Buffer, session *proxy.SessionInfo) {
+	source := session.Source
 	log.Info("Socks: Client UDP connection from ", source)
 	request, err := protocol.ReadUDPRequest(payload.Value)
 	payload.Release()

+ 25 - 0
transport/internet/internal/sysfd.go

@@ -0,0 +1,25 @@
+package internal
+
+import (
+	"errors"
+	"net"
+	"reflect"
+)
+
+var (
+	ErrInvalidConn = errors.New("Invalid Connection.")
+)
+
+func GetSysFd(conn net.Conn) (int, error) {
+	cv := reflect.ValueOf(conn)
+	switch ce := cv.Elem(); ce.Kind() {
+	case reflect.Struct:
+		netfd := ce.FieldByName("conn").FieldByName("fd")
+		switch fe := netfd.Elem(); fe.Kind() {
+		case reflect.Struct:
+			fd := fe.FieldByName("sysfd")
+			return int(fd.Int()), nil
+		}
+	}
+	return 0, ErrInvalidConn
+}

+ 5 - 2
transport/internet/kcp/listener.go

@@ -9,6 +9,7 @@ import (
 	"github.com/v2ray/v2ray-core/common/log"
 	v2net "github.com/v2ray/v2ray-core/common/net"
 	"github.com/v2ray/v2ray-core/common/serial"
+	"github.com/v2ray/v2ray-core/proxy"
 	"github.com/v2ray/v2ray-core/transport/internet"
 	"github.com/v2ray/v2ray-core/transport/internet/udp"
 )
@@ -39,7 +40,7 @@ func NewListener(address v2net.Address, port v2net.Port) (*Listener, error) {
 		},
 		running: true,
 	}
-	hub, err := udp.ListenUDP(address, port, l.OnReceive)
+	hub, err := udp.ListenUDP(address, port, udp.ListenOption{Callback: l.OnReceive})
 	if err != nil {
 		return nil, err
 	}
@@ -48,9 +49,11 @@ func NewListener(address v2net.Address, port v2net.Port) (*Listener, error) {
 	return l, nil
 }
 
-func (this *Listener) OnReceive(payload *alloc.Buffer, src v2net.Destination) {
+func (this *Listener) OnReceive(payload *alloc.Buffer, session *proxy.SessionInfo) {
 	defer payload.Release()
 
+	src := session.Source
+
 	if valid := this.authenticator.Open(payload); !valid {
 		log.Info("KCP|Listener: discarding invalid payload from ", src)
 		return

+ 3 - 21
transport/internet/tcp/connection.go

@@ -1,15 +1,11 @@
 package tcp
 
 import (
-	"errors"
 	"io"
 	"net"
-	"reflect"
 	"time"
-)
 
-var (
-	ErrInvalidConn = errors.New("Invalid Connection.")
+	"github.com/v2ray/v2ray-core/transport/internet/internal"
 )
 
 type ConnectionManager interface {
@@ -27,7 +23,7 @@ func (this *RawConnection) Reusable() bool {
 func (this *RawConnection) SetReusable(b bool) {}
 
 func (this *RawConnection) SysFd() (int, error) {
-	return getSysFd(&this.TCPConn)
+	return internal.GetSysFd(&this.TCPConn)
 }
 
 type Connection struct {
@@ -106,19 +102,5 @@ func (this *Connection) Reusable() bool {
 }
 
 func (this *Connection) SysFd() (int, error) {
-	return getSysFd(this.conn)
-}
-
-func getSysFd(conn net.Conn) (int, error) {
-	cv := reflect.ValueOf(conn)
-	switch ce := cv.Elem(); ce.Kind() {
-	case reflect.Struct:
-		netfd := ce.FieldByName("conn").FieldByName("fd")
-		switch fe := netfd.Elem(); fe.Kind() {
-		case reflect.Struct:
-			fd := fe.FieldByName("sysfd")
-			return int(fd.Int()), nil
-		}
-	}
-	return 0, ErrInvalidConn
+	return internal.GetSysFd(this.conn)
 }

+ 34 - 0
transport/internet/udp/hub_linux.go

@@ -0,0 +1,34 @@
+// +build linux
+
+package udp
+
+import (
+	"syscall"
+
+	v2net "github.com/v2ray/v2ray-core/common/net"
+)
+
+func SetOriginalDestOptions(fd int) error {
+	if err := syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
+		return err
+	}
+	if err := syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_RECVORIGDSTADDR, 1); err != nil {
+		return err
+	}
+	return nil
+}
+
+func RetrieveOriginalDest(oob []byte) v2net.Destination {
+	msgs, err := syscall.ParseSocketControlMessage(oob)
+	if err != nil {
+		return nil
+	}
+	for _, msg := range msgs {
+		if msg.Header.Level == syscall.SOL_IP && msg.Header.Type == syscall.IP_ORIGDSTADDR {
+			ip := v2net.IPAddress(msg.Data[4:8])
+			port := v2net.PortFromBytes(msg.Data[2:4])
+			return v2net.UDPDestination(ip, port)
+		}
+	}
+	return nil
+}

+ 15 - 0
transport/internet/udp/hub_other.go

@@ -0,0 +1,15 @@
+// +build !linux
+
+package udp
+
+import (
+	v2net "github.com/v2ray/v2ray-core/common/net"
+)
+
+func SetOriginalDestOptions(fd int) error {
+	return nil
+}
+
+func RetrieveOriginalDest(oob []byte) v2net.Destination {
+	return nil
+}

+ 34 - 8
transport/internet/udp/udp.go

@@ -5,19 +5,27 @@ import (
 	"sync"
 
 	"github.com/v2ray/v2ray-core/common/alloc"
+	"github.com/v2ray/v2ray-core/common/log"
 	v2net "github.com/v2ray/v2ray-core/common/net"
+	"github.com/v2ray/v2ray-core/proxy"
+	"github.com/v2ray/v2ray-core/transport/internet/internal"
 )
 
-type UDPPayloadHandler func(*alloc.Buffer, v2net.Destination)
+type UDPPayloadHandler func(*alloc.Buffer, *proxy.SessionInfo)
 
 type UDPHub struct {
 	sync.RWMutex
 	conn      *net.UDPConn
-	callback  UDPPayloadHandler
+	option    ListenOption
 	accepting bool
 }
 
-func ListenUDP(address v2net.Address, port v2net.Port, callback UDPPayloadHandler) (*UDPHub, error) {
+type ListenOption struct {
+	Callback            UDPPayloadHandler
+	ReceiveOriginalDest bool
+}
+
+func ListenUDP(address v2net.Address, port v2net.Port, option ListenOption) (*UDPHub, error) {
 	udpConn, err := net.ListenUDP("udp", &net.UDPAddr{
 		IP:   address.IP(),
 		Port: int(port),
@@ -25,9 +33,21 @@ func ListenUDP(address v2net.Address, port v2net.Port, callback UDPPayloadHandle
 	if err != nil {
 		return nil, err
 	}
+	if option.ReceiveOriginalDest {
+		fd, err := internal.GetSysFd(udpConn)
+		if err != nil {
+			log.Warning("UDP|Listener: Failed to get fd: ", err)
+			return nil, err
+		}
+		err = SetOriginalDestOptions(fd)
+		if err != nil {
+			log.Warning("UDP|Listener: Failed to set socket options: ", err)
+			return nil, err
+		}
+	}
 	hub := &UDPHub{
-		conn:     udpConn,
-		callback: callback,
+		conn:   udpConn,
+		option: option,
 	}
 	go hub.start()
 	return hub, nil
@@ -53,16 +73,22 @@ func (this *UDPHub) start() {
 	this.accepting = true
 	this.Unlock()
 
+	oobBytes := make([]byte, 256)
 	for this.Running() {
 		buffer := alloc.NewBuffer()
-		nBytes, addr, err := this.conn.ReadFromUDP(buffer.Value)
+		nBytes, noob, _, addr, err := this.conn.ReadMsgUDP(buffer.Value, oobBytes)
 		if err != nil {
 			buffer.Release()
 			continue
 		}
 		buffer.Slice(0, nBytes)
-		dest := v2net.UDPDestination(v2net.IPAddress(addr.IP), v2net.Port(addr.Port))
-		go this.callback(buffer, dest)
+
+		session := new(proxy.SessionInfo)
+		session.Source = v2net.UDPDestination(v2net.IPAddress(addr.IP), v2net.Port(addr.Port))
+		if this.option.ReceiveOriginalDest && noob > 0 {
+			session.Destination = RetrieveOriginalDest(oobBytes[:noob])
+		}
+		go this.option.Callback(buffer, session)
 	}
 }