diff --git a/cmd/gost/route.go b/cmd/gost/route.go index 16648f0..ef585a4 100644 --- a/cmd/gost/route.go +++ b/cmd/gost/route.go @@ -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 != "" { diff --git a/go.mod b/go.mod index 08d4cca..6f783d0 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 9181901..ef42f7b 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/gost.go b/gost.go index e961247..cc07518 100644 --- a/gost.go +++ b/gost.go @@ -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. diff --git a/handler.go b/handler.go index 66f894d..11c80e1 100644 --- a/handler.go +++ b/handler.go @@ -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 } diff --git a/node.go b/node.go index dc012a1..3a94dd3 100644 --- a/node.go +++ b/node.go @@ -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 = "" } diff --git a/server.go b/server.go index 17f8cc1..88d2530 100644 --- a/server.go +++ b/server.go @@ -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) } } diff --git a/tun.go b/tun.go new file mode 100644 index 0000000..68ee620 --- /dev/null +++ b/tun.go @@ -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 +}