diff --git a/cmd/gost/route.go b/cmd/gost/route.go index ef585a4..359e81b 100644 --- a/cmd/gost/route.go +++ b/cmd/gost/route.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "os" + "strings" "time" "github.com/ginuerzh/gost" @@ -413,9 +414,10 @@ func (r *route) GenRouters() ([]router, error) { handler = gost.SNIHandler() case "tun": cfg := gost.TunConfig{ - Name: node.Get("name"), - Addr: node.Get("net"), - MTU: node.GetInt("mtu"), + Name: node.Get("name"), + Addr: node.Get("net"), + MTU: node.GetInt("mtu"), + Routes: strings.Split(node.Get("route"), ","), } handler = gost.TunHandler(node.Remote, gost.TunConfigHandlerOption(cfg)) default: diff --git a/go.mod b/go.mod index 382d100..893ef04 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861 // indirect github.com/cheekybits/genny v1.0.0 // indirect github.com/dchest/siphash v1.2.1 // indirect - github.com/docker/libcontainer v2.2.1+incompatible // indirect + github.com/docker/libcontainer v2.2.1+incompatible github.com/ginuerzh/gosocks4 v0.0.1 github.com/ginuerzh/gosocks5 v0.2.0 github.com/ginuerzh/tls-dissector v0.0.1 diff --git a/tun.go b/tun.go index f228f09..7521297 100644 --- a/tun.go +++ b/tun.go @@ -12,6 +12,7 @@ import ( "github.com/shadowsocks/go-shadowsocks2/core" "github.com/songgao/water" "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" ) type TunConfig struct { @@ -117,6 +118,12 @@ func (h *tunHandler) transportTun(tun net.Conn, conn net.PacketConn, raddr net.A } if header.Version != ipv4.Version { + if Debug && header.Version == ipv6.Version { + if hdr, _ := ipv6.ParseHeader(b[:n]); hdr != nil { + log.Logf("[tun] %s: %s -> %s %d %d", + tun.LocalAddr(), hdr.Src, hdr.Dst, hdr.PayloadLen, hdr.TrafficClass) + } + } log.Logf("[tun] %s: v%d ignored, only support ipv4", tun.LocalAddr(), header.Version) return nil @@ -127,13 +134,14 @@ func (h *tunHandler) transportTun(tun net.Conn, conn net.PacketConn, raddr net.A addr = v.(net.Addr) } if addr == nil { - log.Logf("[tun] %s: no address to forward for %s -> %s", - tun.LocalAddr(), header.Src, header.Dst) + log.Logf("[tun] %s: no route for %s -> %s %d/%d %x %d %d", + tun.LocalAddr(), header.Src, header.Dst, + header.Len, header.TotalLen, header.ID, header.Flags, header.Protocol) return nil } if Debug { - log.Logf("[tun] %s >>> %s: %s -> %s %d/%d %x %x %d", + log.Logf("[tun] %s >>> %s: %s -> %s %d/%d %x %d %d", tun.LocalAddr(), addr, header.Src, header.Dst, header.Len, header.TotalLen, header.ID, header.Flags, header.Protocol) } @@ -169,23 +177,31 @@ func (h *tunHandler) transportTun(tun net.Conn, conn net.PacketConn, raddr net.A } if header.Version != ipv4.Version { + if Debug && header.Version == ipv6.Version { + if hdr, _ := ipv6.ParseHeader(b[:n]); hdr != nil { + log.Logf("[tun] %s <<< %s: %s -> %s %d %d", + tun.LocalAddr(), addr, hdr.Src, hdr.Dst, hdr.PayloadLen, hdr.TrafficClass) + } + } 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", + log.Logf("[tun] %s <<< %s: %s -> %s %d/%d %x %d %d", tun.LocalAddr(), addr, header.Src, header.Dst, header.Len, header.TotalLen, header.ID, header.Flags, header.Protocol) } - if h.ipNet != nil && h.ipNet.Contains(header.Src) { + if h.ipNet != nil && h.ipNet.IP.Equal(header.Src.Mask(h.ipNet.Mask)) { 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, addr, actual.(net.Addr)) } + } else { + log.Logf("[tun] %s: record route: %s -> %s", tun.LocalAddr(), header.Src, addr) } } diff --git a/tun_darwin.go b/tun_darwin.go deleted file mode 100644 index 5b29bd1..0000000 --- a/tun_darwin.go +++ /dev/null @@ -1,44 +0,0 @@ -// +build darwin - -package gost - -import ( - "net" - "os/exec" - "strconv" - - "github.com/songgao/water" -) - -func createTun(cfg TunConfig) (conn net.Conn, ipNet *net.IPNet, err error) { - ip, ipNet, err := net.ParseCIDR(cfg.Addr) - if err != nil { - return - } - - ifce, err := water.New(water.Config{ - DeviceType: water.TUN, - }) - if err != nil { - return - } - - mtu := cfg.MTU - if mtu <= 0 { - mtu = DefaultMTU - } - - if err = exec.Command( - "ifconfig", ifce.Name(), - "inet", cfg.Addr, - "mtu", strconv.Itoa(mtu), - "up").Run(); err != nil { - return - } - - conn = &tunConn{ - ifce: ifce, - addr: &net.IPAddr{IP: ip}, - } - return -} diff --git a/tun_linux.go b/tun_linux.go index 53b2bf0..997b41f 100644 --- a/tun_linux.go +++ b/tun_linux.go @@ -3,8 +3,11 @@ package gost import ( + "fmt" "net" + "github.com/docker/libcontainer/netlink" + "github.com/go-log/log" "github.com/milosgajdos83/tenus" "github.com/songgao/water" ) @@ -34,13 +37,29 @@ func createTun(cfg TunConfig) (conn net.Conn, ipNet *net.IPNet, err error) { if mtu <= 0 { mtu = DefaultMTU } - if err = link.SetLinkMTU(mtu); err != nil { + + cmd := fmt.Sprintf("ip link set dev %s mtu %d", ifce.Name(), mtu) + log.Log("[tun]", cmd) + if er := link.SetLinkMTU(mtu); er != nil { + err = fmt.Errorf("%s: %v", cmd, er) return } - if err = link.SetLinkIp(ip, ipNet); err != nil { + + cmd = fmt.Sprintf("ip address add %s dev %s", cfg.Addr, ifce.Name()) + log.Log("[tun]", cmd) + if er := link.SetLinkIp(ip, ipNet); er != nil { + err = fmt.Errorf("%s: %v", cmd, er) return } - if err = link.SetLinkUp(); err != nil { + + cmd = fmt.Sprintf("ip link set dev %s up", ifce.Name()) + log.Log("[tun]", cmd) + if er := link.SetLinkUp(); er != nil { + err = fmt.Errorf("%s: %v", cmd, er) + return + } + + if err = addRoutes(ifce.Name(), cfg.Routes...); err != nil { return } @@ -50,3 +69,17 @@ func createTun(cfg TunConfig) (conn net.Conn, ipNet *net.IPNet, err error) { } return } + +func addRoutes(ifName string, routes ...string) error { + for _, route := range routes { + if route == "" { + continue + } + cmd := fmt.Sprintf("ip route add %s dev %s", route, ifName) + log.Log("[tun]", cmd) + if err := netlink.AddRoute(route, "", "", ifName); err != nil { + return fmt.Errorf("%s: %v", cmd, err) + } + } + return nil +} diff --git a/tun_unix.go b/tun_unix.go new file mode 100644 index 0000000..e3d00df --- /dev/null +++ b/tun_unix.go @@ -0,0 +1,70 @@ +// +build !linux,!windows + +package gost + +import ( + "fmt" + "net" + "os/exec" + "strconv" + + "github.com/go-log/log" + "github.com/songgao/water" +) + +func createTun(cfg TunConfig) (conn net.Conn, ipNet *net.IPNet, err error) { + ip, ipNet, err := net.ParseCIDR(cfg.Addr) + if err != nil { + return + } + + ifce, err := water.New(water.Config{ + DeviceType: water.TUN, + }) + if err != nil { + return + } + + mtu := cfg.MTU + if mtu <= 0 { + mtu = DefaultMTU + } + + cmd := fmt.Sprintf("ifconfig %s inet %s mtu %d up", ifce.Name(), cfg.Addr, mtu) + log.Log("[tun]", cmd) + if er := exec.Command( + "ifconfig", ifce.Name(), + "inet", cfg.Addr, + "mtu", strconv.Itoa(mtu), + "up").Run(); er != nil { + err = fmt.Errorf("%s: %v", cmd, er) + return + } + + if err = addRoutes(ifce.Name(), cfg.Routes...); err != nil { + return + } + + conn = &tunConn{ + ifce: ifce, + addr: &net.IPAddr{IP: ip}, + } + return +} + +func addRoutes(ifName string, routes ...string) error { + for _, route := range routes { + if route == "" { + continue + } + cmd := fmt.Sprintf("route add -net %s -interface %s", route, ifName) + log.Log("[tun]", cmd) + if er := exec.Command( + "route", "add", + "-net", route, + "-interface", ifName).Run(); er != nil { + return fmt.Errorf("%s: %v", cmd, er) + } + } + return nil +} diff --git a/tun_win.go b/tun_win.go index 3a99c5c..68e4b5c 100644 --- a/tun_win.go +++ b/tun_win.go @@ -7,6 +7,8 @@ import ( "net" ) +//TODO: wintun for Windows: https://www.wintun.net/ +// https://godoc.org/golang.zx2c4.com/wireguard/tun/wintun func createTun(cfg TunConfig) (conn net.Conn, ipNet *net.IPNet, err error) { err = errors.New("tun is not supported on Windows") return