Chore: update tproxy udp packet read logic
This commit is contained in:
parent
06d75da257
commit
2fa1a5c4b9
6 changed files with 71 additions and 53 deletions
|
@ -2,12 +2,13 @@ package tproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
type packet struct {
|
type packet struct {
|
||||||
lAddr *net.UDPAddr
|
lAddr netip.AddrPort
|
||||||
buf []byte
|
buf []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ func (c *packet) Data() []byte {
|
||||||
|
|
||||||
// WriteBack opens a new socket binding `addr` to write UDP packet back
|
// WriteBack opens a new socket binding `addr` to write UDP packet back
|
||||||
func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||||
tc, err := dialUDP("udp", addr.(*net.UDPAddr), c.lAddr)
|
tc, err := dialUDP("udp", addr.(*net.UDPAddr).AddrPort(), c.lAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n = 0
|
n = 0
|
||||||
return
|
return
|
||||||
|
@ -29,7 +30,7 @@ func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||||
|
|
||||||
// LocalAddr returns the source IP/Port of UDP Packet
|
// LocalAddr returns the source IP/Port of UDP Packet
|
||||||
func (c *packet) LocalAddr() net.Addr {
|
func (c *packet) LocalAddr() net.Addr {
|
||||||
return c.lAddr
|
return &net.UDPAddr{IP: c.lAddr.Addr().AsSlice(), Port: int(c.lAddr.Port()), Zone: c.lAddr.Addr().Zone()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *packet) Drop() {
|
func (c *packet) Drop() {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package tproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/inbound"
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
|
@ -58,7 +59,7 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error)
|
||||||
oob := make([]byte, 1024)
|
oob := make([]byte, 1024)
|
||||||
for {
|
for {
|
||||||
buf := pool.Get(pool.UDPBufferSize)
|
buf := pool.Get(pool.UDPBufferSize)
|
||||||
n, oobn, _, lAddr, err := c.ReadMsgUDP(buf, oob)
|
n, oobn, _, lAddr, err := c.ReadMsgUDPAddrPort(buf, oob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pool.Put(buf)
|
pool.Put(buf)
|
||||||
if rl.closed {
|
if rl.closed {
|
||||||
|
@ -67,19 +68,19 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rAddr, err := getOrigDst(oob, oobn)
|
rAddr, err := getOrigDst(oob[:oobn])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
handlePacketConn(l, in, buf[:n], lAddr, rAddr)
|
handlePacketConn(in, buf[:n], lAddr, rAddr)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return rl, nil
|
return rl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePacketConn(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, lAddr *net.UDPAddr, rAddr *net.UDPAddr) {
|
func handlePacketConn(in chan<- *inbound.PacketAdapter, buf []byte, lAddr, rAddr netip.AddrPort) {
|
||||||
target := socks5.ParseAddrToSocksAddr(rAddr)
|
target := socks5.AddrFromStdAddrPort(rAddr)
|
||||||
pkt := &packet{
|
pkt := &packet{
|
||||||
lAddr: lAddr,
|
lAddr: lAddr,
|
||||||
buf: buf,
|
buf: buf,
|
||||||
|
|
|
@ -3,13 +3,14 @@
|
||||||
package tproxy
|
package tproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -19,7 +20,7 @@ const (
|
||||||
|
|
||||||
// dialUDP acts like net.DialUDP for transparent proxy.
|
// dialUDP acts like net.DialUDP for transparent proxy.
|
||||||
// It binds to a non-local address(`lAddr`).
|
// It binds to a non-local address(`lAddr`).
|
||||||
func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
|
func dialUDP(network string, lAddr, rAddr netip.AddrPort) (uc *net.UDPConn, err error) {
|
||||||
rSockAddr, err := udpAddrToSockAddr(rAddr)
|
rSockAddr, err := udpAddrToSockAddr(rAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -35,23 +36,25 @@ func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPCo
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(fd)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
|
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
|
||||||
syscall.Close(fd)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
|
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
|
||||||
syscall.Close(fd)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = syscall.Bind(fd, lSockAddr); err != nil {
|
if err = syscall.Bind(fd, lSockAddr); err != nil {
|
||||||
syscall.Close(fd)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = syscall.Connect(fd, rSockAddr); err != nil {
|
if err = syscall.Connect(fd, rSockAddr); err != nil {
|
||||||
syscall.Close(fd)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,35 +63,26 @@ func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPCo
|
||||||
|
|
||||||
c, err := net.FileConn(fdFile)
|
c, err := net.FileConn(fdFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(fd)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.(*net.UDPConn), nil
|
return c.(*net.UDPConn), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func udpAddrToSockAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) {
|
func udpAddrToSockAddr(addr netip.AddrPort) (syscall.Sockaddr, error) {
|
||||||
switch {
|
if addr.Addr().Is4() {
|
||||||
case addr.IP.To4() != nil:
|
return &syscall.SockaddrInet4{Addr: addr.Addr().As4(), Port: int(addr.Port())}, nil
|
||||||
ip := [4]byte{}
|
|
||||||
copy(ip[:], addr.IP.To4())
|
|
||||||
|
|
||||||
return &syscall.SockaddrInet4{Addr: ip, Port: addr.Port}, nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
ip := [16]byte{}
|
|
||||||
copy(ip[:], addr.IP.To16())
|
|
||||||
|
|
||||||
zoneID, err := strconv.ParseUint(addr.Zone, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
zoneID = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return &syscall.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zoneID, err := strconv.ParseUint(addr.Addr().Zone(), 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
zoneID = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return &syscall.SockaddrInet6{Addr: addr.Addr().As16(), Port: int(addr.Port()), ZoneId: uint32(zoneID)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func udpAddrFamily(net string, lAddr, rAddr *net.UDPAddr) int {
|
func udpAddrFamily(net string, lAddr, rAddr netip.AddrPort) int {
|
||||||
switch net[len(net)-1] {
|
switch net[len(net)-1] {
|
||||||
case '4':
|
case '4':
|
||||||
return syscall.AF_INET
|
return syscall.AF_INET
|
||||||
|
@ -96,29 +90,35 @@ func udpAddrFamily(net string, lAddr, rAddr *net.UDPAddr) int {
|
||||||
return syscall.AF_INET6
|
return syscall.AF_INET6
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lAddr == nil || lAddr.IP.To4() != nil) && (rAddr == nil || lAddr.IP.To4() != nil) {
|
if lAddr.Addr().Is4() && rAddr.Addr().Is4() {
|
||||||
return syscall.AF_INET
|
return syscall.AF_INET
|
||||||
}
|
}
|
||||||
return syscall.AF_INET6
|
return syscall.AF_INET6
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) {
|
func getOrigDst(oob []byte) (netip.AddrPort, error) {
|
||||||
msgs, err := syscall.ParseSocketControlMessage(oob[:oobn])
|
// oob contains socket control messages which we need to parse.
|
||||||
|
scms, err := unix.ParseSocketControlMessage(oob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return netip.AddrPort{}, fmt.Errorf("parse control message: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, msg := range msgs {
|
// retrieve the destination address from the SCM.
|
||||||
if msg.Header.Level == syscall.SOL_IP && msg.Header.Type == syscall.IP_RECVORIGDSTADDR {
|
sa, err := unix.ParseOrigDstAddr(&scms[0])
|
||||||
ip := net.IP(msg.Data[4:8])
|
if err != nil {
|
||||||
port := binary.BigEndian.Uint16(msg.Data[2:4])
|
return netip.AddrPort{}, fmt.Errorf("retrieve destination: %w", err)
|
||||||
return &net.UDPAddr{IP: ip, Port: int(port)}, nil
|
|
||||||
} else if msg.Header.Level == syscall.SOL_IPV6 && msg.Header.Type == IPV6_RECVORIGDSTADDR {
|
|
||||||
ip := net.IP(msg.Data[8:24])
|
|
||||||
port := binary.BigEndian.Uint16(msg.Data[2:4])
|
|
||||||
return &net.UDPAddr{IP: ip, Port: int(port)}, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New("cannot find origDst")
|
// encode the destination address into a cmsg.
|
||||||
|
var rAddr netip.AddrPort
|
||||||
|
switch v := sa.(type) {
|
||||||
|
case *unix.SockaddrInet4:
|
||||||
|
rAddr = netip.AddrPortFrom(netip.AddrFrom4(v.Addr), uint16(v.Port))
|
||||||
|
case *unix.SockaddrInet6:
|
||||||
|
rAddr = netip.AddrPortFrom(netip.AddrFrom16(v.Addr), uint16(v.Port))
|
||||||
|
default:
|
||||||
|
return netip.AddrPort{}, fmt.Errorf("unsupported address type: %T", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rAddr, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,13 @@ package tproxy
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) {
|
func getOrigDst(oob []byte) (netip.AddrPort, error) {
|
||||||
return nil, errors.New("UDP redir not supported on current platform")
|
return netip.AddrPort{}, errors.New("UDP redir not supported on current platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
|
func dialUDP(network string, lAddr, rAddr netip.AddrPort) (*net.UDPConn, error) {
|
||||||
return nil, errors.New("UDP redir not supported on current platform")
|
return nil, errors.New("UDP redir not supported on current platform")
|
||||||
}
|
}
|
||||||
|
|
|
@ -398,6 +398,21 @@ func ParseAddrToSocksAddr(addr net.Addr) Addr {
|
||||||
return parsed
|
return parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AddrFromStdAddrPort(addrPort netip.AddrPort) Addr {
|
||||||
|
addr := addrPort.Addr()
|
||||||
|
if addr.Is4() {
|
||||||
|
ip4 := addr.As4()
|
||||||
|
return []byte{AtypIPv4, ip4[0], ip4[1], ip4[2], ip4[3], byte(addrPort.Port() >> 8), byte(addrPort.Port())}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 1+net.IPv6len+2)
|
||||||
|
buf[0] = AtypIPv6
|
||||||
|
copy(buf[1:], addr.AsSlice())
|
||||||
|
buf[1+net.IPv6len] = byte(addrPort.Port() >> 8)
|
||||||
|
buf[1+net.IPv6len+1] = byte(addrPort.Port())
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
// DecodeUDPPacket split `packet` to addr payload, and this function is mutable with `packet`
|
// DecodeUDPPacket split `packet` to addr payload, and this function is mutable with `packet`
|
||||||
func DecodeUDPPacket(packet []byte) (addr Addr, payload []byte, err error) {
|
func DecodeUDPPacket(packet []byte) (addr Addr, payload []byte, err error) {
|
||||||
if len(packet) < 5 {
|
if len(packet) < 5 {
|
||||||
|
|
Loading…
Reference in a new issue