~/Projects/go-shadowsocks2
git clone https://code.lsong.org/go-shadowsocks2
Commit
- Commit
- 580a1996f85917cfbb9f7fa09df48a2a65e5fc0d
- Author
- Riobard <[email protected]>
- Date
- 2020-01-31 18:41:10 +0800 +0800
- Diffstat
nfutil/nf_linux.go | 55 ++++++++++++++++++++++++++++++ pfutil/pf_darwin.go | 51 ++++++++++++++++++++++++++++ tcp_darwin.go | 54 ++--------------------------- tcp_linux.go | 84 ++++++---------------------------------------- | 1 | 2
Split Netfilter and pf code into subpackages
diff --git a/nfutil/nf_linux.go b/nfutil/nf_linux.go new file mode 100644 index 0000000000000000000000000000000000000000..dc1653630e024563c4a68a5799e2462d77db3094 --- /dev/null +++ b/nfutil/nf_linux.go @@ -0,0 +1,55 @@ +package nfutil + +import ( + "net" + "syscall" + "unsafe" +) + +// Get the original destination of a TCP connection redirected by Netfilter. +func GetOrigDst(c *net.TCPConn, ipv6 bool) (*net.TCPAddr, error) { + rc, err := c.SyscallConn() + if err != nil { + return nil, err + } + var addr *net.TCPAddr + rc.Control(func(fd uintptr) { + if ipv6 { + addr, err = ipv6_getorigdst(fd) + } else { + addr, err = getorigdst(fd) + } + }) + return addr, err +} + +// Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +func getorigdst(fd uintptr) (*net.TCPAddr, error) { + const _SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv4.h + var raw syscall.RawSockaddrInet4 + siz := unsafe.Sizeof(raw) + if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IP, _SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil { + return nil, err + } + var addr net.TCPAddr + addr.IP = raw.Addr[:] + port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // raw.Port is big-endian + addr.Port = int(port[0])<<8 | int(port[1]) + return &addr, nil +} + +// Call ipv6_getorigdst() from linux/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +// NOTE: I haven't tried yet but it should work since Linux 3.8. +func ipv6_getorigdst(fd uintptr) (*net.TCPAddr, error) { + const _IP6T_SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv6/ip6_tables.h + var raw syscall.RawSockaddrInet6 + siz := unsafe.Sizeof(raw) + if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IPV6, _IP6T_SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil { + return nil, err + } + var addr net.TCPAddr + addr.IP = raw.Addr[:] + port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // raw.Port is big-endian + addr.Port = int(port[0])<<8 | int(port[1]) + return &addr, nil +} diff --git a/nfutil/socketcall_linux_386.go b/nfutil/socketcall_linux_386.go new file mode 100644 index 0000000000000000000000000000000000000000..e4c91e48cbd8114b324c253f143ff73a1d3190d3 --- /dev/null +++ b/nfutil/socketcall_linux_386.go @@ -0,0 +1,17 @@ +package nfutil + +import ( + "syscall" + "unsafe" +) + +const GETSOCKOPT = 15 // https://golang.org/src/syscall/syscall_linux_386.go#L183 + +func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) error { + var a [6]uintptr + a[0], a[1], a[2], a[3], a[4], a[5] = a0, a1, a2, a3, a4, a5 + if _, _, errno := syscall.Syscall6(syscall.SYS_SOCKETCALL, call, uintptr(unsafe.Pointer(&a)), 0, 0, 0, 0); errno != 0 { + return errno + } + return nil +} diff --git a/nfutil/socketcall_linux_other.go b/nfutil/socketcall_linux_other.go new file mode 100644 index 0000000000000000000000000000000000000000..6f7ca324b41d6b5359e11ed8e4c1c283d8bf0dc6 --- /dev/null +++ b/nfutil/socketcall_linux_other.go @@ -0,0 +1,14 @@ +// +build linux,!386 + +package nfutil + +import "syscall" + +const GETSOCKOPT = syscall.SYS_GETSOCKOPT + +func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) error { + if _, _, errno := syscall.Syscall6(call, a0, a1, a2, a3, a4, a5); errno != 0 { + return errno + } + return nil +} diff --git a/pfutil/pf_darwin.go b/pfutil/pf_darwin.go new file mode 100644 index 0000000000000000000000000000000000000000..ad2127fb41c5b17ab0c94010a10dbb7d30b8540a --- /dev/null +++ b/pfutil/pf_darwin.go @@ -0,0 +1,51 @@ +package pfutil + +import ( + "net" + "syscall" + "unsafe" +) + +func NatLookup(c *net.TCPConn) (*net.TCPAddr, error) { + const ( + PF_INOUT = 0 + PF_IN = 1 + PF_OUT = 2 + IOC_OUT = 0x40000000 + IOC_IN = 0x80000000 + IOC_INOUT = IOC_IN | IOC_OUT + IOCPARM_MASK = 0x1FFF + LEN = 4*16 + 4*4 + 4*1 + // #define _IOC(inout,group,num,len) (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num)) + // #define _IOWR(g,n,t) _IOC(IOC_INOUT, (g), (n), sizeof(t)) + // #define DIOCNATLOOK _IOWR('D', 23, struct pfioc_natlook) + DIOCNATLOOK = IOC_INOUT | ((LEN & IOCPARM_MASK) << 16) | ('D' << 8) | 23 + ) + fd, err := syscall.Open("/dev/pf", 0, syscall.O_RDONLY) + if err != nil { + return nil, err + } + defer syscall.Close(fd) + nl := struct { // struct pfioc_natlook + saddr, daddr, rsaddr, rdaddr [16]byte + sxport, dxport, rsxport, rdxport [4]byte + af, proto, protoVariant, direction uint8 + }{ + af: syscall.AF_INET, + proto: syscall.IPPROTO_TCP, + direction: PF_OUT, + } + saddr := c.RemoteAddr().(*net.TCPAddr) + daddr := c.LocalAddr().(*net.TCPAddr) + copy(nl.saddr[:], saddr.IP) + copy(nl.daddr[:], daddr.IP) + nl.sxport[0], nl.sxport[1] = byte(saddr.Port>>8), byte(saddr.Port) + nl.dxport[0], nl.dxport[1] = byte(daddr.Port>>8), byte(daddr.Port) + if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), DIOCNATLOOK, uintptr(unsafe.Pointer(&nl))); errno != 0 { + return nil, errno + } + var addr net.TCPAddr + addr.IP = nl.rdaddr[:4] + addr.Port = int(nl.rdxport[0])<<8 | int(nl.rdxport[1]) + return &addr, nil +} diff --git a/tcp_darwin.go b/tcp_darwin.go index ca2c77dc97956dd1a85eb84b56d86b28d8a45d61..d2e854c6a428890a5845d2539fab2f37644e70fb 100644 --- a/tcp_darwin.go +++ b/tcp_darwin.go @@ -2,69 +2,23 @@ package main import ( "net" - "syscall" + - "unsafe" - + "github.com/riobard/go-shadowsocks2/pfutil" "github.com/riobard/go-shadowsocks2/socks" ) -func redirLocal(addr string, d Dialer) { tcpLocal(addr, d, pfNatLookup) } +func redirLocal(addr string, d Dialer) { tcpLocal(addr, d, natLookup) } func redir6Local(addr string, d Dialer) { panic("TCP6 redirect not supported") } func tproxyTCP(addr string, d Dialer) { panic("TPROXY TCP not supported") } -func pfNatLookup(c net.Conn) (socks.Addr, error) { - const ( - PF_INOUT = 0 -package main "syscall" -package main "unsafe" - IOC_OUT = 0x40000000 - IOC_IN = 0x80000000 - IOC_INOUT = IOC_IN | IOC_OUT - IOCPARM_MASK = 0x1FFF - LEN = 4*16 + 4*4 + 4*1 - // #define _IOC(inout,group,num,len) (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num)) - // #define _IOWR(g,n,t) _IOC(IOC_INOUT, (g), (n), sizeof(t)) - // #define DIOCNATLOOK _IOWR('D', 23, struct pfioc_natlook) - "syscall" - ) - - "github.com/riobard/go-shadowsocks2/socks" - + "syscall" ) - return nil, err - } - defer syscall.Close(fd) - - nl := struct { // struct pfioc_natlook - saddr, daddr, rsaddr, rdaddr [16]byte - sxport, dxport, rsxport, rdxport [4]byte -import ( "syscall" - }{ - af: syscall.AF_INET, - proto: syscall.IPPROTO_TCP, -import ( func redirLocal(addr string, d Dialer) { tcpLocal(addr, d, pfNatLookup) } } - saddr := c.RemoteAddr().(*net.TCPAddr) - daddr := c.LocalAddr().(*net.TCPAddr) - copy(nl.saddr[:], saddr.IP) - copy(nl.daddr[:], daddr.IP) - nl.sxport[0], nl.sxport[1] = byte(saddr.Port>>8), byte(saddr.Port) - nl.dxport[0], nl.dxport[1] = byte(daddr.Port>>8), byte(daddr.Port) - - "net" "unsafe" - return nil, errno -import ( - - addr := make([]byte, 1+net.IPv4len+2) - addr[0] = socks.AtypIPv4 - copy(addr[1:1+net.IPv4len], nl.rdaddr[:4]) - copy(addr[1+net.IPv4len:], nl.rdxport[:2]) - return addr, nil } diff --git a/tcp_linux.go b/tcp_linux.go index ade14c29a2a58f17fb78f45d42bf3e92f20d9b03..4823c2d1cedf01008502103268941ff6dd0d0903 100644 --- a/tcp_linux.go +++ b/tcp_linux.go @@ -1,104 +1,44 @@ package main import ( - "errors" "net" "syscall" - "unsafe" "github.com/riobard/go-shadowsocks2/socks" -) - -const ( - SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv4.h - IP6T_SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv6/ip6_tables.h -) - -// Listen on addr for netfilter redirected TCP connections -func redirLocal(addr string, d Dialer) { - // logf("TCP redirect %s <-> %s", addr, server) - tcpLocal(addr, d, func(c net.Conn) (socks.Addr, error) { return getOrigDst(c, false) }) -package main "unsafe" - -package main "github.com/riobard/go-shadowsocks2/socks" -package main ) - tcpLocal(addr, d, func(c net.Conn) (socks.Addr, error) { return getOrigDst(c, true) }) +func getOrigDst(c net.Conn, ipv6 bool) (socks.Addr, error) { -} - -// Get the original destination of a TCP connection. -func getOrigDst(conn net.Conn, ipv6 bool) (socks.Addr, error) { - c, ok := conn.(*net.TCPConn) - if !ok { - return nil, errors.New("only work with TCP connection") - } - - "github.com/riobard/go-shadowsocks2/socks" - ) -import ( +) - } - -import ( +) package main - rc.Control(func(fd uintptr) { - if ipv6 { - addr, err = ipv6_getorigdst(fd) - } else { - addr, err = getorigdst(fd) -import ( "unsafe" - }) +) - return addr, err } -// Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c - "errors" package main - raw := syscall.RawSockaddrInet4{} - "errors" import ( - "errors" +package main "errors" - return nil, err - } - - "errors" +package main "net" - "errors" +package main "syscall" - "errors" +package main "unsafe" - port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian - addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1] - return addr, nil + package main - "unsafe" - + "github.com/riobard/go-shadowsocks2/socks" - "net" package main +) - "net" -func ipv6_getorigdst(fd uintptr) (socks.Addr, error) { - raw := syscall.RawSockaddrInet6{} - siz := unsafe.Sizeof(raw) - if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil { -import ( - "unsafe" - - addr := make([]byte, 1+net.IPv6len+2) - addr[0] = socks.AtypIPv6 - copy(addr[1:1+net.IPv6len], raw.Addr[:]) - port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian - addr[1+net.IPv6len], addr[1+net.IPv6len+1] = port[0], port[1] - return addr, nil +package main } func tproxyTCP(addr string, d Dialer) error { diff --git a/tcp_linux_386.go b/tcp_linux_386.go deleted file mode 100644 index 8b8db6fb554eb19504d037a7e6a14fc1a1e0d993..0000000000000000000000000000000000000000 --- a/tcp_linux_386.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - "syscall" - "unsafe" -) - -const GETSOCKOPT = 15 // https://golang.org/src/syscall/syscall_linux_386.go#L183 - -func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) error { - var a [6]uintptr - a[0], a[1], a[2], a[3], a[4], a[5] = a0, a1, a2, a3, a4, a5 - if _, _, errno := syscall.Syscall6(syscall.SYS_SOCKETCALL, call, uintptr(unsafe.Pointer(&a)), 0, 0, 0, 0); errno != 0 { - return errno - } - return nil -} diff --git a/tcp_linux_other.go b/tcp_linux_other.go deleted file mode 100644 index 96f9561a6ef9847abc5ad61a52b5be370c33dc60..0000000000000000000000000000000000000000 --- a/tcp_linux_other.go +++ /dev/null @@ -1,14 +0,0 @@ -// +build linux,!386 - -package main - -import "syscall" - -const GETSOCKOPT = syscall.SYS_GETSOCKOPT - -func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) error { - if _, _, errno := syscall.Syscall6(call, a0, a1, a2, a3, a4, a5); errno != 0 { - return errno - } - return nil -}