2021-08-18 05:26:23 +00:00
|
|
|
//go:build linux
|
2020-03-08 13:58:49 +00:00
|
|
|
|
2021-06-13 09:23:10 +00:00
|
|
|
package tproxy
|
2020-03-08 13:58:49 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net"
|
2022-06-12 11:37:27 +00:00
|
|
|
"net/netip"
|
2020-03-08 13:58:49 +00:00
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
"syscall"
|
2022-06-12 11:37:27 +00:00
|
|
|
|
|
|
|
"golang.org/x/sys/unix"
|
2020-03-08 13:58:49 +00:00
|
|
|
)
|
|
|
|
|
2021-06-13 09:23:10 +00:00
|
|
|
const (
|
|
|
|
IPV6_TRANSPARENT = 0x4b
|
|
|
|
IPV6_RECVORIGDSTADDR = 0x4a
|
|
|
|
)
|
|
|
|
|
2020-03-08 13:58:49 +00:00
|
|
|
// dialUDP acts like net.DialUDP for transparent proxy.
|
|
|
|
// It binds to a non-local address(`lAddr`).
|
2022-06-12 11:37:27 +00:00
|
|
|
func dialUDP(network string, lAddr, rAddr netip.AddrPort) (uc *net.UDPConn, err error) {
|
2020-03-08 13:58:49 +00:00
|
|
|
rSockAddr, err := udpAddrToSockAddr(rAddr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
lSockAddr, err := udpAddrToSockAddr(lAddr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
fd, err := syscall.Socket(udpAddrFamily(network, lAddr, rAddr), syscall.SOCK_DGRAM, 0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-06-12 11:37:27 +00:00
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
|
|
|
syscall.Close(fd)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2020-03-08 13:58:49 +00:00
|
|
|
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = syscall.Bind(fd, lSockAddr); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = syscall.Connect(fd, rSockAddr); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
fdFile := os.NewFile(uintptr(fd), fmt.Sprintf("net-udp-dial-%s", rAddr.String()))
|
|
|
|
defer fdFile.Close()
|
|
|
|
|
|
|
|
c, err := net.FileConn(fdFile)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.(*net.UDPConn), nil
|
|
|
|
}
|
|
|
|
|
2022-06-12 11:37:27 +00:00
|
|
|
func udpAddrToSockAddr(addr netip.AddrPort) (syscall.Sockaddr, error) {
|
|
|
|
if addr.Addr().Is4() {
|
|
|
|
return &syscall.SockaddrInet4{Addr: addr.Addr().As4(), Port: int(addr.Port())}, nil
|
|
|
|
}
|
2020-03-08 13:58:49 +00:00
|
|
|
|
2022-06-12 11:37:27 +00:00
|
|
|
zoneID, err := strconv.ParseUint(addr.Addr().Zone(), 10, 32)
|
|
|
|
if err != nil {
|
|
|
|
zoneID = 0
|
2020-03-08 13:58:49 +00:00
|
|
|
}
|
2022-06-12 11:37:27 +00:00
|
|
|
|
|
|
|
return &syscall.SockaddrInet6{Addr: addr.Addr().As16(), Port: int(addr.Port()), ZoneId: uint32(zoneID)}, nil
|
2020-03-08 13:58:49 +00:00
|
|
|
}
|
|
|
|
|
2022-06-12 11:37:27 +00:00
|
|
|
func udpAddrFamily(net string, lAddr, rAddr netip.AddrPort) int {
|
2020-03-08 13:58:49 +00:00
|
|
|
switch net[len(net)-1] {
|
|
|
|
case '4':
|
|
|
|
return syscall.AF_INET
|
|
|
|
case '6':
|
|
|
|
return syscall.AF_INET6
|
|
|
|
}
|
|
|
|
|
2022-06-12 11:37:27 +00:00
|
|
|
if lAddr.Addr().Is4() && rAddr.Addr().Is4() {
|
2020-03-08 13:58:49 +00:00
|
|
|
return syscall.AF_INET
|
|
|
|
}
|
|
|
|
return syscall.AF_INET6
|
|
|
|
}
|
2021-06-13 09:23:10 +00:00
|
|
|
|
2022-06-12 11:37:27 +00:00
|
|
|
func getOrigDst(oob []byte) (netip.AddrPort, error) {
|
|
|
|
// oob contains socket control messages which we need to parse.
|
|
|
|
scms, err := unix.ParseSocketControlMessage(oob)
|
2021-06-13 09:23:10 +00:00
|
|
|
if err != nil {
|
2022-06-12 11:37:27 +00:00
|
|
|
return netip.AddrPort{}, fmt.Errorf("parse control message: %w", err)
|
2021-06-13 09:23:10 +00:00
|
|
|
}
|
|
|
|
|
2022-06-12 11:37:27 +00:00
|
|
|
// retrieve the destination address from the SCM.
|
|
|
|
sa, err := unix.ParseOrigDstAddr(&scms[0])
|
|
|
|
if err != nil {
|
|
|
|
return netip.AddrPort{}, fmt.Errorf("retrieve destination: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
2021-06-13 09:23:10 +00:00
|
|
|
}
|
|
|
|
|
2022-06-12 11:37:27 +00:00
|
|
|
return rAddr, nil
|
2021-06-13 09:23:10 +00:00
|
|
|
}
|