mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-12-24 13:27:56 +08:00
修订示例,文档,代码;为macos实现为tun自动配置路由表功能
This commit is contained in:
@@ -5,6 +5,8 @@ package main
|
||||
// gui界面, 所属计划为 vsc 计划,即versyimple client计划,使用图形界面. 服务端无需gui,所以我们叫client
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/e1732a364fed/ui"
|
||||
_ "github.com/e1732a364fed/ui/winmanifest"
|
||||
"github.com/e1732a364fed/v2ray_simple/utils"
|
||||
@@ -295,6 +297,11 @@ func setupUI() {
|
||||
}
|
||||
filesM.AppendItem("Open github").OnClicked(openUrlFunc(weblink))
|
||||
filesM.AppendItem("Check github releases").OnClicked(openUrlFunc(weblink + "releases"))
|
||||
qi := filesM.AppendItem("Quit App")
|
||||
|
||||
qi.OnClicked(func(mi *ui.MenuItem, w *ui.Window) {
|
||||
syscall.Kill(syscall.Getpid(), syscall.SIGINT) //退出app
|
||||
})
|
||||
|
||||
var y = ui.NewMenu("Debug")
|
||||
y.AppendItem("test").OnClicked(func(mi *ui.MenuItem, w *ui.Window) {
|
||||
@@ -308,7 +315,7 @@ func setupUI() {
|
||||
|
||||
{
|
||||
mainwin.OnClosing(func(*ui.Window) bool {
|
||||
ui.Quit()
|
||||
ui.Quit() //只是退出gui模式,不会退出app
|
||||
mainwin = nil
|
||||
return true
|
||||
})
|
||||
|
||||
@@ -301,6 +301,23 @@ func mainFunc() (result int) {
|
||||
|
||||
runPreCommands()
|
||||
|
||||
stopGorouteCaptureSignalChan := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
osSignals := make(chan os.Signal, 1)
|
||||
signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM) //os.Kill cannot be trapped
|
||||
select {
|
||||
case <-stopGorouteCaptureSignalChan:
|
||||
return
|
||||
case <-osSignals:
|
||||
utils.Info("Program got close signal.")
|
||||
|
||||
defaultMachine.Stop()
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
defaultMachine.Start()
|
||||
|
||||
//没可用的listen/dial,而且还无法动态更改配置
|
||||
@@ -350,6 +367,8 @@ func mainFunc() (result int) {
|
||||
}
|
||||
|
||||
{
|
||||
close(stopGorouteCaptureSignalChan)
|
||||
|
||||
osSignals := make(chan os.Signal, 1)
|
||||
signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM) //os.Kill cannot be trapped
|
||||
<-osSignals
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
# 对于小白来说,下面的指导太过于高级,难以看懂,因此对于小白来说推荐全自动化的方案。
|
||||
|
||||
# 在macos上, 配置路由表还需要在tun设备建立之后才能进行,因为tun设备的名称是无法自定义的
|
||||
# 在macos上, 需要管理员权限(sudo)运行vs_gui 才能 成功自动创建tun设备
|
||||
|
||||
# 下面给出macos上的路由表配置指导
|
||||
|
||||
# 首先删除默认路由,然后将路由指向 utun3 (用户自己运行的到的名称可能不同)
|
||||
# 最后将自己的服务器的ip (我们的例子是127.0.0.1,请你改成实际服务器ip) 的路由指向原来的 路由器地址
|
||||
# 这个方案只适用于
|
||||
# 最后将自己的服务器的ip (我们的例子是 vlesss 的 127.0.0.1,请你改成实际服务器ip) 的路由指向原来的 路由器地址
|
||||
|
||||
# sudo route delete -host default
|
||||
# sudo route add default -interface utun3
|
||||
@@ -22,12 +23,25 @@
|
||||
# sudo route delete -host default
|
||||
# sudo route add default 192.168.1.1
|
||||
|
||||
# 这个方案只适用于 不直连,全经过代理的情况。如果要分流直连,则需要更高级的 路由方案才行,否则会导致本地回环
|
||||
|
||||
# 你还可以参考 https://github.com/yangchuansheng/love-gfw/blob/master/docs/gotun2socks-macos.md
|
||||
|
||||
|
||||
[[listen]]
|
||||
protocol = "tun"
|
||||
|
||||
# tun Server使用 host 配置作为 tun device name (macos不设此项)
|
||||
# 使用 ip 配置作为 gateway 的ip , 若不给出,默认为 10.1.0.20
|
||||
# 使用 extra.tun_selfip 作为 tun向外拨号的ip, 若不给出, 默认为 10.1.0.10
|
||||
# 使用 extra.tun_mask 作为 子网掩码, 若不给出, 默认为 255.255.255.0
|
||||
|
||||
|
||||
# 如果 extra.tun_auto_route 给出,vs_gui会试图自动配置路由表.
|
||||
# 此时必须额外给出需要 直连的ip列表, 比如你的 代理服务器的ip地址; 如果不给出, 则不会自动配置路由表
|
||||
|
||||
extra.tun_auto_route = true
|
||||
extra.tun_auto_route_direct_list = [ "127.0.0.1" ]
|
||||
|
||||
[[dial]]
|
||||
protocol = "vlesss"
|
||||
|
||||
2
go.mod
2
go.mod
@@ -18,6 +18,7 @@ require (
|
||||
github.com/pires/go-proxyproto v0.6.2
|
||||
github.com/pkg/profile v1.6.0
|
||||
github.com/refraction-networking/utls v1.1.5
|
||||
github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b
|
||||
github.com/xtaci/smux v1.5.16
|
||||
github.com/yl2chen/cidranger v1.0.2
|
||||
go.uber.org/atomic v1.9.0
|
||||
@@ -38,7 +39,6 @@ require (
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -2,7 +2,6 @@ package tun
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
@@ -110,16 +109,23 @@ func (h *handler) ReceiveTo(conn core.UDPConn, data []byte, addr *net.UDPAddr) e
|
||||
return nil
|
||||
}
|
||||
|
||||
// selfaddr是tun向外拨号时使用的ip; realAddr 是 tun接收数据时对外暴露的ip。
|
||||
// selfaddr是tun向外拨号时使用的ip; realAddr 是 tun接收数据时对外暴露的ip。也被称为gateway
|
||||
// realAddr 是在路由表中需要配置的那个ip。
|
||||
// mask是子网掩码,不是很重要.
|
||||
// macos上的使用举例:"", "10.1.0.10", "10.1.0.20", "255.255.255.0"
|
||||
func CreateTun(name, selfaddr, realAddr, mask string) (tunDev io.ReadWriteCloser, err error) {
|
||||
func CreateTun(name, selfaddr, realAddr, mask string) (realname string, tunDev io.ReadWriteCloser, err error) {
|
||||
|
||||
//macos 上无法指定tun名称
|
||||
tunDev, err = tun.OpenTunDevice(name, selfaddr, realAddr, mask, nil, false)
|
||||
if err == nil {
|
||||
realname = tunDev.(*water.Interface).Name()
|
||||
if ce := utils.CanLogInfo("created new tun device"); ce != nil {
|
||||
ce.Write(zap.String("name", tunDev.(*water.Interface).Name()))
|
||||
ce.Write(
|
||||
zap.String("name", realname),
|
||||
zap.String("gateway", realAddr),
|
||||
zap.String("selfip", selfaddr),
|
||||
zap.String("mask", mask),
|
||||
)
|
||||
}
|
||||
}
|
||||
/*
|
||||
@@ -143,7 +149,10 @@ func ListenTun(tunDev io.ReadWriteCloser) (tcpChan <-chan netLayer.TCPRequestInf
|
||||
go func() {
|
||||
_, err := io.CopyBuffer(lwip, tunDev, make([]byte, utils.MTU))
|
||||
if err != nil {
|
||||
log.Fatalf("tun copying from tunDev to lwip failed: %v", err)
|
||||
if ce := utils.CanLogWarn("tun copying from tunDev to lwip failed"); ce != nil {
|
||||
ce.Write(zap.Error(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
}()
|
||||
tcpChan = nh.tcpChan
|
||||
|
||||
113
proxy/tun/route_darwin.go
Normal file
113
proxy/tun/route_darwin.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/e1732a364fed/v2ray_simple/utils"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var rememberedRouterIP string
|
||||
|
||||
func init() {
|
||||
autoRouteFunc = func(tunDevName, tunGateway, tunIP string, directList []string) {
|
||||
var ok = false
|
||||
params := "-nr -f inet"
|
||||
out, err := exec.Command("netstat", strings.Split(params, " ")...).Output()
|
||||
if err != nil {
|
||||
if ce := utils.CanLogErr("auto route failed"); ce != nil {
|
||||
ce.Write(zap.Error(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
//log.Println(string(out))
|
||||
lines := strings.Split(string(out), "\n")
|
||||
for i, l := range lines {
|
||||
if strings.HasPrefix(l, "Destination") {
|
||||
if i < len(lines)-1 && strings.HasPrefix(lines[i+1], "default") {
|
||||
str := utils.StandardizeSpaces(lines[i+1])
|
||||
lines = strings.Split(str, " ")
|
||||
//log.Println(lines, len(lines))
|
||||
|
||||
if len(lines) > 1 {
|
||||
routerIP := lines[1]
|
||||
if ce := utils.CanLogInfo("auto route: Your router's ip should be"); ce != nil {
|
||||
ce.Write(zap.String("ip", routerIP))
|
||||
}
|
||||
rememberedRouterIP = routerIP
|
||||
|
||||
params1 := "delete -host default"
|
||||
out1, err := exec.Command("route", strings.Split(params1, " ")...).Output()
|
||||
|
||||
//这里err只能捕获没有权限运行等错误; 如果路由表修改失败,是不会返回err的
|
||||
|
||||
errStep:
|
||||
if ce := utils.CanLogInfo("auto route delete default"); ce != nil {
|
||||
ce.Write(zap.String("output", string(out1)))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if ce := utils.CanLogErr("auto route failed"); ce != nil {
|
||||
ce.Write(zap.Error(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
params1 = "add default -interface " + tunDevName
|
||||
out1, err = exec.Command("route", strings.Split(params1, " ")...).Output()
|
||||
if err != nil {
|
||||
goto errStep
|
||||
}
|
||||
if ce := utils.CanLogInfo("auto route add tun"); ce != nil {
|
||||
ce.Write(zap.String("output", string(out1)))
|
||||
}
|
||||
|
||||
for _, v := range directList {
|
||||
params1 = "add -host " + v + " " + rememberedRouterIP
|
||||
out1, err = exec.Command("route", strings.Split(params1, " ")...).Output()
|
||||
if err != nil {
|
||||
goto errStep
|
||||
}
|
||||
if ce := utils.CanLogInfo("auto route add direct"); ce != nil {
|
||||
ce.Write(zap.String("output", string(out1)))
|
||||
}
|
||||
}
|
||||
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
utils.Warn("auto route failed")
|
||||
}
|
||||
}
|
||||
|
||||
autoRouteDownFunc = func(tunDevName, tunGateway, tunIP string, directList []string) {
|
||||
//恢复路由表
|
||||
params := "delete -host default"
|
||||
out1, _ := exec.Command("route", strings.Split(params, " ")...).Output()
|
||||
|
||||
if ce := utils.CanLogInfo("auto route delete tun route"); ce != nil {
|
||||
ce.Write(zap.String("output", string(out1)))
|
||||
}
|
||||
|
||||
params = "add default " + rememberedRouterIP
|
||||
out1, _ = exec.Command("route", strings.Split(params, " ")...).Output()
|
||||
|
||||
if ce := utils.CanLogInfo("auto route recover default route"); ce != nil {
|
||||
ce.Write(zap.String("output", string(out1)))
|
||||
}
|
||||
|
||||
for _, v := range directList {
|
||||
params = "delete -host " + v
|
||||
out1, _ = exec.Command("route", strings.Split(params, " ")...).Output()
|
||||
|
||||
if ce := utils.CanLogInfo("auto route delete direct"); ce != nil {
|
||||
ce.Write(zap.String("output", string(out1)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
// Package tun implements proxy.Server for tun device.
|
||||
/*
|
||||
tun Server使用 host 配置作为 tun device name
|
||||
使用 ip 配置作为 gateway 的ip
|
||||
使用 extra.tun_selfip 作为 tun向外拨号的ip
|
||||
使用 extra.tun_mask 作为 子网掩码
|
||||
*/
|
||||
package tun
|
||||
|
||||
import (
|
||||
@@ -20,6 +27,11 @@ func init() {
|
||||
|
||||
type ServerCreator struct{ proxy.CreatorCommonStruct }
|
||||
|
||||
var (
|
||||
autoRouteFunc func(tunDevName, tunGateway, tunIP string, directlist []string)
|
||||
autoRouteDownFunc func(tunDevName, tunGateway, tunIP string, directlist []string)
|
||||
)
|
||||
|
||||
func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format int) (*proxy.ListenConf, error) {
|
||||
if lc == nil {
|
||||
return nil, utils.ErrNilParameter
|
||||
@@ -31,10 +43,69 @@ func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format
|
||||
func (ServerCreator) NewServer(lc *proxy.ListenConf) (proxy.Server, error) {
|
||||
|
||||
s := &Server{}
|
||||
s.devName = lc.Host
|
||||
s.realIP = lc.IP
|
||||
if len(lc.Extra) > 0 {
|
||||
if thing := lc.Extra["tun_selfip"]; thing != nil {
|
||||
if str, ok := thing.(string); ok {
|
||||
s.selfip = str
|
||||
}
|
||||
}
|
||||
if thing := lc.Extra["tun_mask"]; thing != nil {
|
||||
if str, ok := thing.(string); ok {
|
||||
s.mask = str
|
||||
}
|
||||
}
|
||||
|
||||
if thing := lc.Extra["tun_auto_route"]; thing != nil {
|
||||
if auto, autoOk := utils.AnyToBool(thing); autoOk && auto {
|
||||
|
||||
if thing := lc.Extra["tun_auto_route_direct_list"]; thing != nil {
|
||||
|
||||
if list, ok := thing.([]any); ok {
|
||||
for _, v := range list {
|
||||
if str, ok := v.(string); ok && str != "" {
|
||||
s.autoRouteDirectList = append(s.autoRouteDirectList, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.autoRouteDirectList) == 0 {
|
||||
utils.Warn("tun auto route set, but no direct list given. auto route will not run.")
|
||||
} else {
|
||||
s.autoRoute = true
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (ServerCreator) AfterCommonConfServer(ps proxy.Server) (err error) {
|
||||
s := ps.(*Server)
|
||||
|
||||
const defaultSelfIP = "10.1.0.10"
|
||||
const defaultRealIP = "10.1.0.20"
|
||||
const defaultMask = "255.255.255.0"
|
||||
|
||||
//上面两个默认ip取自water项目给出的示例
|
||||
|
||||
if s.realIP == "" {
|
||||
s.realIP = defaultRealIP
|
||||
}
|
||||
if s.selfip == "" {
|
||||
s.selfip = defaultSelfIP
|
||||
}
|
||||
if s.mask == "" {
|
||||
s.mask = defaultMask
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
proxy.Base
|
||||
|
||||
@@ -43,6 +114,10 @@ type Server struct {
|
||||
infoChan chan<- netLayer.TCPRequestInfo
|
||||
udpRequestChan chan<- netLayer.UDPRequestInfo
|
||||
lwipCloser io.Closer
|
||||
|
||||
devName, realIP, selfip, mask string
|
||||
autoRoute bool
|
||||
autoRouteDirectList []string
|
||||
}
|
||||
|
||||
func (*Server) Name() string { return name }
|
||||
@@ -65,18 +140,29 @@ func (s *Server) Stop() {
|
||||
s.lwipCloser.Close()
|
||||
close(s.infoChan)
|
||||
close(s.udpRequestChan)
|
||||
if s.autoRoute && autoRouteDownFunc != nil {
|
||||
utils.Info("tun running auto table down")
|
||||
|
||||
autoRouteDownFunc(s.devName, s.realIP, s.selfip, s.autoRouteDirectList)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) StartListen(tcpRequestChan chan<- netLayer.TCPRequestInfo, udpRequestChan chan<- netLayer.UDPRequestInfo) io.Closer {
|
||||
tunDev, err := tun.CreateTun("", "10.1.0.10", "10.1.0.20", "255.255.255.0")
|
||||
//log.Println(s.devName, s.selfip, s.realIP, s.mask)
|
||||
realname, tunDev, err := tun.CreateTun(s.devName, s.selfip, s.realIP, s.mask)
|
||||
if err != nil {
|
||||
if ce := utils.CanLogErr("tun listen failed"); ce != nil {
|
||||
ce.Write(zap.Error(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
s.devName = realname
|
||||
if s.autoRoute && autoRouteFunc != nil {
|
||||
utils.Info("tun running auto table")
|
||||
autoRouteFunc(realname, s.realIP, s.selfip, s.autoRouteDirectList)
|
||||
}
|
||||
s.infoChan = tcpRequestChan
|
||||
s.udpRequestChan = udpRequestChan
|
||||
|
||||
|
||||
@@ -106,3 +106,8 @@ func Openbrowser(url string) error {
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/37290693/how-to-remove-redundant-spaces-whitespace-from-a-string-in-golang
|
||||
func StandardizeSpaces(s string) string {
|
||||
return strings.Join(strings.Fields(s), " ")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user