-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: support tun mode #44
Conversation
好功能,我今天之前看看 😸 |
core/protocol.go
Outdated
continue | ||
} | ||
|
||
if header.Protocol != 6 && header.Protocol != 17 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
服务端似乎不会响应 TCP/UDP 之外的东西,比如 ICMP,所以这里做了一下过滤。不过考虑到过滤对性能的影响,不知道是不是去掉更好
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这点过滤没啥性能影响,全是cache的操作,不过最好能把6和17用常量定义
core/tun_stack_windows.go
Outdated
@@ -68,4 +71,10 @@ func SetupTunStack(ip []byte, endpoint *EasyConnectTunEndpoint) { | |||
if err != nil { | |||
log.Printf("SetIPAddresses failed: %v", err) | |||
} | |||
|
|||
cmd := exec.Command("route", "add", "0.0.0.0", "mask", "0.0.0.0", ipStr, "metric", "9999") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
如果不加这条路由,Windows 不允许在这个接口上通过目的地为公网的流量
目前运行时路由表是这个样子 |
可以考虑使用ip命令取代ifconfig,所有Linux发行版基本上都提供了 |
确实,不过 macOS 上貌似还没有,所以 darwin 那个还是用 ifconfig https://superuser.com/questions/687310/ip-command-in-mac-os-x-terminal/ |
想到一个可能需要的功能 |
Windows 下 DNS 设置现在我正在做,目前在写 DNS Server |
如果在 TUN 模式下,把 zju-connect 内部的 DNS 逻辑暴露成一个 DNS Server,并设置 TUN 接口使用这个 DNS,下面这个本地解析的逻辑会不会导致循环:
感觉如果暴露出一个 DNS Server 的话,干脆取消解析失败后用 local DNS 的逻辑,这样就可以得到一个使用 ZJU DNS + DNS 规则 + 自定义 DNS 规则的 DNS Server。如果以上方式都失败就直接放弃 |
我也想过这个问题,感觉是会的,但是手头账号用于ical了没法试 😢 |
要么就取消解析失败后用 local DNS 的逻辑,这样就可以得到一个使用 ZJU DNS + 服务端下发的 DNS 规则 + 自定义 DNS 规则的 DNS Server。如果以上方式都失败就直接放弃 或者更复杂一点,给一个设置项手动指定后备 DNS |
如果不设置后备dns的话,tun mode接管所有dns会不会有问题,可以考虑像clash tun mode那样默认几个国内的dns |
flag.StringVar(&core.ZjuDnsServer, "zju-dns-server", "10.10.0.21", "ZJU DNS server address")
flag.StringVar(&core.SecondaryDnsServer, "secondary-dns-server", "114.114.114.114", "Secondary DNS server address. Leave empty to use system default DNS server")
flag.StringVar(&core.DnsServerBind, "dns-server-bind", "", "The address DNS server listens on (e.g. 127.0.0.1:53)")
flag.StringVar(&core.TunDnsServer, "tun-dns-server", "", "DNS Server address for TUN interface (e.g. 127.0.0.1). You should not specify the port") 打算用 secondary-dns-server 决定 ZJU DNS 解析失败后用什么。给一个默认值,如果留空就直接用默认的解析 Windows 虽然有多个 DNS,但超时时间会导致体验很差 |
那tun mode下ResolveWithLocal的逻辑,如果使用system default的dns会导致循环吗( |
如果设置成空就会,但默认是用 114.114.114.114 |
func (resolve *DnsResolve) Resolve(ctx context.Context, host string) (context.Context, net.IP, error) {
if config.IsDnsRuleAvailable() {
if ip, hasDnsRule := config.GetSingleDnsRule(host); hasDnsRule {
ctx = context.WithValue(ctx, "USE_PROXY", true)
log.Printf("%s -> %s", host, ip)
return ctx, net.ParseIP(ip), nil
}
}
var useProxy = false
if config.IsZjuForceProxyRuleAvailable() {
if isInZjuForceProxyRule := config.IsInZjuForceProxyRule(host); isInZjuForceProxyRule {
useProxy = true
}
}
if !useProxy && config.IsDomainRuleAvailable() {
if _, found := config.GetSingleDomainRule(host); found {
useProxy = true
}
}
ctx = context.WithValue(ctx, "USE_PROXY", useProxy)
if UseZjuDns {
if cachedIP, found := GetDnsCache(host); found {
log.Printf("%s -> %s", host, cachedIP.String())
return ctx, cachedIP, nil
} else { 这个地方现在的逻辑似乎是服务端下发的规则 > 自定义规则,因为 |
在macos上找到了这样的解决方法,并测试成功: if TunMode {
if network == "tcp" {
log.Printf("%s -> PROXY", addr)
goDialer := &net.Dialer{
Control: func(network, address string, c syscall.RawConn) error {
return c.Control(func(fd uintptr) {
iface, err := net.InterfaceByName("utun10")
if err != nil {
fmt.Println(err)
}
if err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVIF, iface.Index); err != nil {
fmt.Println(err)
}
})
},
}
return goDialer.Dial("tcp", addr) insomniacslk/dhcp#378 |
测试了一下macos上clash tun mode的情况,由于没有linux那样的网络子系统,clash是修改dns到8.8.8.8,然后路由写死,全部流量进clash,包括8.8.8.8:53,然后再用之前系统的dns进行解析。 感觉最后可以提供一个这样形式的dns server:
还有一个想法:初步实现中,使用配置文件中指定的SecondaryDns感觉没啥问题,但是后续改成可以自动使用用户机器原本的dns |
这个是为了在不配置系统代理的情况下正确访问 xxx.zju.edu.cn 吗。那我觉得后期还需要完善一下这个内置的 DNS,毕竟只实现了 A 和 AAAA 的解析
macos 下 dns 如果可以走命令行设置的话,能不能像现在 Windows 的实现那样监听 0.0.0.0:53 然后设置为 127.0.0.1
我也有这个想法。有什么方法获取用户之前使用的 DNS 嘛 |
是的,感觉这个需求在tun mode下是非常重要的,确实需要完善一下除了A和AAAA,不过也初步够用了
这个肯定是可以的,就是怕用户自己其他程序可能用了53端口,劫持dns毕竟还是更加的
感觉可以后面研究一下,现在我只能想到mac上可以用 |
感觉 Clash 的 TUN 模式是没有做自动获取之前的 DNS 这个功能的,开启 TUN+DNS 后似乎必须要手动设置一个 nameserver |
确实,clash for windows是默认了三个nameserver |
https://github.com/Mythologyli/zju-connect/tree/refactor 我在这个分支上做了一些重构。对于在路由器上跑 zju-connect 的用户,感觉最后的体积还是蛮重要的。现在这样重构比较方便未来在做一个只用 TUN 的版本,去掉 gVisor 体积应该能减少不小 我的 golang 纯纯野路子水平,有空的话希望帮忙看看整个结构设计的有没有问题(哭 |
过了一遍感觉水平很高 👍 现在感觉除了dns相关的,tun mode已经比较完善了(可以merge的感觉)。但linux和macos上的tun模式下dns我还是有点挠头
所以一个可以接受的方案就是直接替换dns的配置文件/etc/resolve.conf,这就需要先实现优雅停机,程序结束前将/etc/resolve.conf.bak 复原。 anyway,后面可以新开一个pr做优雅停机和其他的改进,感觉这个tun mode已经基本可用了 |
也许可以看看 clash meta 的实现? |
看了一下clash meta的思路,他们是用这一套https://github.com/MetaCubeX/sing-tun ,同时基于tun配套实现了一个system的stack 我们这最简单的方法就是只管udp,把53端口的请求拿出来,单独resolve,然后把把返回的包的源ip和源port改成之前的目的ip和目的port。 我们感觉可以用这个tun替换现在的water.tun,这个tun对route和ip rule的支持也很完善 |
感觉不错! |
现在分支dns-hijack已经可以正确拦截基于udp的dns请求 nslookup baidu.com 223.5.5.5
nslookup cspo.zju.edu.cn 223.5.5.5
但仍然还有一些问题:
考虑到后面如果换sing-tun或者其他tun实现,这里我就没用command写死
|
分支dns-hijack已经将linux和macos的tun切换到sing-tun,支持了自动配置ip route 和 ip rule linux下可以拦截非localhost下的dns请求,macos下只能在实现了优雅停机之后手动设置用户dns解析为一个fake ip,然后路由route这个fake ip到tun设备 macos下还存在问题: 使用proxy代理访问需要代理的、非10.0.0.0/8的网站现在发现还是报错no route to host,之前测试的有问题。我找了半天原因,最后看到这一篇 macos下使用的是强主机模型,
macos只能有一个默认路由,看这句话的样子就是说如果路由表中没有 ip为非10.0.0.0/8的、走tun设备的路由条目,则就算实用bindtodevice看起来也没法路由出去 使用proxy下测试访问cnki.net ,只有10.0.0.0/8的路由情况下报错 dial tcp4 10.190.65.184:0->115.31.65.10:80: connect: no route to host,添加 sudo route -n add 115.31.65.10 -interface utun10后,可以正常访问。 所以后面考虑开启tun模式下,macos上所有访问非10.0.0.0/8的可能得一律direct |
或许后面可以考虑将windows的tun也切到sing-tun下,我fork了一份sing-tun,修改了部分设置优先级、路由策略的方法,如果windows需要修改里面的路由设置啥的我可以帮忙 |
我记得 Windows 也是强主机模型,当时一开始测试的时候也会 no route to host,最后是通过加一条到 0.0.0.0/0 的低优先级路由解决的 |
确实,我也以为这样可以,但是在sonoma14.0上面发现没法设置多个默认路由,netstat -rn 看不到跃点数,很烦 |
要不要先不管 dns-hijack 了,先把 refactor 合并到 main,dns-hijack 单独开一个 PR |
合理,感觉改下readme就能合 |
此 PR 希望为 zju-connect 带来 TUN 模式,具体的好处有:
目前的初步想法为:
10.0.0.0/8
。如果路由0.0.0.0
可能分流比较复杂存在的问题:
github.com/songgao/water
对 Android 的支持似乎不好,所以目前暂未实现 Android