diff --git a/handler/redirect/tcp/handler_linux.go b/handler/redirect/tcp/handler_linux.go index 6c15c327..610c77b4 100644 --- a/handler/redirect/tcp/handler_linux.go +++ b/handler/redirect/tcp/handler_linux.go @@ -1,7 +1,6 @@ package redirect import ( - "encoding/binary" "errors" "net" "syscall" @@ -30,6 +29,10 @@ func (h *redirectHandler) getOriginalDstAddr(conn net.Conn) (addr net.Addr, err var cerr error err = rc.Control(func(fd uintptr) { 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) if err != nil { 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]), } } 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) if err != nil { cerr = err return } - var buf = make([]byte, 2) - binary.BigEndian.PutUint16(buf, info.Addr.Port) + // info.Addr.Port is in network byte order (big-endian). addr = &net.TCPAddr{ IP: net.IP(info.Addr.Addr[:]), - Port: int(binary.NativeEndian.Uint16(buf)), + Port: int(uint16(info.Addr.Port>>8 | info.Addr.Port<<8)), } } }) diff --git a/listener/redirect/tcp/listener_linux.go b/listener/redirect/tcp/listener_linux.go index 6e33b26f..581f7381 100644 --- a/listener/redirect/tcp/listener_linux.go +++ b/listener/redirect/tcp/listener_linux.go @@ -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 { 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) + } }) }