add tun device support (#420)

This commit is contained in:
ginuerzh 2019-12-26 14:57:11 +08:00
parent 5414768920
commit b11ce8a71c
8 changed files with 368 additions and 11 deletions

View File

@ -372,6 +372,8 @@ func (r *route) GenRouters() ([]router, error) {
ln, err = gost.Obfs4Listener(node.Addr)
case "ohttp":
ln, err = gost.ObfsHTTPListener(node.Addr)
case "tun":
ln, err = gost.TunListener(node.Addr)
default:
ln, err = gost.TCPListener(node.Addr)
}
@ -409,6 +411,13 @@ func (r *route) GenRouters() ([]router, error) {
handler = gost.ShadowUDPdHandler()
case "sni":
handler = gost.SNIHandler()
case "tun":
cfg := gost.TunConfig{
Name: node.Get("name"),
Addr: node.Get("net"),
MTU: node.GetInt("mtu"),
}
handler = gost.TunHandler(node.Remote, gost.TunConfigHandlerOption(cfg))
default:
// start from 2.5, if remote is not empty, then we assume that it is a forward tunnel.
if node.Remote != "" {

1
go.mod
View File

@ -31,6 +31,7 @@ require (
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735
github.com/shadowsocks/go-shadowsocks2 v0.0.11
github.com/shadowsocks/shadowsocks-go v0.0.0-20170121203516-97a5c71f80ba
github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect
github.com/tjfoc/gmsm v1.0.1 // indirect

2
go.sum
View File

@ -65,6 +65,8 @@ github.com/shadowsocks/go-shadowsocks2 v0.0.11 h1:dXloqEhYnZV40jblWTK8kWeC0Eb+dg
github.com/shadowsocks/go-shadowsocks2 v0.0.11/go.mod h1:R+KWaoIwRRhnpw6XV+dZil0XHi64Hc1D7hXUyXTjUzQ=
github.com/shadowsocks/shadowsocks-go v0.0.0-20170121203516-97a5c71f80ba h1:tJgNXb3S+RkB4kNPi6N5OmEWe3m+Y3Qs6LUMiNDAONM=
github.com/shadowsocks/shadowsocks-go v0.0.0-20170121203516-97a5c71f80ba/go.mod h1:mttDPaeLm87u74HMrP+n2tugXvIKWcwff/cqSX0lehY=
github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b h1:+y4hCMc/WKsDbAPsOQZgBSaSZ26uh2afyaWeVg/3s/c=
github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkBak2MM0u+vhGhlQwpeimUi7QncM=

View File

@ -20,14 +20,14 @@ import (
)
// Version is the gost version.
const Version = "2.8.2"
const Version = "2.9.0-dev"
// Debug is a flag that enables the debug log.
var Debug bool
var (
tinyBufferSize = 128
smallBufferSize = 1 * 1024 // 1KB small buffer
tinyBufferSize = 512
smallBufferSize = 2 * 1024 // 2KB small buffer
mediumBufferSize = 8 * 1024 // 8KB medium buffer
largeBufferSize = 32 * 1024 // 32KB large buffer
)
@ -77,6 +77,8 @@ var (
// DefaultUserAgent is the default HTTP User-Agent header used by HTTP and websocket.
DefaultUserAgent = "Chrome/78.0.3904.106"
DefaultMTU = 1350 // default mtu for tun device
)
// SetLogger sets a new logger for internal log system.

View File

@ -40,6 +40,7 @@ type HandlerOptions struct {
Node Node
Host string
IPs []string
TunConfig TunConfig
}
// HandlerOption allows a common way to set handler options.
@ -195,6 +196,13 @@ func IPsHandlerOption(ips []string) HandlerOption {
}
}
// TunConfigHandlerOption sets the config for tun device.
func TunConfigHandlerOption(cfg TunConfig) HandlerOption {
return func(opts *HandlerOptions) {
opts.TunConfig = cfg
}
}
type autoHandler struct {
options *HandlerOptions
}

View File

@ -82,6 +82,7 @@ func ParseNode(s string) (node Node, err error) {
case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding
case "rtcp", "rudp": // rtcp and rudp are for remote port forwarding
case "ohttp": // obfs-http
case "tun": //tun device
default:
node.Transport = "tcp"
}
@ -93,6 +94,7 @@ func ParseNode(s string) (node Node, err error) {
case "tcp", "udp", "rtcp", "rudp": // port forwarding
case "direct", "remote", "forward": // forwarding
case "redirect": // TCP transparent proxy
case "tun": // tun device
default:
node.Protocol = ""
}

View File

@ -81,14 +81,6 @@ func (s *Server) Serve(h Handler, opts ...ServerOption) error {
}
tempDelay = 0
/*
if s.options.Bypass.Contains(conn.RemoteAddr().String()) {
log.Log("[bypass]", conn.RemoteAddr())
conn.Close()
continue
}
*/
go h.Handle(conn)
}
}

341
tun.go Normal file
View File

@ -0,0 +1,341 @@
package gost
import (
"errors"
"io"
"net"
"os"
"os/exec"
"strconv"
"sync"
"time"
"github.com/go-log/log"
"github.com/shadowsocks/go-shadowsocks2/core"
"github.com/songgao/water"
"golang.org/x/net/ipv4"
)
type TunConfig struct {
Name string
Addr string
MTU int
Routes []string
}
type tunHandler struct {
raddr string
options *HandlerOptions
}
// TunHandler creates a handler for tun tunnel.
func TunHandler(raddr string, opts ...HandlerOption) Handler {
h := &tunHandler{
raddr: raddr,
options: &HandlerOptions{},
}
for _, opt := range opts {
opt(h.options)
}
return h
}
func (h *tunHandler) Init(options ...HandlerOption) {
if h.options == nil {
h.options = &HandlerOptions{}
}
for _, opt := range options {
opt(h.options)
}
}
func (h *tunHandler) Handle(conn net.Conn) {
defer os.Exit(0)
defer conn.Close()
uc, ok := conn.(net.PacketConn)
if !ok {
log.Log("[tun] wrong connection type, must be PacketConn")
return
}
tc, err := h.createTun()
if err != nil {
log.Logf("[tun] %s create tun: %v", conn.LocalAddr(), err)
return
}
defer tc.Close()
log.Logf("[tun] %s - %s: tun creation successful", tc.LocalAddr(), conn.LocalAddr())
var raddr net.Addr
if h.raddr != "" {
raddr, err = net.ResolveUDPAddr("udp", h.raddr)
if err != nil {
log.Logf("[tun] %s - %s remote addr: %v", tc.LocalAddr(), conn.LocalAddr(), err)
return
}
}
if len(h.options.Users) > 0 && h.options.Users[0] != nil {
passwd, _ := h.options.Users[0].Password()
cipher, err := core.PickCipher(h.options.Users[0].Username(), nil, passwd)
if err != nil {
log.Logf("[tun] %s - %s cipher: %v", tc.LocalAddr(), conn.LocalAddr(), err)
return
}
uc = cipher.PacketConn(uc)
}
h.transportTun(tc, uc, raddr)
}
func (h *tunHandler) createTun() (conn net.Conn, err error) {
cfg := h.options.TunConfig
ip, _, err := net.ParseCIDR(cfg.Addr)
if err != nil {
return
}
ifce, err := water.New(water.Config{
DeviceType: water.TUN,
PlatformSpecificParams: water.PlatformSpecificParams{
Name: cfg.Name,
},
})
if err != nil {
return
}
setup := func(args ...string) error {
cmd := exec.Command("/sbin/ip", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
mtu := cfg.MTU
if mtu <= 0 {
mtu = DefaultMTU
}
if err = setup("link", "set", "dev", ifce.Name(), "mtu", strconv.Itoa(mtu)); err != nil {
return
}
if err = setup("addr", "add", cfg.Addr, "dev", ifce.Name()); err != nil {
return
}
if err = setup("link", "set", "dev", ifce.Name(), "up"); err != nil {
return
}
tc := &tunConn{
ifce: ifce,
addr: &net.IPAddr{IP: ip},
}
return tc, nil
}
func (h *tunHandler) transportTun(tun net.Conn, conn net.PacketConn, raddr net.Addr) error {
var routes sync.Map
errc := make(chan error, 1)
go func() {
for {
err := func() error {
b := sPool.Get().([]byte)
defer sPool.Put(b)
n, err := tun.Read(b)
if err != nil {
return err
}
header, err := ipv4.ParseHeader(b[:n])
if err != nil {
log.Logf("[tun] %s: %v", tun.LocalAddr(), err)
return err
}
if header.Version != ipv4.Version {
log.Logf("[tun] %s: v%d ignored, only support ipv4",
tun.LocalAddr(), header.Version)
return nil
}
addr := raddr
if v, ok := routes.Load(header.Dst.String()); ok {
addr = v.(net.Addr)
}
if addr == nil {
log.Logf("[tun] %s: no address to forward for %s -> %s",
tun.LocalAddr(), header.Src, header.Dst)
return nil
}
if Debug {
log.Logf("[tun] %s >>> %s: %s -> %s %d/%d %x %x %d",
tun.LocalAddr(), addr, header.Src, header.Dst,
header.Len, header.TotalLen, header.ID, header.Flags, header.Protocol)
}
if _, err := conn.WriteTo(b[:n], addr); err != nil {
return err
}
return nil
}()
if err != nil {
errc <- err
return
}
}
}()
go func() {
for {
err := func() error {
b := sPool.Get().([]byte)
defer mPool.Put(b)
n, addr, err := conn.ReadFrom(b)
if err != nil {
return err
}
header, err := ipv4.ParseHeader(b[:n])
if err != nil {
log.Logf("[tun] %s <- %s: %v", tun.LocalAddr(), addr, err)
return err
}
if header.Version != ipv4.Version {
log.Logf("[tun] %s <- %s: v%d ignored, only support ipv4",
tun.LocalAddr(), addr, header.Version)
return nil
}
if Debug {
log.Logf("[tun] %s <<< %s: %s -> %s %d/%d %x %x %d",
tun.LocalAddr(), addr, header.Src, header.Dst,
header.Len, header.TotalLen, header.ID, header.Flags, header.Protocol)
}
if actual, loaded := routes.LoadOrStore(header.Src.String(), addr); loaded {
if actual.(net.Addr).String() != addr.String() {
log.Logf("[tun] %s <- %s: unexpected address mapping %s -> %s(actual %s)",
tun.LocalAddr(), addr, header.Dst.String(), addr, actual.(net.Addr).String())
}
}
if _, err := tun.Write(b[:n]); err != nil {
return err
}
return nil
}()
if err != nil {
errc <- err
return
}
}
}()
err := <-errc
if err != nil && err == io.EOF {
err = nil
}
log.Logf("[tun] %s - %s: %v", tun.LocalAddr(), conn.LocalAddr(), err)
return err
}
type tunConn struct {
ifce *water.Interface
addr net.Addr
}
func (c *tunConn) Read(b []byte) (n int, err error) {
return c.ifce.Read(b)
}
func (c *tunConn) Write(b []byte) (n int, err error) {
return c.ifce.Write(b)
}
func (c *tunConn) Close() (err error) {
return c.ifce.Close()
}
func (c *tunConn) LocalAddr() net.Addr {
return c.addr
}
func (c *tunConn) RemoteAddr() net.Addr {
return &net.IPAddr{}
}
func (c *tunConn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tun", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *tunConn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tun", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *tunConn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tun", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
type tunListener struct {
conn *net.UDPConn
accepted, closed chan struct{}
}
// TunListener creates a listener for tun tunnel.
func TunListener(addr string) (Listener, error) {
laddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
conn, err := net.ListenUDP("udp", laddr)
if err != nil {
return nil, err
}
return &tunListener{
conn: conn,
accepted: make(chan struct{}),
closed: make(chan struct{}),
}, nil
}
func (l *tunListener) Accept() (net.Conn, error) {
select {
case <-l.accepted:
default:
close(l.accepted)
return l.conn, nil
}
select {
case <-l.closed:
}
return nil, errors.New("accept on closed listener")
}
func (l *tunListener) Addr() net.Addr {
return l.conn.LocalAddr()
}
func (l *tunListener) Close() error {
select {
case <-l.closed:
return errors.New("listener has been closed")
default:
close(l.closed)
}
return nil
}