add support for linux and udp

This commit is contained in:
kony
2025-04-01 13:45:40 +08:00
parent ab67769c18
commit 32135e6423
13 changed files with 317 additions and 132 deletions

View File

@@ -11,8 +11,7 @@ GOBUILD=GO111MODULE=on \
-X "gogo.BuildTime=$(BuildTime)" \ -X "gogo.BuildTime=$(BuildTime)" \
-w -s -buildid=' -w -s -buildid='
PLATFORM_LIST = \ LINUX_PLATFORM_LIST = \
windows-amd64-ui \
linux-386-cmd \ linux-386-cmd \
linux-amd64-cmd \ linux-amd64-cmd \
linux-arm-cmd \ linux-arm-cmd \
@@ -23,11 +22,14 @@ PLATFORM_LIST = \
linux-mipsle-cmd \ linux-mipsle-cmd \
linux-mips64-cmd \ linux-mips64-cmd \
linux-riscv64-cmd \ linux-riscv64-cmd \
linux-mips64le-cmd \ linux-mips64le-cmd
WINDOWS_PLATFORM_LIST = \
windows-amd64-ui \
windows-amd64-cmd \ windows-amd64-cmd \
windows-arm64-cmd windows-arm64-cmd
all: $(PLATFORM_LIST) strip all: $(WINDOWS_PLATFORM_LIST) rm_uac $(LINUX_PLATFORM_LIST) strip
linux-386-cmd: linux-386-cmd:
GOARCH=386 GOOS=linux $(GOBUILD) -tags "cmd" -o $(BINDIR)/$(NAME)-$@ GOARCH=386 GOOS=linux $(GOBUILD) -tags "cmd" -o $(BINDIR)/$(NAME)-$@
@@ -79,9 +81,12 @@ windows-amd64-ui:
# go build -ldflags -H=windowsgui # go build -ldflags -H=windowsgui
mkdir bin; fyne package; mv *.exe bin/ mkdir bin; fyne package; mv *.exe bin/
uac: create_uac:
rsrc -manifest nac.manifest -o nac.syso rsrc -manifest nac.manifest -o nac.syso
rm_uac:
rm -rf nac.syso
strip: strip:
upx $(BINDIR)/* upx $(BINDIR)/*

View File

@@ -1,10 +1,10 @@
<img src="https://gitee.com/konyshe/goodlink/raw/master/assert/letter-g-2.png" width="400" height="100"> <img src="https://gitee.com/konyshe/goodlink/raw/master/assert/letter-g-2.png" width="400" height="100">
由于经常异地办公, 对于市面上的远程桌面工具, 无论速度、画面等, 都不如 windows 自带的远程桌面, 但异地如使用 windows远程桌面呢 由于经常外出办公, 对于市面上的远程桌面工具, 无论画面、适配等, 都不如 windows 自带的远程桌面, 但外出如何使用 windows远程桌面呢
是否可以无需远程桌面, 直接浏览器访问公司的内网 WEB, GIT, SSH 等, 和在公司一模一样 是否可以无需远程桌面, 直接访问公司的内网 WEB, GIT, SSH 等?
**注: 该项目仅用于学习研究, 目前没有任何商业合作,更没有任何恶意行为。如果未来有广告之类盈利的行为,会郑重告知大家。另外声明:严禁用于违法行为!!!** **注: 该项目仅用于学习研究, 目前商业合作,更恶意行为。如果未来有广告之类盈利的行为,会郑重告知大家。另外声明:严禁用于违法行为!!!**
[切换回1.6版文档](https://gitee.com/konyshe/goodlink/blob/v1.6/README.md) [切换回1.6版文档](https://gitee.com/konyshe/goodlink/blob/v1.6/README.md)
@@ -18,7 +18,7 @@
# 重点 # 重点
1. 本程序即支持命令行方式, 也支持 docker 方式, windows 平台还增加了UI版本适合新手 1. 本程序即支持命令行方式, 也支持 docker 方式, windows 版本还新增了UI版本, 适合新手。以下举例仅作参考, 可随意搭配
2. 两端主机运行同一个程序 / Docker, 一端使用--remote 选项(以下称 remote 端), 另一端使用--local 选项(以下称 local 端) 2. 两端主机运行同一个程序 / Docker, 一端使用--remote 选项(以下称 remote 端), 另一端使用--local 选项(以下称 local 端)
@@ -32,6 +32,10 @@
7. 以下举例说明中的密钥(--key), 请不要使用, 否则会连上别人的 remote 端, 或者被别人的 local 端连上。自己随机一个 16-24 字节长度的密钥 7. 以下举例说明中的密钥(--key), 请不要使用, 否则会连上别人的 remote 端, 或者被别人的 local 端连上。自己随机一个 16-24 字节长度的密钥
8. 对于有安全疑问,或者想进阶使用的同学,可以看: [使用GoodLink 是否足够安全?](https://gitee.com/konyshe/goodlink/issues/IBFKC2)
9. 该项目刚刚起步, 可能不太稳定, 欢迎到 Issues 上提出问题和建议, 帮忙测试的同学将保证永久免费使用
<table> <table>
<th>Remote端</th><th>Local端</th><th>P2P成功</th> <th>Remote端</th><th>Local端</th><th>P2P成功</th>
<tr><td>NAT1-3</td><td>NAT1-4</td><td>YES</td></tr> <tr><td>NAT1-3</td><td>NAT1-4</td><td>YES</td></tr>
@@ -47,13 +51,11 @@
Local端会创建一个虚拟网卡, 因此需要管理员权限运行。连接成功后,界面会显示: 对端IP Local端会创建一个虚拟网卡, 因此需要管理员权限运行。连接成功后,界面会显示: 对端IP
不限端口访问对端IP的任意端口相当于访问Remote端本机的任意端口 不限端口访问对端IP的任意端口相当于访问Remote端本机的任意端口
对端IP目前固定为: 192.17.19.1 , 具体以界面或者日志显示为准 对端IP目前固定为: 192.17.19.1 , 具体以界面或者日志显示为准
举例: 在家里电脑(或出差电脑), 打开 windows 远程桌面, 填写对端IP, 即可访问公司电脑的远程桌面 举例: 在Local端打开 windows 远程桌面, 填写对端IP, 即可访问Remote端的远程桌面
注: 目前仅支持TCP协议, 下一版本将支持UDP协议
### 代理模式 ### 代理模式
@@ -61,9 +63,7 @@
代理地址端口: socket5://对端IP:1080 代理地址端口: socket5://对端IP:1080
local端需要在系统或者软件中配置Socket5代理, 访问任意主机端口, 相当于Remote端自己在访问 举例: 在Local端配置代理: socks5://对端IP:1080, 即可通过Remote端访问所有的网络资源
举例: 在公司电脑上配置代理: socks5://对端IP:1080, 访问家里包括 NAS 在内的所有主机端口
注: 目前仅支持TCP代理浏览器可安装插件 SwitchyOmega。其他 GIT, SVN, SSH 等, 都支持socks5代理 注: 目前仅支持TCP代理浏览器可安装插件 SwitchyOmega。其他 GIT, SVN, SSH 等, 都支持socks5代理
@@ -93,8 +93,6 @@
#### linux, Docker #### linux, Docker
2.0版还未提交Docker
``` ```
docker rm goodlink -f; docker run -d --name=goodlink --net=host --restart=always registry.cn-shanghai.aliyuncs.com/kony/goodlink --key=nas_202412140928 --remote docker rm goodlink -f; docker run -d --name=goodlink --net=host --restart=always registry.cn-shanghai.aliyuncs.com/kony/goodlink --key=nas_202412140928 --remote
``` ```
@@ -116,7 +114,7 @@ docker rm goodlink -f; docker run -d --name=goodlink --net=host --restart=always
#### linux, Docker #### linux, Docker
``` ```
由于Local端需要创建虚拟网卡Docker中并不支持因此Local端不支持Docker部署 由于Local端需要创建虚拟网卡Docker中并不支持
``` ```
#### linux, 命令行 #### linux, 命令行

17
netstack/device.go Normal file
View File

@@ -0,0 +1,17 @@
package netstack
import (
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
// Device is the interface that implemented by network layer devices (e.g. tun),
// and easy to use as stack.LinkEndpoint.
type Device interface {
stack.LinkEndpoint
// Name returns the current name of the device.
Name() string
// Type returns the driver type of the device.
Type() string
}

View File

@@ -1,5 +1,5 @@
// Package iobased provides the implementation of io.ReadWriter //go:build windows
// based data-link layer endpoints.
package netstack package netstack
import ( import (

View File

@@ -1,5 +1,3 @@
//go:build windows
package netstack package netstack
import ( import (

View File

@@ -1,5 +1,3 @@
//go:build windows
package netstack package netstack
import ( import (

80
netstack/setup.go Normal file
View File

@@ -0,0 +1,80 @@
package netstack
import (
"fmt"
"github.com/quic-go/quic-go"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
)
func setNetStack(s *stack.Stack, nicID tcpip.NICID) error {
s.SetRouteTable([]tcpip.Route{
{
Destination: header.IPv4EmptySubnet,
NIC: nicID,
},
})
if err := netstack_stack.SetPromiscuousMode(nicID, true); err != nil {
return fmt.Errorf("promisc: %s", err)
}
if err := netstack_stack.SetSpoofing(nicID, true); err != nil {
return fmt.Errorf("spoofing: %s", err)
}
return nil
}
var (
init_stack_suss = false
netstack_stack *stack.Stack
)
func Start() error {
if init_stack_suss {
return nil
}
netstack_stack = stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol},
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol},
})
wintunEP, err := Open(GetName(), 1490) //因为要加自定义头防止超出1500
if err != nil {
return fmt.Errorf("请管理员权限运行")
}
SetTunIP(&wintunEP, GetRemoteIP(), 32)
// 将TUN设备注册到协议栈中使用NIC ID 1
nicID := tcpip.NICID(1)
if err := netstack_stack.CreateNIC(nicID, wintunEP); err != nil {
return fmt.Errorf("设备注册: %v", err)
}
setNetStack(netstack_stack, nicID)
init_stack_suss = true
return nil
}
func SetForWarder(stun_quic_conn quic.Connection) {
netstack_stack.SetTransportProtocolHandler(tcp.ProtocolNumber, NewTcpForwarder(netstack_stack, stun_quic_conn).HandlePacket)
netstack_stack.SetTransportProtocolHandler(udp.ProtocolNumber, NewUdpForwarder(netstack_stack, stun_quic_conn).HandlePacket)
}
func GetRemoteIP() string {
return "192.17.19.1"
}
func GetName() string {
return "GoodLink"
}

View File

@@ -3,24 +3,100 @@
package netstack package netstack
import ( import (
"github.com/quic-go/quic-go" "fmt"
"log"
"net"
"os/exec"
"unsafe"
"golang.org/x/sys/unix"
) )
// setupNetstack 初始化并配置网络栈 func setUnixIP(name string, ip net.IP, mask net.IPMask) error {
// 该函数负责创建协议栈、设置网络接口、配置IP地址和路由表 log.Printf("setUnixIP: %s %s %s", name, ip, mask)
// 返回:
// - *stack.Stack: 配置好的网络栈实例 // 新增接口存在性检查
// - error: 初始化过程中的错误信息 if err := checkInterfaceExists(name); err != nil {
func Start() error { return err
}
// 修改标志位设置流程(先关闭再开启)
ifreq, _ := unix.NewIfreq(name)
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(unix.AF_INET), unix.SIOCGIFFLAGS, uintptr(unsafe.Pointer(ifreq)))
if errno != 0 {
return fmt.Errorf("SIOCGIFFLAGS(pre-check) failed: %s", unix.ErrnoName(errno))
}
originalFlags := ifreq.Uint16()
// 临时关闭接口(关键步骤)
ifreq.SetUint16(originalFlags &^ unix.IFF_UP)
_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(unix.AF_INET), unix.SIOCSIFFLAGS, uintptr(unsafe.Pointer(ifreq)))
if errno != 0 {
return fmt.Errorf("SIOCSIFFLAGS(down) failed: %s", unix.ErrnoName(errno))
}
// 设置IP地址结构体修复掩码处理
addr, _ := unix.NewIfreq(name)
addr.SetInet4Addr(ip.To4())
_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(unix.AF_INET), unix.SIOCSIFADDR, uintptr(unsafe.Pointer(addr)))
if errno != 0 {
return fmt.Errorf("SIOCSIFADDR failed: %s", unix.ErrnoName(errno)) // 显示具体错误名称
}
// 设置子网掩码(修复变量名错误)
maskAddr, _ := unix.NewIfreq(name)
maskAddr.SetInet4Addr(net.IP(mask).To4()) // 显式转换掩码类型
_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(unix.AF_INET), unix.SIOCSIFNETMASK, uintptr(unsafe.Pointer(maskAddr)))
if errno != 0 {
return fmt.Errorf("SIOCSIFNETMASK failed: %s", unix.ErrnoName(errno))
}
// 修改接口激活逻辑(关键修复)
ifreq, _ = unix.NewIfreq(name)
ifreq.SetUint16((originalFlags | unix.IFF_UP) &^ unix.IFF_NOARP) // 保留原始标志位并添加UP标志
_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(unix.AF_INET), unix.SIOCSIFFLAGS, uintptr(unsafe.Pointer(ifreq)))
if errno != 0 {
return fmt.Errorf("SIOCSIFFLAGS failed: %s", unix.ErrnoName(errno))
}
log.Printf("Configuration applied successfully")
return nil return nil
} }
func SetForWarder(stun_quic_conn quic.Connection) { // 新增辅助函数
func checkInterfaceExists(name string) error {
ifreq, _ := unix.NewIfreq(name)
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(unix.AF_INET), unix.SIOCGIFFLAGS, uintptr(unsafe.Pointer(ifreq)))
if errno != 0 {
return fmt.Errorf("interface %s not exists: %s", name, unix.ErrnoName(errno))
}
return nil
} }
func GetRemoteIP() string { // 修改调用端错误处理
return "Linux暂不支持Local端" func SetTunIP(wintunEP *Device, ip string, mask int) error {
} // ip addr add 192.17.0.1/32 dev GoodLink
// ip link set GoodLink up
// ip route add 192.17.19.1 dev GoodLink
func Stop() { // 设置网卡eth0的IP地址为192.168.1.10/24
cmd := exec.Command("ip", "addr", "add", "192.17.0.1/32", "dev", GetName())
if err := cmd.Run(); err != nil {
return err
}
cmd = exec.Command("ip", "link", "set", GetName(), "up")
if err := cmd.Run(); err != nil {
return err
}
cmd = exec.Command("ip", "route", "add", GetRemoteIP(), "dev", GetName())
if err := cmd.Run(); err != nil {
return err
}
/*if err := setUnixIP(GetName(), net.ParseIP(ip), net.CIDRMask(mask, 32)); err != nil {
return fmt.Errorf("setUnixIP failed: %w", err)
}*/
return nil
} }

View File

@@ -11,17 +11,9 @@ import (
"net/http" "net/http"
"net/netip" "net/netip"
"time" "time"
"github.com/quic-go/quic-go"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
) )
func SetWinTunIP(wintunEP *Device, ip string, mask int) error { func SetTunIP(wintunEP *Device, ip string, mask int) error {
var ipf netip.Prefix var ipf netip.Prefix
var err error var err error
@@ -51,30 +43,6 @@ func SetWinTunIP(wintunEP *Device, ip string, mask int) error {
return nil return nil
} }
func setNetStack(s *stack.Stack, nicID tcpip.NICID) error {
s.SetRouteTable([]tcpip.Route{
{
Destination: header.IPv4EmptySubnet,
NIC: nicID,
},
})
if err := netstack_stack.SetPromiscuousMode(nicID, true); err != nil {
return fmt.Errorf("promisc: %s", err)
}
if err := netstack_stack.SetSpoofing(nicID, true); err != nil {
return fmt.Errorf("spoofing: %s", err)
}
return nil
}
var (
init_stack_suss = false
netstack_stack *stack.Stack
)
func InitWintunDll() error { func InitWintunDll() error {
if gogo.Utils().FileExist("wintun.dll") { if gogo.Utils().FileExist("wintun.dll") {
return nil return nil
@@ -105,48 +73,3 @@ func InitWintunDll() error {
return nil return nil
} }
const (
tunIP = "192.17.19.1"
)
func Start() error {
InitWintunDll()
if init_stack_suss {
return nil
}
netstack_stack = stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol},
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol},
})
wintunEP, err := Open("GoodLink", 1490) //因为要加自定义头防止超出1500
if err != nil {
return fmt.Errorf("请管理员权限运行")
}
SetWinTunIP(&wintunEP, tunIP, 32)
// 将TUN设备注册到协议栈中使用NIC ID 1
nicID := tcpip.NICID(1)
if err := netstack_stack.CreateNIC(nicID, wintunEP); err != nil {
return fmt.Errorf("设备注册: %v", err)
}
setNetStack(netstack_stack, nicID)
init_stack_suss = true
return nil
}
func SetForWarder(stun_quic_conn quic.Connection) {
netstack_stack.SetTransportProtocolHandler(tcp.ProtocolNumber, NewTcpForwarder(netstack_stack, stun_quic_conn).HandlePacket)
netstack_stack.SetTransportProtocolHandler(udp.ProtocolNumber, NewUdpForwarder(netstack_stack, stun_quic_conn).HandlePacket)
}
func GetRemoteIP() string {
return tunIP
}

View File

@@ -1,5 +1,3 @@
//go:build windows
package netstack package netstack
import ( import (

View File

@@ -1,5 +1,3 @@
//go:build windows
package netstack package netstack
import ( import (

105
netstack/tun_linux.go Normal file
View File

@@ -0,0 +1,105 @@
//go:build linux
package netstack
import (
"fmt"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/rawfile"
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
"gvisor.dev/gvisor/pkg/tcpip/link/tun"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
type TUN struct {
stack.LinkEndpoint
fd int
mtu uint32
name string
}
func Open(name string, mtu uint32) (Device, error) {
t := &TUN{name: name, mtu: mtu}
if len(t.name) >= unix.IFNAMSIZ {
return nil, fmt.Errorf("interface name too long: %s", t.name)
}
fd, err := tun.Open(t.name)
if err != nil {
return nil, fmt.Errorf("create tun: %w", err)
}
t.fd = fd
if t.mtu > 0 {
if err := setMTU(t.name, t.mtu); err != nil {
return nil, fmt.Errorf("set mtu: %w", err)
}
}
_mtu, err := rawfile.GetMTU(t.name)
if err != nil {
return nil, fmt.Errorf("get mtu: %w", err)
}
t.mtu = _mtu
ep, err := fdbased.New(&fdbased.Options{
FDs: []int{fd},
MTU: t.mtu,
// TUN only, ignore ethernet header.
EthernetHeader: false,
// SYS_READV support only for TUN fd.
PacketDispatchMode: fdbased.Readv,
// TAP/TUN fd's are not sockets and using the WritePackets calls results
// in errors as it always defaults to using SendMMsg which is not supported
// for tap/tun device fds.
//
// This CL changes WritePackets to gracefully degrade to using writev instead
// of sendmmsg if the underlying fd is not a socket.
//
// Fixed: https://github.com/google/gvisor/commit/f33d034fecd7723a1e560ccc62aeeba328454fd0
MaxSyscallHeaderBytes: 0x00,
})
if err != nil {
return nil, fmt.Errorf("create endpoint: %w", err)
}
t.LinkEndpoint = ep
return t, nil
}
func (t *TUN) Name() string {
return t.name
}
func (t *TUN) Close() {
defer t.LinkEndpoint.Close()
_ = unix.Close(t.fd)
}
func (t *TUN) Type() string {
return "tun" // 返回固定的设备类型标识符
}
func setMTU(name string, n uint32) error {
// open datagram socket
fd, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
0,
)
if err != nil {
return err
}
defer unix.Close(fd)
ifr, err := unix.NewIfreq(name)
if err != nil {
return err
}
ifr.SetUint32(n)
return unix.IoctlIfreq(fd, unix.SIOCSIFMTU, ifr)
}

View File

@@ -10,21 +10,8 @@ import (
"sync" "sync"
"golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/tun"
"gvisor.dev/gvisor/pkg/tcpip/stack"
) )
// Device is the interface that implemented by network layer devices (e.g. tun),
// and easy to use as stack.LinkEndpoint.
type Device interface {
stack.LinkEndpoint
// Name returns the current name of the device.
Name() string
// Type returns the driver type of the device.
Type() string
}
// 常量定义 // 常量定义
const ( const (
offset = 0 // 数据包偏移量用于处理TUN_PI头0表示不使用TUN_PI offset = 0 // 数据包偏移量用于处理TUN_PI头0表示不使用TUN_PI
@@ -59,6 +46,8 @@ type TUN struct {
// - Device: 实现了Device接口的TUN设备 // - Device: 实现了Device接口的TUN设备
// - error: 创建过程中的错误信息 // - error: 创建过程中的错误信息
func Open(name string, mtu uint32) (_ Device, err error) { func Open(name string, mtu uint32) (_ Device, err error) {
InitWintunDll()
// 使用defer和recover处理可能的panic确保错误被正确捕获和包装 // 使用defer和recover处理可能的panic确保错误被正确捕获和包装
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {