fix(redirect): add IPv6 TPROXY support for TCP redirect listener
Add missing IPV6_TRANSPARENT socket option to the TCP redirect listener Control callback, matching what the UDP redirect listener already sets. Without this, the kernel silently drops IPv6 TPROXY-redirected connections. Also clean up handler_linux.go getOriginalDstAddr: - Remove encoding/binary import and heap allocation for port conversion - Replace with zero-allocation ntohs (native-to-network byte order swap) - Document the struct layout hack where GetsockoptIPv6Mreq and GetsockoptIPv6MTUInfo are used to read sockaddr_in/sockaddr_in6 from SO_ORIGINAL_DST (kernel returns smaller structs than the getter functions expect, but only the first N bytes are populated) Fixes go-gost/gost#126
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
package redirect
|
package redirect
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"syscall"
|
"syscall"
|
||||||
@@ -30,6 +29,10 @@ func (h *redirectHandler) getOriginalDstAddr(conn net.Conn) (addr net.Addr, err
|
|||||||
var cerr error
|
var cerr error
|
||||||
err = rc.Control(func(fd uintptr) {
|
err = rc.Control(func(fd uintptr) {
|
||||||
if tcpAddr.IP.To4() != nil {
|
if tcpAddr.IP.To4() != nil {
|
||||||
|
// SO_ORIGINAL_DST returns a struct sockaddr_in (16 bytes).
|
||||||
|
// GetsockoptIPv6Mreq reads into a 20-byte ipv6_mreq; only the
|
||||||
|
// first 16 bytes (the Multiaddr field) are populated.
|
||||||
|
// Layout: [0:2]=sin_family, [2:4]=sin_port (BE), [4:8]=sin_addr.
|
||||||
mreq, err := unix.GetsockoptIPv6Mreq(int(fd), unix.IPPROTO_IP, unix.SO_ORIGINAL_DST)
|
mreq, err := unix.GetsockoptIPv6Mreq(int(fd), unix.IPPROTO_IP, unix.SO_ORIGINAL_DST)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cerr = err
|
cerr = err
|
||||||
@@ -41,17 +44,19 @@ func (h *redirectHandler) getOriginalDstAddr(conn net.Conn) (addr net.Addr, err
|
|||||||
Port: int(mreq.Multiaddr[2])<<8 + int(mreq.Multiaddr[3]),
|
Port: int(mreq.Multiaddr[2])<<8 + int(mreq.Multiaddr[3]),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// SO_ORIGINAL_DST returns a struct sockaddr_in6 (28 bytes).
|
||||||
|
// GetsockoptIPv6MTUInfo reads into a 32-byte ipv6_mtuinfo; only
|
||||||
|
// the first 28 bytes (the Addr field) are populated.
|
||||||
info, err := unix.GetsockoptIPv6MTUInfo(int(fd), unix.IPPROTO_IPV6, unix.SO_ORIGINAL_DST)
|
info, err := unix.GetsockoptIPv6MTUInfo(int(fd), unix.IPPROTO_IPV6, unix.SO_ORIGINAL_DST)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cerr = err
|
cerr = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf = make([]byte, 2)
|
// info.Addr.Port is in network byte order (big-endian).
|
||||||
binary.BigEndian.PutUint16(buf, info.Addr.Port)
|
|
||||||
addr = &net.TCPAddr{
|
addr = &net.TCPAddr{
|
||||||
IP: net.IP(info.Addr.Addr[:]),
|
IP: net.IP(info.Addr.Addr[:]),
|
||||||
Port: int(binary.NativeEndian.Uint16(buf)),
|
Port: int(uint16(info.Addr.Port>>8 | info.Addr.Port<<8)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -11,5 +11,8 @@ func (l *redirectListener) control(network, address string, c syscall.RawConn) e
|
|||||||
if err := unix.SetsockoptInt(int(fd), unix.SOL_IP, unix.IP_TRANSPARENT, 1); err != nil {
|
if err := unix.SetsockoptInt(int(fd), unix.SOL_IP, unix.IP_TRANSPARENT, 1); err != nil {
|
||||||
l.logger.Errorf("SetsockoptInt(SOL_IP, IP_TRANSPARENT, 1): %v", err)
|
l.logger.Errorf("SetsockoptInt(SOL_IP, IP_TRANSPARENT, 1): %v", err)
|
||||||
}
|
}
|
||||||
|
if err := unix.SetsockoptInt(int(fd), unix.SOL_IPV6, unix.IPV6_TRANSPARENT, 1); err != nil {
|
||||||
|
l.logger.Errorf("SetsockoptInt(SOL_IPV6, IPV6_TRANSPARENT, 1): %v", err)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user