add tun device support (#420)
This commit is contained in:
parent
5414768920
commit
b11ce8a71c
@ -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
1
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
|
||||
|
2
go.sum
2
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=
|
||||
|
8
gost.go
8
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.
|
||||
|
@ -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
|
||||
}
|
||||
|
2
node.go
2
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 = ""
|
||||
}
|
||||
|
@ -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
341
tun.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user